Skip to content

Commit c7b727a

Browse files
buty4649claude
andauthored
document: ブラウザで書類を開くopenサブコマンドを追加 (#21)
既定のブラウザで書類の表示ページを開く `xp document open <docid>` を追加。--no-browser (-n) で URL のみを出力する。 Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 02abf13 commit c7b727a

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,13 @@ xp document status 266248 --history # 全バージョンの承認履歴
105105
xp document status 266248 --jq '.document.status.name'
106106
```
107107

108+
### ドキュメントをブラウザで開く
109+
110+
```sh
111+
xp document open 266248 # 既定のブラウザで書類を開く
112+
xp document open 266248 --no-browser # URLだけ出力(ブラウザは起動しない)
113+
```
114+
108115
### ドキュメントのPDFダウンロード
109116

110117
```sh

cmd/document.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ var (
4141

4242
docStatusHistory bool
4343
docStatusJQ string
44+
45+
docOpenNoBrowser bool
4446
)
4547

4648
var documentCmd = &cobra.Command{
@@ -127,6 +129,17 @@ approval histories for all past versions.`,
127129
RunE: runDocumentStatus,
128130
}
129131

132+
var documentOpenCmd = &cobra.Command{
133+
Use: "open <docid>",
134+
Short: "Open a document in the default web browser",
135+
Long: `Open the document view page in the default web browser.
136+
137+
The URL is built from the configured subdomain (no API request is made).
138+
Pass --no-browser (or -n) to print the URL without launching the browser.`,
139+
Args: cobra.ExactArgs(1),
140+
RunE: runDocumentOpen,
141+
}
142+
130143
var documentDownloadCmd = &cobra.Command{
131144
Use: "download <docid>",
132145
Short: "Download a document as PDF",
@@ -150,6 +163,7 @@ func init() {
150163
documentCmd.AddCommand(documentDeleteCmd)
151164
documentCmd.AddCommand(documentStatusCmd)
152165
documentCmd.AddCommand(documentDownloadCmd)
166+
documentCmd.AddCommand(documentOpenCmd)
153167

154168
f := documentSearchCmd.Flags()
155169
f.StringVar(&docSearchBody, "body", "", "search condition JSON: inline, file path, or - for stdin")
@@ -184,6 +198,9 @@ func init() {
184198

185199
dlf := documentDownloadCmd.Flags()
186200
dlf.StringVarP(&docDownloadOutput, "output", "o", "", "output path: FILE, DIR/, or - for stdout (default: server-provided filename in current directory)")
201+
202+
of := documentOpenCmd.Flags()
203+
of.BoolVarP(&docOpenNoBrowser, "no-browser", "n", false, "print the URL without launching the browser")
187204
}
188205

189206
func runDocumentSearch(cmd *cobra.Command, args []string) error {
@@ -401,6 +418,28 @@ func runDocumentDownload(cmd *cobra.Command, args []string) error {
401418
return nil
402419
}
403420

421+
func runDocumentOpen(_ *cobra.Command, args []string) error {
422+
docID, err := parseDocID(args[0])
423+
if err != nil {
424+
return err
425+
}
426+
sub, err := resolveSubdomain()
427+
if err != nil {
428+
return err
429+
}
430+
url := xpoint.NewClient(sub, xpoint.Auth{}).DocumentURL(docID)
431+
432+
if docOpenNoBrowser {
433+
fmt.Fprintln(os.Stdout, url)
434+
return nil
435+
}
436+
fmt.Fprintf(os.Stdout, "Opening %s\n", url)
437+
if err := openBrowser(url); err != nil {
438+
return fmt.Errorf("open browser: %w (run with --no-browser to print the URL)", err)
439+
}
440+
return nil
441+
}
442+
404443
// resolveDownloadPath decides the on-disk path for a downloaded PDF.
405444
//
406445
// When out is empty, the server-provided filename is used in the current

cmd/document_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,43 @@ func TestRunDocumentEdit_RequiresBody(t *testing.T) {
167167
}
168168
}
169169

170+
func TestRunDocumentOpen_InvalidDocID(t *testing.T) {
171+
err := runDocumentOpen(documentOpenCmd, []string{"0"})
172+
if err == nil || !strings.Contains(err.Error(), "invalid docid") {
173+
t.Errorf("err = %v", err)
174+
}
175+
}
176+
177+
func TestRunDocumentOpen_NoBrowserPrintsURL(t *testing.T) {
178+
docOpenNoBrowser = true
179+
t.Cleanup(func() { docOpenNoBrowser = false })
180+
t.Setenv("XPOINT_SUBDOMAIN", "acme")
181+
182+
origStdout := os.Stdout
183+
r, w, err := os.Pipe()
184+
if err != nil {
185+
t.Fatalf("pipe: %v", err)
186+
}
187+
os.Stdout = w
188+
t.Cleanup(func() { os.Stdout = origStdout })
189+
190+
done := make(chan string, 1)
191+
go func() {
192+
b, _ := io.ReadAll(r)
193+
done <- string(b)
194+
}()
195+
196+
if err := runDocumentOpen(documentOpenCmd, []string{"266248"}); err != nil {
197+
t.Fatalf("runDocumentOpen: %v", err)
198+
}
199+
_ = w.Close()
200+
got := strings.TrimSpace(<-done)
201+
want := "https://acme.atledcloud.jp/xpoint/form.do?act=view&docid=266248"
202+
if got != want {
203+
t.Errorf("stdout = %q, want %q", got, want)
204+
}
205+
}
206+
170207
func TestRunDocumentEdit_InvalidDocID(t *testing.T) {
171208
err := runDocumentEdit(documentEditCmd, []string{"not-a-number"})
172209
if err == nil || !strings.Contains(err.Error(), "invalid docid") {

0 commit comments

Comments
 (0)