Skip to content

Commit b7f4f4d

Browse files
buty4649claude
andauthored
query: クエリグラフ取得サブコマンドを追加 (#28)
GET /api/v1/query/graph/{query_code} を呼び出してクエリに設定された グラフ画像 (PNG/JPEG) をダウンロードする `xpoint query graph` サブ コマンドを追加。--format で PNG/JPEG を選択、--output で保存先 (ファイル/ディレクトリ/- で stdout) を指定できる。 refs #25 Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5f86260 commit b7f4f4d

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

cmd/query.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ var (
1818
queryExecRows int
1919
queryExecOffset int
2020
queryExecJQ string
21+
22+
queryGraphFormat string
23+
queryGraphOutput string
2124
)
2225

2326
var queryCmd = &cobra.Command{
@@ -44,10 +47,26 @@ only. --rows (default 500) and --offset control pagination for list queries.`,
4447
RunE: runQueryExec,
4548
}
4649

50+
var queryGraphCmd = &cobra.Command{
51+
Use: "graph <query_code>",
52+
Short: "Download the graph image of a query",
53+
Long: `Download the graph image configured on a query via
54+
GET /api/v1/query/graph/{query_code}.
55+
56+
The format defaults to PNG; pass --format jpeg for JPEG. Output destination:
57+
--output FILE save to FILE
58+
--output DIR/ save into DIR/ using the server-provided filename
59+
--output - write the image bytes to stdout
60+
(default) use the server-provided filename in the current directory`,
61+
Args: cobra.ExactArgs(1),
62+
RunE: runQueryGraph,
63+
}
64+
4765
func init() {
4866
rootCmd.AddCommand(queryCmd)
4967
queryCmd.AddCommand(queryListCmd)
5068
queryCmd.AddCommand(queryExecCmd)
69+
queryCmd.AddCommand(queryGraphCmd)
5170

5271
lf := queryListCmd.Flags()
5372
lf.StringVarP(&queryListOutput, "output", "o", "", "output format: table|json (default: table on TTY, json otherwise)")
@@ -58,6 +77,10 @@ func init() {
5877
ef.IntVar(&queryExecRows, "rows", 0, "max rows returned by list queries (0 = omit; server default 500; range 1-10000)")
5978
ef.IntVar(&queryExecOffset, "offset", 0, "offset for list queries (0 = omit)")
6079
ef.StringVar(&queryExecJQ, "jq", "", "apply a gojq filter to the JSON response")
80+
81+
gf := queryGraphCmd.Flags()
82+
gf.StringVar(&queryGraphFormat, "format", "", "image format: png | jpeg (default: png)")
83+
gf.StringVarP(&queryGraphOutput, "output", "o", "", "output path: FILE, DIR/, or - for stdout (default: server-provided filename in current directory)")
6184
}
6285

6386
func runQueryList(cmd *cobra.Command, args []string) error {
@@ -128,3 +151,43 @@ func runQueryExec(cmd *cobra.Command, args []string) error {
128151
}
129152
return writeJSON(os.Stdout, raw)
130153
}
154+
155+
func runQueryGraph(cmd *cobra.Command, args []string) error {
156+
queryCode := strings.TrimSpace(args[0])
157+
if queryCode == "" {
158+
return fmt.Errorf("query_code is required")
159+
}
160+
format := strings.ToLower(strings.TrimSpace(queryGraphFormat))
161+
switch format {
162+
case "", "png", "jpeg":
163+
default:
164+
return fmt.Errorf("unknown --format %q (must be png or jpeg)", queryGraphFormat)
165+
}
166+
167+
client, err := newClientFromFlags(cmd.Context())
168+
if err != nil {
169+
return err
170+
}
171+
filename, data, err := client.GetQueryGraph(cmd.Context(), queryCode, format)
172+
if err != nil {
173+
return err
174+
}
175+
176+
if queryGraphOutput == "-" {
177+
_, werr := os.Stdout.Write(data)
178+
return werr
179+
}
180+
if filename == "" {
181+
ext := "png"
182+
if format == "jpeg" {
183+
ext = "jpg"
184+
}
185+
filename = fmt.Sprintf("%s.%s", queryCode, ext)
186+
}
187+
dst := resolveDownloadPath(queryGraphOutput, filename, 0)
188+
if err := os.WriteFile(dst, data, 0o600); err != nil {
189+
return fmt.Errorf("write image: %w", err)
190+
}
191+
fmt.Fprintf(os.Stderr, "saved: %s (%d bytes)\n", dst, len(data))
192+
return nil
193+
}

internal/schema/query.graph.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"method": "GET",
3+
"path": "/api/v1/query/graph/{query_code}",
4+
"summary": "クエリグラフ表示",
5+
"description": "認証ユーザが閲覧できるクエリに設定されているグラフ画像を取得する。PNG か JPEG を指定可能 (デフォルト PNG)。",
6+
"parameters": [
7+
{
8+
"name": "query_code",
9+
"in": "path",
10+
"type": "string",
11+
"required": true,
12+
"description": "クエリコード"
13+
},
14+
{
15+
"name": "outFormat",
16+
"in": "query",
17+
"type": "string",
18+
"description": "フォーマット: png / jpeg (デフォルト: png)"
19+
}
20+
],
21+
"response": {
22+
"contentType": "image/png | image/jpeg",
23+
"description": "グラフ画像 (Content-Disposition でファイル名が返される)"
24+
}
25+
}

internal/schema/schema_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func TestAliases_Sorted(t *testing.T) {
3434
"form.list",
3535
"form.show",
3636
"query.exec",
37+
"query.graph",
3738
"query.list",
3839
}
3940
if len(got) != len(want) {

internal/xpoint/client.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,24 @@ func (c *Client) GetQuery(ctx context.Context, queryCode string, p GetQueryParam
530530
return out, nil
531531
}
532532

533+
// GetQueryGraph calls GET /api/v1/query/graph/{query_code} and returns the
534+
// image bytes and the server-provided filename (from Content-Disposition).
535+
// outFormat must be "png" or "jpeg" (empty string defaults to png on the
536+
// server).
537+
func (c *Client) GetQueryGraph(ctx context.Context, queryCode, outFormat string) (string, []byte, error) {
538+
path := fmt.Sprintf("/api/v1/query/graph/%s", url.PathEscape(queryCode))
539+
var q url.Values
540+
if outFormat != "" {
541+
q = url.Values{}
542+
q.Set("outFormat", outFormat)
543+
}
544+
accept := "image/png"
545+
if outFormat == "jpeg" {
546+
accept = "image/jpeg"
547+
}
548+
return c.downloadBytes(ctx, http.MethodGet, path, q, nil, "", accept)
549+
}
550+
533551
type AddCommentRequest struct {
534552
Content string `json:"content"`
535553
AttentionFlg int `json:"attentionflg"`

0 commit comments

Comments
 (0)