classifyOne is the per-operation classifier. It first decides whether the operation can be exposed at all (mutating verbs, async patterns, non-JSON responses are immediately skipped) and then picks between RowJoin / SingleByID / List based on path shape and user configuration.
( specKey, path, method string, op *openapi3.Operation, pathItem *openapi3.PathItem, cfg SpecConfig, )
| 72 | // non-JSON responses are immediately skipped) and then picks between |
| 73 | // RowJoin / SingleByID / List based on path shape and user configuration. |
| 74 | func classifyOne( |
| 75 | specKey, path, method string, |
| 76 | op *openapi3.Operation, |
| 77 | pathItem *openapi3.PathItem, |
| 78 | cfg SpecConfig, |
| 79 | ) OpDescriptor { |
| 80 | d := OpDescriptor{ |
| 81 | SpecKey: specKey, |
| 82 | OperationID: operationID(op, method, path), |
| 83 | Method: method, |
| 84 | PathTemplate: path, |
| 85 | Mode: OpModeSkipped, |
| 86 | } |
| 87 | |
| 88 | // Read-only first cut: anything that mutates server state is out of |
| 89 | // scope until the write-side feature lands. |
| 90 | if method != "GET" { |
| 91 | d.SkipReason = "mutating verb (write-side not yet supported)" |
| 92 | return d |
| 93 | } |
| 94 | |
| 95 | // Operations explicitly disabled via config never appear regardless |
| 96 | // of how they would otherwise classify. |
| 97 | if ov, ok := cfg.Operations[d.OperationID]; ok && ov.Disabled { |
| 98 | d.SkipReason = "disabled by config" |
| 99 | return d |
| 100 | } |
| 101 | |
| 102 | // Find the success response. We only examine 200 — 201/202/204 are |
| 103 | // either non-applicable to GET or signal async/empty-body patterns |
| 104 | // that don't fit the join model. |
| 105 | resp := findSuccessResponse(op) |
| 106 | if resp == nil { |
| 107 | d.SkipReason = "no 200 success response declared" |
| 108 | return d |
| 109 | } |
| 110 | |
| 111 | // Async / file-download responses can't participate in a query — they |
| 112 | // don't return JSON in their response body. |
| 113 | if hasLocationHeader(resp) { |
| 114 | d.SkipReason = "async pattern (Location header on success response)" |
| 115 | return d |
| 116 | } |
| 117 | |
| 118 | jsonContent, mediaType := pickJSONContent(resp) |
| 119 | if jsonContent == nil { |
| 120 | d.SkipReason = fmt.Sprintf("non-JSON response (%s)", mediaType) |
| 121 | return d |
| 122 | } |
| 123 | |
| 124 | // Parameters live in two places — on the path item (shared across |
| 125 | // methods) and on the operation itself. Merge and bucket by location. |
| 126 | pathParams, queryParams, headerParams := collectParams(pathItem, op) |
| 127 | d.PathParams = pathParams |
| 128 | d.QueryParams = queryParams |
| 129 | d.HeaderParams = headerParams |
| 130 | |
| 131 | // Detect a sensible result_path by walking the response schema. List |
no test coverage detected