coverage~bigbes/sr-ht-spec64cae3afmcpsrv/list.go

Coverage
86.2% 25/29 statements
Δ
+0.0
Blob
48802f9
1 package mcpsrv
2
3 import (
4 "context"
5 "errors"
6 "strings"
7 )
8
9 type listInput struct {
10 Space string `json:"space,omitempty" jsonschema:"the space to list documents from, written \"~owner/name\". Omit it to list the spaces on this instance instead."`
11 Rev string `json:"rev,omitempty" jsonschema:"pin the listing to one immutable revision, given as a git object name (lowercase hex). Omit to list the space's approved head. Requires space."`
12 }
13
14 type spaceEntry struct {
15 // Space is the name every other tool takes: "~owner/name".
16 Space string `json:"space"`
17 Owner string `json:"owner"`
18 Name string `json:"name"`
19 }
20
21 type documentEntry struct {
22 // ID is how spec_read addresses this document.
23 ID string `json:"id"`
24 DocID string `json:"doc_id,omitempty"`
25 Path string `json:"path"`
26 Blob string `json:"blob,omitempty"`
27 Title string `json:"title,omitempty"`
28 // Section is the top-level directory the document lives under, and is what
29 // spec_search's sections filter takes.
30 Section string `json:"section,omitempty"`
31 // Status is the authored lifecycle marker, not approval state.
32 Status string `json:"status,omitempty"`
33 Summary string `json:"summary,omitempty"`
34 Tags []string `json:"tags,omitempty"`
35 }
36
37 // listOutput carries whichever of the two listings was asked for. One tool
38 // rather than two because the answer to "what can I read" is one question with
39 // a drill-down, and an agent that has just been handed a space name should not
40 // have to find a second tool to use it.
41 type listOutput struct {
42 // Spaces is set when no space was named.
43 Spaces []spaceEntry `json:"spaces,omitempty"`
44 // Space, Rev and Documents are set when one was.
45 Space string `json:"space,omitempty"`
46 // Rev is the revision listed, resolved to an immutable commit. Pass it to
47 // spec_read to read any of these documents at exactly this revision.
48 Rev string `json:"rev,omitempty"`
49 Documents []documentEntry `json:"documents,omitempty"`
50 }
51
52 6 func listHandler(ctx context.Context, b Backend, in listInput) (listOutput, error) {
53 6 if err := requireRead(ctx); err != nil {
54 1 return listOutput{}, err
55 1 }
56 5 if strings.TrimSpace(in.Space) == "" {
57 2 // A rev with no space is a caller that meant to name one. Ignoring it
58 2 // would answer a different question than was asked and look like it
59 2 // had worked.
60 2 if strings.TrimSpace(in.Rev) != "" {
61 1 return listOutput{}, errors.New("rev names a revision of a space; pass space as well, or omit rev to list spaces")
62 1 }
63 1 return listSpaces(ctx, b)
64 }
65
66 3 ref, err := parseSpace(in.Space)
67 3 if err != nil {
68 0 return listOutput{}, err
69 0 }
70 3 rev, err := parseRev(in.Rev)
71 3 if err != nil {
72 1 return listOutput{}, err
73 1 }
74 2 sp, err := b.Docs.OpenSpace(ctx, ref)
75 2 if err != nil {
76 0 return listOutput{}, missingOrDenied(err, "spec_list", noSpace(ref))
77 0 }
78 2 arc, _, resolved, err := archiveAt(ctx, b, sp, rev)
79 2 if err != nil {
80 0 return listOutput{}, missingOrDenied(err, "spec_list", noRevision(ref, rev))
81 0 }
82
83 2 out := listOutput{Space: ref.String(), Rev: resolved, Documents: make([]documentEntry, 0, len(arc.All()))}
84 3 for _, p := range arc.All() {
85 3 out.Documents = append(out.Documents, documentEntry{
86 3 ID: p.ID,
87 3 DocID: p.DocID,
88 3 Path: p.Path,
89 3 Blob: p.Blob,
90 3 Title: p.Title,
91 3 Section: p.Section,
92 3 Status: string(p.Status),
93 3 Summary: p.Summary,
94 3 Tags: p.Tags,
95 3 })
96 3 }
97 2 return out, nil
98 }
99
100 1 func listSpaces(ctx context.Context, b Backend) (listOutput, error) {
101 1 spaces, err := b.Docs.ListSpaces(ctx)
102 1 if err != nil {
103 0 // No space was named, so there is no "that one does not exist" to
104 0 // answer: whatever went wrong here is this service's.
105 0 return listOutput{}, internalError(err, "spec_list")
106 0 }
107 1 out := listOutput{Spaces: make([]spaceEntry, 0, len(spaces))}
108 2 for _, sp := range spaces {
109 2 out.Spaces = append(out.Spaces, spaceEntry{
110 2 Space: sp.Ref.String(),
111 2 Owner: sp.Ref.Owner,
112 2 Name: sp.Ref.Name,
113 2 })
114 2 }
115 1 return out, nil
116 }