Commit a7b9e2f
feat(oq): fix query bugs and add schema content fields for first-class OpenAPI queries (#182)
* fix(oq): support single-quoted strings and fix group-by pipeline filtering
Single-quoted strings in expressions (e.g. `name == 'Foo'`) were silently
parsed as field references, causing where/select predicates to return empty
results. This made refs-out, blast-radius, and matches appear broken when
users naturally used single quotes.
Group-by, cycles, and clusters wrote results only to result.Groups but not
result.Rows, so downstream pipeline stages (where, sort, take, count) saw
zero rows and produced empty output. Now all group-producing stages emit
both Groups and Rows with a new GroupRowResult kind.
Also adds query and query-reference subcommands to README.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(oq): add schema content and operation response fields for first-class OpenAPI queries
* feat: add format(yaml) output mode for raw YAML node access
* feat: add parent stage and rename edge fields to via/key/from
* fix: address PR feedback — single-quote edge cases, GroupRowResult fields, parser quote handling
* refactor: remove legacy edge_kind/edge_label/edge_from aliases
* fix: gofmt exec.go
* chore: update cmd/openapi dependency to latest root module
* fix: remove unused fields assignment in FormatYAML to fix ineffassign lint
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address PR review — single-quote handling in matches, splitKeywordCall, findUnquotedSemicolon
- Add stripQuotes helper to properly handle \x00-prefixed single-quote
tokens in both infix and function-call forms of matches()
- Update splitKeywordCall to track single-quoted strings when matching
parentheses, preventing parse failures with parens inside quotes
- Update findUnquotedSemicolon to track single-quoted strings, preventing
incorrect semicolon splitting inside quoted strings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor: remove legacy oq syntax, split yaml format into emit stage
Legacy syntax removed:
- where → select(expr)
- sort → sort_by(field; desc)
- take/head → first(N)
- select <fields> → pick <fields>
- group-by → group_by(field)
- count → length
New emit stage:
- Replaces format(yaml) for raw YAML node extraction
- Wraps output under the schema/operation key name
- Separated from output format concerns (table/json/markdown/toon)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: gofmt oq.go
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: preserve FormatHint and EmitYAML through execWhere and execUnique
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add query-reference cross-reference to spec query help text
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: propagate FormatHint/EmitYAML through all exec functions, add escape handling consistency
- Add deriveResult helper to consistently propagate Fields, FormatHint,
and EmitYAML when creating new Result objects
- Replace all 16 bare &Result{Fields: result.Fields} with deriveResult
- Add backslash escape handling to findUnquotedSemicolon and
splitKeywordCall for consistency with splitPipeline/splitSemicolonArgs
- Move examples to cobra Example field, clean up help text
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: move pipeline stages, operators, and examples after Usage section
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: restructure query help — Usage+Flags first, stages, then examples
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: add pipeline structure hint to query help
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add string ops, arithmetic, contains, and count() to expression system
String functions (composable — take expressions, not just field names):
lower(expr), upper(expr), trim(expr), len(expr)
startswith(expr, prefix), endswith(expr, suffix)
contains(expr, substr), replace(expr, old, new)
split(expr, sep) → array, split(expr, sep, N) → Nth segment
Arithmetic operators: +, -, *, / with correct precedence
contains infix operator: field contains "value"
Works on both strings and arrays
count() expression function: count(field) returns array/string length
Array value type (KindArray):
tags, required, enum fields now return arrays
select(required contains "id"), select(tags contains "billing")
Function composition works naturally:
select(startswith(lower(name), "p"))
select(split(path, "/", 1) == "users")
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add coverage for string ops, arithmetic, contains, and array values
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: respect pick field projection after group_by
When pick sets explicit fields, group results now go through the
standard field-based formatter instead of the hardcoded group formatter.
This allows queries like: group_by(type) | pick key, names
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(oq): simplify query implementation — deduplicate parsing, traversals, and expressions
- Unify splitPipeline/splitSemicolonArgs into generic splitAtDelim
- Deduplicate identical isCall/!isCall branches in first/last/sample/neighbors
- Remove redundant format isCall no-op assignment
- Unify traverseRefsOut/traverseProperties/traverseItems via traverseOutEdges
- Extract nodeIDsToRows from traverseReachable/traverseAncestors
- Merge identical count()/len() expression functions
- Remove dead StageWhere case from execStage (unreachable via execStageWithEnv)
- Fix perfsprint lint: use e.Value directly instead of fmt.Sprintf
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(oq): add navigation stages for parameters, responses, content-types, headers
Implements the navigation model from DESIGN.md:
- New row types: ParameterResult, ResponseResult, RequestBodyResult,
ContentTypeResult, HeaderResult
- New stages: parameters, responses, request-body, content-types,
headers, schema (singular), operation (back-nav)
- Context propagation: child rows inherit status_code, operation name
- SchemaByPtr on graph for bridging nav rows back to schema graph
- Remove schemas.components and schemas.inline sources (use select())
- Fix emit to use path instead of name for YAML key attribution
Enables queries like:
operations | responses | content-types | select(media_type == "text/event-stream") | operation | unique
operations | parameters | select(in == "cookie") | pick name, in, operation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update query help text, reference, and READMEs for navigation stages
- Update query command help to show navigation stages and remove
schemas.components/schemas.inline sources
- Add parameter, response, request-body, content-type, and header
fields documentation to query-reference
- Add navigation examples to all READMEs
- Update oq/README.md with navigation stages and new row type fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(oq): add components.* sources, security stages, and emit for nav rows
- Add components.schemas, components.parameters, components.responses,
components.request-bodies, components.headers, components.security-schemes
as pipeline sources for querying reusable OpenAPI component definitions
- Add security stage: operations | security yields SecurityRequirementResult
rows with scheme_name, scheme_type, scopes, scope_count fields
- Add SecuritySchemeResult row type for components.security-schemes source
with name, type, in, scheme, bearer_format, description, has_flows fields
- Fix emit for navigation rows (responses, parameters, etc.) — getRootNode
now handles all row types instead of returning nil
- Store Index on SchemaGraph for component/security access
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(oq): resolve $ref in schema stage, inherit global security, fix unique after pick
Bug fixes:
- B1: unique now deduplicates by projected field values when pick is active,
falling back to row identity when no projection is set
- B2: schema stage follows $ref edges via resolveRefTarget to return the
actual component schema instead of the inline $ref wrapper node
- B3: security stage inherits global security requirements when an operation
has no per-operation security (nil vs empty array distinction)
- D3: emit uses contextual compound keys for nav rows (e.g.,
"listEntities/200" for responses, "createPet/parameters/name" for params)
Tests:
- Extend petstore fixture with security schemes, deprecated parameters,
SSE endpoint, headers, multiple content types, component params/responses
- Add 20 new tests covering navigation stages, security inheritance,
security opt-out, $ref resolution, unique-after-pick, content-type
dedup, response headers, deprecated parameters, SSE queries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(oq): rename ParamName to ComponentKey, fix component response names, add group_by name field
- Rename Row.ParamName → Row.ComponentKey for consistent component key
storage across all components.* sources
- Fix components.responses: use ComponentKey instead of StatusCode for
the component map key name (B4)
- Fix RequestBodyResult.name: show ComponentKey for component request
bodies instead of hardcoded "request-body"
- Add group_by(field; name_field) syntax: optional second arg specifies
which field to collect as group names (default: "name") (D2)
- Add tests: component response name/status_code separation,
group_by with name_field
- Update AUDIT.md: mark B1-B4, D2, D3 as fixed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: remove audit doc
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(oq): guard group_by semicolon args against nil slice access
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(oq): add depth-limited reachable(N) for bounded traversal
reachable without args remains unlimited (full transitive closure).
reachable(N) limits BFS to N hops from the seed schemas.
Examples:
schema | reachable(1) → direct children only
schema | reachable(2) → children + their children (follows $refs)
schema | reachable → everything (unchanged behavior)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update query help and reference for reachable(N), components.*, security, group_by name field
- Add components.* sources, security stage, reachable(N) to help text
- Add security scheme and security requirement field documentation
- Document group_by(field; name_field) and unique-after-pick behavior
- Update expression docs: contains, string functions, arithmetic, single quotes
- Overhaul examples: organized by category (schema analysis, operations &
navigation, security, content auditing, advanced) with representative
queries covering the full feature set
- Update CLI usage examples to showcase navigation and security queries
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>1 parent 5083f6f commit a7b9e2f
19 files changed
Lines changed: 3534 additions & 664 deletions
File tree
- cmd/openapi
- commands/openapi
- graph
- oq
- expr
- testdata
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
141 | 150 | | |
142 | 151 | | |
143 | 152 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
134 | 134 | | |
135 | 135 | | |
136 | 136 | | |
| 137 | + | |
| 138 | + | |
137 | 139 | | |
138 | 140 | | |
139 | 141 | | |
| |||
179 | 181 | | |
180 | 182 | | |
181 | 183 | | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
182 | 190 | | |
183 | 191 | | |
184 | 192 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
| 28 | + | |
27 | 29 | | |
28 | 30 | | |
29 | 31 | | |
| |||
1196 | 1198 | | |
1197 | 1199 | | |
1198 | 1200 | | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
| 1207 | + | |
| 1208 | + | |
| 1209 | + | |
| 1210 | + | |
| 1211 | + | |
| 1212 | + | |
| 1213 | + | |
| 1214 | + | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
| 1234 | + | |
| 1235 | + | |
| 1236 | + | |
| 1237 | + | |
| 1238 | + | |
| 1239 | + | |
| 1240 | + | |
| 1241 | + | |
| 1242 | + | |
| 1243 | + | |
| 1244 | + | |
| 1245 | + | |
| 1246 | + | |
| 1247 | + | |
| 1248 | + | |
| 1249 | + | |
| 1250 | + | |
| 1251 | + | |
| 1252 | + | |
| 1253 | + | |
| 1254 | + | |
| 1255 | + | |
| 1256 | + | |
1199 | 1257 | | |
1200 | 1258 | | |
1201 | 1259 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
74 | 53 | | |
75 | 54 | | |
76 | 55 | | |
| |||
81 | 60 | | |
82 | 61 | | |
83 | 62 | | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
84 | 82 | | |
85 | 83 | | |
86 | 84 | | |
| |||
146 | 144 | | |
147 | 145 | | |
148 | 146 | | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
149 | 154 | | |
150 | 155 | | |
151 | 156 | | |
| |||
0 commit comments