Skip to content

Commit dbc1b8d

Browse files
committed
feat(pub): update publication commands with output options and plaintext support
1 parent 3db8ba0 commit dbc1b8d

File tree

7 files changed

+231
-110
lines changed

7 files changed

+231
-110
lines changed

cmd/publication_commands.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,15 +214,22 @@ Examples:
214214
isDraft, _ := cmd.Flags().GetBool("draft")
215215
preview, _ := cmd.Flags().GetBool("preview")
216216
validate, _ := cmd.Flags().GetBool("validate")
217+
output, _ := cmd.Flags().GetString("output")
218+
plaintext, _ := cmd.Flags().GetBool("plaintext")
219+
txt, _ := cmd.Flags().GetBool("txt")
220+
221+
if txt {
222+
plaintext = true
223+
}
217224

218225
defer c.handler.Close()
219226

220227
if preview {
221-
return c.handler.PostPreview(cmd.Context(), noteID, isDraft)
228+
return c.handler.PostPreview(cmd.Context(), noteID, isDraft, output, plaintext)
222229
}
223230

224231
if validate {
225-
return c.handler.PostValidate(cmd.Context(), noteID, isDraft)
232+
return c.handler.PostValidate(cmd.Context(), noteID, isDraft, output, plaintext)
226233
}
227234

228235
return c.handler.Post(cmd.Context(), noteID, isDraft)
@@ -231,6 +238,9 @@ Examples:
231238
postCmd.Flags().Bool("draft", false, "Create as draft instead of publishing")
232239
postCmd.Flags().Bool("preview", false, "Show what would be posted without actually posting")
233240
postCmd.Flags().Bool("validate", false, "Validate markdown conversion without posting")
241+
postCmd.Flags().StringP("output", "o", "", "Write document to file (defaults to JSON format)")
242+
postCmd.Flags().Bool("plaintext", false, "Use plaintext format for output file")
243+
postCmd.Flags().Bool("txt", false, "Alias for --plaintext")
234244
root.AddCommand(postCmd)
235245

236246
patchCmd := &cobra.Command{
@@ -257,22 +267,32 @@ Examples:
257267

258268
preview, _ := cmd.Flags().GetBool("preview")
259269
validate, _ := cmd.Flags().GetBool("validate")
270+
output, _ := cmd.Flags().GetString("output")
271+
plaintext, _ := cmd.Flags().GetBool("plaintext")
272+
txt, _ := cmd.Flags().GetBool("txt")
273+
274+
if txt {
275+
plaintext = true
276+
}
260277

261278
defer c.handler.Close()
262279

263280
if preview {
264-
return c.handler.PatchPreview(cmd.Context(), noteID)
281+
return c.handler.PatchPreview(cmd.Context(), noteID, output, plaintext)
265282
}
266283

267284
if validate {
268-
return c.handler.PatchValidate(cmd.Context(), noteID)
285+
return c.handler.PatchValidate(cmd.Context(), noteID, output, plaintext)
269286
}
270287

271288
return c.handler.Patch(cmd.Context(), noteID)
272289
},
273290
}
274291
patchCmd.Flags().Bool("preview", false, "Show what would be updated without actually patching")
275292
patchCmd.Flags().Bool("validate", false, "Validate markdown conversion without patching")
293+
patchCmd.Flags().StringP("output", "o", "", "Write document to file (defaults to JSON format)")
294+
patchCmd.Flags().Bool("plaintext", false, "Use plaintext format for output file")
295+
patchCmd.Flags().Bool("txt", false, "Alias for --plaintext")
276296
root.AddCommand(patchCmd)
277297

278298
pushCmd := &cobra.Command{

internal/handlers/publication.go

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package handlers
33

44
import (
55
"context"
6+
"encoding/json"
67
"fmt"
8+
"os"
79
"path/filepath"
810
"time"
911

@@ -549,8 +551,55 @@ func (h *PublicationHandler) prepareDocumentForPublish(ctx context.Context, note
549551
return note, doc, nil
550552
}
551553

554+
// writeDocumentOutput writes document to a file in JSON or plaintext format
555+
func writeDocumentOutput(doc *public.Document, note *models.Note, outputPath string, plaintext bool) error {
556+
var content []byte
557+
var err error
558+
559+
if plaintext {
560+
status := "published"
561+
if note != nil && note.IsDraft {
562+
status = "draft"
563+
}
564+
565+
output := "Document Preview\n"
566+
output += "================\n\n"
567+
output += fmt.Sprintf("Title: %s\n", doc.Title)
568+
output += fmt.Sprintf("Status: %s\n", status)
569+
if note != nil {
570+
output += fmt.Sprintf("Note ID: %d\n", note.ID)
571+
if note.LeafletRKey != nil {
572+
output += fmt.Sprintf("RKey: %s\n", *note.LeafletRKey)
573+
}
574+
}
575+
output += fmt.Sprintf("Pages: %d\n", len(doc.Pages))
576+
if len(doc.Pages) > 0 {
577+
output += fmt.Sprintf("Blocks: %d\n", len(doc.Pages[0].Blocks))
578+
}
579+
if doc.PublishedAt != "" {
580+
output += fmt.Sprintf("PublishedAt: %s\n", doc.PublishedAt)
581+
}
582+
if doc.Author != "" {
583+
output += fmt.Sprintf("Author: %s\n", doc.Author)
584+
}
585+
586+
content = []byte(output)
587+
} else {
588+
content, err = json.MarshalIndent(doc, "", " ")
589+
if err != nil {
590+
return fmt.Errorf("failed to marshal document to JSON: %w", err)
591+
}
592+
}
593+
594+
if err := os.WriteFile(outputPath, content, 0644); err != nil {
595+
return fmt.Errorf("failed to write output to %s: %w", outputPath, err)
596+
}
597+
598+
return nil
599+
}
600+
552601
// PostPreview shows what would be posted without actually posting
553-
func (h *PublicationHandler) PostPreview(ctx context.Context, noteID int64, isDraft bool) error {
602+
func (h *PublicationHandler) PostPreview(ctx context.Context, noteID int64, isDraft bool, outputPath string, plaintext bool) error {
554603
if !h.atproto.IsAuthenticated() {
555604
return fmt.Errorf("not authenticated - run 'noteleaf pub auth' first")
556605
}
@@ -574,13 +623,25 @@ func (h *PublicationHandler) PostPreview(ctx context.Context, noteID int64, isDr
574623
ui.Infoln(" PublishedAt: %s", doc.PublishedAt)
575624
}
576625
ui.Infoln(" Note ID: %d", note.ID)
626+
627+
if outputPath != "" {
628+
if err := writeDocumentOutput(doc, note, outputPath, plaintext); err != nil {
629+
return err
630+
}
631+
format := "JSON"
632+
if plaintext {
633+
format = "plaintext"
634+
}
635+
ui.Successln("Output written to %s (%s format)", outputPath, format)
636+
}
637+
577638
ui.Successln("Preview complete - no changes made")
578639

579640
return nil
580641
}
581642

582643
// PostValidate validates markdown conversion without posting
583-
func (h *PublicationHandler) PostValidate(ctx context.Context, noteID int64, isDraft bool) error {
644+
func (h *PublicationHandler) PostValidate(ctx context.Context, noteID int64, isDraft bool, outputPath string, plaintext bool) error {
584645
if !h.atproto.IsAuthenticated() {
585646
return fmt.Errorf("not authenticated - run 'noteleaf pub auth' first")
586647
}
@@ -595,11 +656,22 @@ func (h *PublicationHandler) PostValidate(ctx context.Context, noteID int64, isD
595656
ui.Infoln(" Title: %s", doc.Title)
596657
ui.Infoln(" Blocks converted: %d", len(doc.Pages[0].Blocks))
597658

659+
if outputPath != "" {
660+
if err := writeDocumentOutput(doc, note, outputPath, plaintext); err != nil {
661+
return err
662+
}
663+
format := "JSON"
664+
if plaintext {
665+
format = "plaintext"
666+
}
667+
ui.Successln("Output written to %s (%s format)", outputPath, format)
668+
}
669+
598670
return nil
599671
}
600672

601673
// PatchPreview shows what would be patched without actually patching
602-
func (h *PublicationHandler) PatchPreview(ctx context.Context, noteID int64) error {
674+
func (h *PublicationHandler) PatchPreview(ctx context.Context, noteID int64, outputPath string, plaintext bool) error {
603675
if !h.atproto.IsAuthenticated() {
604676
return fmt.Errorf("not authenticated - run 'noteleaf pub auth' first")
605677
}
@@ -628,13 +700,25 @@ func (h *PublicationHandler) PatchPreview(ctx context.Context, noteID int64) err
628700
if doc.PublishedAt != "" {
629701
ui.Infoln(" PublishedAt: %s", doc.PublishedAt)
630702
}
703+
704+
if outputPath != "" {
705+
if err := writeDocumentOutput(doc, note, outputPath, plaintext); err != nil {
706+
return err
707+
}
708+
format := "JSON"
709+
if plaintext {
710+
format = "plaintext"
711+
}
712+
ui.Successln("Output written to %s (%s format)", outputPath, format)
713+
}
714+
631715
ui.Successln("Preview complete - no changes made")
632716

633717
return nil
634718
}
635719

636720
// PatchValidate validates markdown conversion without patching
637-
func (h *PublicationHandler) PatchValidate(ctx context.Context, noteID int64) error {
721+
func (h *PublicationHandler) PatchValidate(ctx context.Context, noteID int64, outputPath string, plaintext bool) error {
638722
if !h.atproto.IsAuthenticated() {
639723
return fmt.Errorf("not authenticated - run 'noteleaf pub auth' first")
640724
}
@@ -655,6 +739,17 @@ func (h *PublicationHandler) PatchValidate(ctx context.Context, noteID int64) er
655739
ui.Infoln(" RKey: %s", *note.LeafletRKey)
656740
ui.Infoln(" Blocks converted: %d", len(doc.Pages[0].Blocks))
657741

742+
if outputPath != "" {
743+
if err := writeDocumentOutput(doc, note, outputPath, plaintext); err != nil {
744+
return err
745+
}
746+
format := "JSON"
747+
if plaintext {
748+
format = "plaintext"
749+
}
750+
ui.Successln("Output written to %s (%s format)", outputPath, format)
751+
}
752+
658753
return nil
659754
}
660755

internal/handlers/publication_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,7 +1019,7 @@ func TestPublicationHandler(t *testing.T) {
10191019
handler := CreateHandler(t, NewPublicationHandler)
10201020
ctx := context.Background()
10211021

1022-
err := handler.PostPreview(ctx, 1, false)
1022+
err := handler.PostPreview(ctx, 1, false, "", false)
10231023
if err == nil {
10241024
t.Error("Expected error when not authenticated")
10251025
}
@@ -1051,7 +1051,7 @@ func TestPublicationHandler(t *testing.T) {
10511051
t.Fatalf("Failed to restore session: %v", err)
10521052
}
10531053

1054-
err = handler.PostPreview(ctx, 999, false)
1054+
err = handler.PostPreview(ctx, 999, false, "", false)
10551055
if err == nil {
10561056
t.Error("Expected error when note does not exist")
10571057
}
@@ -1095,7 +1095,7 @@ func TestPublicationHandler(t *testing.T) {
10951095
t.Fatalf("Failed to restore session: %v", err)
10961096
}
10971097

1098-
err = handler.PostPreview(ctx, id, false)
1098+
err = handler.PostPreview(ctx, id, false, "", false)
10991099
if err == nil {
11001100
t.Error("Expected error when note already published")
11011101
}
@@ -1135,7 +1135,7 @@ func TestPublicationHandler(t *testing.T) {
11351135
t.Fatalf("Failed to restore session: %v", err)
11361136
}
11371137

1138-
err = handler.PostPreview(ctx, id, false)
1138+
err = handler.PostPreview(ctx, id, false, "", false)
11391139
suite.AssertNoError(err, "preview should succeed")
11401140
})
11411141

@@ -1169,7 +1169,7 @@ func TestPublicationHandler(t *testing.T) {
11691169
t.Fatalf("Failed to restore session: %v", err)
11701170
}
11711171

1172-
err = handler.PostPreview(ctx, id, true)
1172+
err = handler.PostPreview(ctx, id, true, "", false)
11731173
suite.AssertNoError(err, "preview draft should succeed")
11741174
})
11751175
})
@@ -1182,7 +1182,7 @@ func TestPublicationHandler(t *testing.T) {
11821182
handler := CreateHandler(t, NewPublicationHandler)
11831183
ctx := context.Background()
11841184

1185-
err := handler.PostValidate(ctx, 1, false)
1185+
err := handler.PostValidate(ctx, 1, false, "", false)
11861186
if err == nil {
11871187
t.Error("Expected error when not authenticated")
11881188
}
@@ -1222,7 +1222,7 @@ func TestPublicationHandler(t *testing.T) {
12221222
t.Fatalf("Failed to restore session: %v", err)
12231223
}
12241224

1225-
err = handler.PostValidate(ctx, id, false)
1225+
err = handler.PostValidate(ctx, id, false, "", false)
12261226
suite.AssertNoError(err, "validation should succeed")
12271227
})
12281228
})
@@ -1235,7 +1235,7 @@ func TestPublicationHandler(t *testing.T) {
12351235
handler := CreateHandler(t, NewPublicationHandler)
12361236
ctx := context.Background()
12371237

1238-
err := handler.PatchPreview(ctx, 1)
1238+
err := handler.PatchPreview(ctx, 1, "", false)
12391239
if err == nil {
12401240
t.Error("Expected error when not authenticated")
12411241
}
@@ -1267,7 +1267,7 @@ func TestPublicationHandler(t *testing.T) {
12671267
t.Fatalf("Failed to restore session: %v", err)
12681268
}
12691269

1270-
err = handler.PatchPreview(ctx, 999)
1270+
err = handler.PatchPreview(ctx, 999, "", false)
12711271
if err == nil {
12721272
t.Error("Expected error when note does not exist")
12731273
}
@@ -1307,7 +1307,7 @@ func TestPublicationHandler(t *testing.T) {
13071307
t.Fatalf("Failed to restore session: %v", err)
13081308
}
13091309

1310-
err = handler.PatchPreview(ctx, id)
1310+
err = handler.PatchPreview(ctx, id, "", false)
13111311
if err == nil {
13121312
t.Error("Expected error when note not published")
13131313
}
@@ -1354,7 +1354,7 @@ func TestPublicationHandler(t *testing.T) {
13541354
t.Fatalf("Failed to restore session: %v", err)
13551355
}
13561356

1357-
err = handler.PatchPreview(ctx, id)
1357+
err = handler.PatchPreview(ctx, id, "", false)
13581358
suite.AssertNoError(err, "preview should succeed")
13591359
})
13601360
})
@@ -1367,7 +1367,7 @@ func TestPublicationHandler(t *testing.T) {
13671367
handler := CreateHandler(t, NewPublicationHandler)
13681368
ctx := context.Background()
13691369

1370-
err := handler.PatchValidate(ctx, 1)
1370+
err := handler.PatchValidate(ctx, 1, "", false)
13711371
if err == nil {
13721372
t.Error("Expected error when not authenticated")
13731373
}
@@ -1412,7 +1412,7 @@ func TestPublicationHandler(t *testing.T) {
14121412
t.Fatalf("Failed to restore session: %v", err)
14131413
}
14141414

1415-
err = handler.PatchValidate(ctx, id)
1415+
err = handler.PatchValidate(ctx, id, "", false)
14161416
suite.AssertNoError(err, "validation should succeed")
14171417
})
14181418
})

0 commit comments

Comments
 (0)