docs(libraries): Work Order 3 batch 2 — 10 missing endpoints + array-response gate support
New frontmatter'd docs with handler-compatible shapes:
- get_library_types.md (public; []LibraryTypes)
- list_libraries.md (admin; {data: ListLibrariesRow})
- get_library_folders.md (admin; []LibraryFolders)
- list/get stats/resolve/delete processing issues (admin; family-lens 403s
documented)
- scan_library.md (202 + 409 coalescing with active_job_id)
- get/update scan-settings (ScanSettingsResponse)
Gate improvements forced by this batch:
- Array responses: registered slice -> doc example must be a JSON array; key
set compared against the element type
- RawMessage pins may now be arrays
- update scan-settings uses a RawMessage pin: handler always sets Message but
the struct's omitempty would reject correct docs via zero-value marshal
Registry: sqlc models pinned directly (LibraryTypes, LibraryFolders,
ListLibrariesRow — json tags verified in the generated code).
This commit is contained in:
1 parent
c4b62af213
commit
85cdaf7196
12 files changed
+423
-11
No files matched your search
@@ -251,8 +251,24 @@ func validateResponseExample(t *testing.T, doc *APIDoc, schema Schema) {
|
||||
return
|
||||
}
|
||||
|
||||
want := responseTopLevelKeys(t, schema.Response)
|
||||
got := topLevelKeys(doc.ExampleResponse)
|
||||
want, isSlice := responseTopLevelKeys(t, schema.Response)
|
||||
|
||||
var got map[string]bool
|
||||
if isSlice {
|
||||
// Array responses: compare the first sample element's keys.
|
||||
var arr []json.RawMessage
|
||||
if err := json.Unmarshal(doc.ExampleResponse, &arr); err != nil {
|
||||
t.Errorf("%s: example-response must be a JSON array for this route (handler returns a slice)", doc.RelPath)
|
||||
return
|
||||
}
|
||||
if len(arr) == 0 {
|
||||
t.Errorf("%s: example-response array needs at least one sample element to check keys", doc.RelPath)
|
||||
return
|
||||
}
|
||||
got = topLevelKeys(arr[0])
|
||||
} else {
|
||||
got = topLevelKeys(doc.ExampleResponse)
|
||||
}
|
||||
|
||||
for _, k := range sortedKeys(want) {
|
||||
if !got[k] {
|
||||
@@ -312,21 +328,25 @@ func topLevelKeys(data []byte) map[string]bool {
|
||||
|
||||
// responseTopLevelKeys derives the expected top-level key set from the
|
||||
// registered Response: marshal a struct (or its element for slices), or use a
|
||||
// RawMessage pin verbatim.
|
||||
func responseTopLevelKeys(t *testing.T, resp any) map[string]bool {
|
||||
// RawMessage pin verbatim. The second return reports that the handler emits a
|
||||
// JSON ARRAY (slice response) — the doc's example must then be an array too.
|
||||
func responseTopLevelKeys(t *testing.T, resp any) (map[string]bool, bool) {
|
||||
t.Helper()
|
||||
if raw, ok := resp.(json.RawMessage); ok {
|
||||
return topLevelKeys(raw)
|
||||
trimmed := bytes.TrimLeft(raw, " \t\r\n")
|
||||
return topLevelKeys(raw), len(trimmed) > 0 && trimmed[0] == '['
|
||||
}
|
||||
rt := reflect.TypeOf(resp)
|
||||
if rt == nil {
|
||||
return nil
|
||||
return nil, false
|
||||
}
|
||||
isSlice := false
|
||||
if rt.Kind() == reflect.Slice {
|
||||
if rt.Elem().Kind() == reflect.Uint8 {
|
||||
return topLevelKeys(reflect.ValueOf(resp).Bytes()) // []byte payloads
|
||||
return topLevelKeys(reflect.ValueOf(resp).Bytes()), false // []byte payloads
|
||||
}
|
||||
rt = rt.Elem()
|
||||
isSlice = true
|
||||
}
|
||||
if rt.Kind() == reflect.Ptr {
|
||||
rt = rt.Elem()
|
||||
@@ -338,7 +358,7 @@ func responseTopLevelKeys(t *testing.T, resp any) map[string]bool {
|
||||
if err != nil {
|
||||
t.Fatalf("marshal schema response %T: %v", resp, err)
|
||||
}
|
||||
return topLevelKeys(b)
|
||||
return topLevelKeys(b), isSlice
|
||||
}
|
||||
|
||||
func sortedKeys(m map[string]bool) []string {
|
||||
|
||||
Reference in new issue
Block a user