Skip to content

Commit 3a24790

Browse files
authored
Merge pull request #2 from oaswrap/refactor/change-openapi-library
refactor: change openapi library
2 parents 4d75287 + 01e4db2 commit 3a24790

72 files changed

Lines changed: 3049 additions & 3202 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ARCHITECTURE.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ Key global stacks (all touched only during tree construction — effectively sin
103103

104104
### Phase 3 — Output
105105

106-
`WriteSpec()` locks the `SpecCollector`, serialises the `openapi3.Spec` to YAML or JSON, and writes the file.
106+
`WriteSpec()` locks the `SpecCollector`, serialises the `openapi.Document` to YAML or JSON, and writes the file.
107107

108108
---
109109

@@ -144,19 +144,20 @@ dslRespExec
144144

145145
### `SpecCollector` (`spec.go`, `spec_collector.go`)
146146

147-
Thread-safe accumulator wrapping the `openapi3.Reflector`.
147+
Thread-safe accumulator wrapping a persistent `oaswrap/spec/openapi.Document`.
148148

149149
```
150150
SpecCollector (struct declared in spec.go)
151151
├── mu sync.Mutex ← protects all spec mutations
152-
├── reflector ← swaggest/openapi-go reflector
152+
├── doc ← generated OpenAPI document
153+
├── openapiOpts ← oaswrap/spec generator options
153154
└── excludePaths []string
154155
```
155156

156157
`newSpecCollector` (in `spec_collector.go`) is decomposed into:
157-
- `applySchemaOptions` — wires JSON-schema reflector options (generic name shortening, inline refs, type maps)
158+
- `buildOpenAPIOptions` — wires root metadata, security schemes, and reflector options for `oaswrap/spec`
158159
- `shortenGenericName` — strips package paths from generic type-argument names
159-
- `applySpecInfo``applySpecTags` / `applySpecServers` — populates the OpenAPI Info/Tags/Servers blocks
160+
- per-operation registration reflects through a temporary `spec.Router`, then merges the generated document into the collector
160161

161162
Four lock points: `Register`, `RegisterDSLOperation`, `injectRecordedResponseSchema`, `appendExamplesLocked`.
162163

@@ -226,15 +227,15 @@ See `examples/parallel` for a working end-to-end example using schema inference
226227
### DSL path (recommended)
227228

228229
1. `Path` / `Get` / `Response` / `RequestBody` / `ResponseSchema` declare metadata during tree construction.
229-
2. `flushPendingDSLOps` (first `RunTest`) calls `RegisterDSLOperation` which drives the `openapi3.Reflector` directly.
230-
3. `injectInferredRequestSchema` is **skipped** if the reflector already placed a schema for any content-type key in `requestBody.content` — preventing conflict when `Consumes` and `SetRawBody` use different content-type strings.
230+
2. `flushPendingDSLOps` (first `RunTest`) calls `RegisterDSLOperation`, which reflects a temporary `oaswrap/spec` route and merges the generated operation into the collector document.
231+
3. `injectInferredRequestSchema` is **skipped** if registration already placed a schema for any content-type key in `requestBody.content` — preventing conflict when `Consumes` and `SetRawBody` use different content-type strings.
231232

232233
### Runtime-only path (no DSL)
233234

234235
For tests that use `requestBuilder` directly (or through the legacy `Register` codepath):
235236

236237
1. `Register` is called with the builder and recorded response.
237-
2. `AddReqStructure` / `AddRespStructure` drive the reflector from the builder's typed fields.
238+
2. `option.Request` / `option.Response` drive `oaswrap/spec` reflection from the builder's typed fields.
238239
3. `injectInferredRequestSchema` fills any gaps from actual request bytes.
239240
4. `injectInferredSchema` fills response schema from actual response bytes when none was declared.
240241

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[![codecov](https://codecov.io/gh/oaswrap/gswag/graph/badge.svg?token=X8zEVtNy5e)](https://codecov.io/gh/oaswrap/gswag)
77
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
88

9-
Generate OpenAPI 3.0 specs directly from your [Ginkgo](https://github.com/onsi/ginkgo) integration tests.
9+
Generate OpenAPI 3.0, 3.1, or 3.2 specs directly from your [Ginkgo](https://github.com/onsi/ginkgo) integration tests.
1010

1111
Inspired by [rswag](https://github.com/rswag/rswag): define API docs alongside executable tests using a nested DSL.
1212

@@ -246,6 +246,7 @@ Get("Get order", func() {
246246
Init(&Config{
247247
Title: "My API", // required
248248
Version: "1.0.0", // required
249+
OpenAPIVersion: OpenAPI310, // default: 3.1.2
249250
Description: "Public API",
250251
TermsOfService: "https://example.com/terms",
251252
Contact: &ContactConfig{
@@ -306,6 +307,12 @@ Security helpers:
306307
- `APIKeyCookie(name)`
307308
- `OAuth2Implicit(authURL, scopes)`
308309

310+
OpenAPI version constants:
311+
312+
- `OpenAPI300`, `OpenAPI301`, `OpenAPI302`, `OpenAPI303`, `OpenAPI304`
313+
- `OpenAPI310`, `OpenAPI311`, `OpenAPI312`
314+
- `OpenAPI320`
315+
309316
## Gomega Matchers
310317

311318
Matchers operate on `*http.Response` (the object passed to `RunTest` callback):

builder_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func TestRequestBuilder_Do_SuccessRegisters(t *testing.T) {
8989
}
9090

9191
// spec should have registered path
92-
if _, ok := sc.reflector.Spec.Paths.MapOfPathItemValues["/test"]; !ok {
92+
if _, ok := sc.doc.Paths["/test"]; !ok {
9393
t.Fatalf("expected spec to contain /test")
9494
}
9595
}

config.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ const (
1313
JSON
1414
)
1515

16+
// OpenAPI version constants.
17+
const (
18+
OpenAPI300 = "3.0.0"
19+
OpenAPI301 = "3.0.1"
20+
OpenAPI302 = "3.0.2"
21+
OpenAPI303 = "3.0.3"
22+
OpenAPI304 = "3.0.4"
23+
OpenAPI310 = "3.1.0"
24+
OpenAPI311 = "3.1.1"
25+
OpenAPI312 = "3.1.2"
26+
OpenAPI320 = "3.2.0"
27+
)
28+
1629
// ServerConfig describes an OpenAPI server entry.
1730
type ServerConfig struct {
1831
URL string
@@ -65,35 +78,43 @@ type SecuritySchemeConfig struct {
6578
Scopes map[string]string // scope -> description
6679
}
6780

81+
// Security scheme type constants.
82+
const (
83+
SecurityTypeHTTP = "http"
84+
SecurityTypeAPIKey = "apiKey"
85+
SecurityTypeOAuth2 = "oauth2"
86+
)
87+
6888
// BearerJWT returns a SecuritySchemeConfig for an HTTP Bearer JWT scheme.
6989
func BearerJWT() SecuritySchemeConfig {
70-
return SecuritySchemeConfig{Type: "http", Scheme: "bearer", BearerFormat: "JWT"}
90+
return SecuritySchemeConfig{Type: SecurityTypeHTTP, Scheme: "bearer", BearerFormat: "JWT"}
7191
}
7292

7393
// APIKeyHeader returns a SecuritySchemeConfig for an API key passed in a header.
7494
func APIKeyHeader(headerName string) SecuritySchemeConfig {
75-
return SecuritySchemeConfig{Type: "apiKey", In: "header", Name: headerName}
95+
return SecuritySchemeConfig{Type: SecurityTypeAPIKey, In: "header", Name: headerName}
7696
}
7797

7898
// APIKeyQuery returns a SecuritySchemeConfig for an API key passed in a query param.
7999
func APIKeyQuery(paramName string) SecuritySchemeConfig {
80-
return SecuritySchemeConfig{Type: "apiKey", In: "query", Name: paramName}
100+
return SecuritySchemeConfig{Type: SecurityTypeAPIKey, In: "query", Name: paramName}
81101
}
82102

83103
// APIKeyCookie returns a SecuritySchemeConfig for an API key passed in a cookie.
84104
func APIKeyCookie(cookieName string) SecuritySchemeConfig {
85-
return SecuritySchemeConfig{Type: "apiKey", In: "cookie", Name: cookieName}
105+
return SecuritySchemeConfig{Type: SecurityTypeAPIKey, In: "cookie", Name: cookieName}
86106
}
87107

88108
// OAuth2Implicit returns a SecuritySchemeConfig for an OAuth2 implicit flow.
89109
func OAuth2Implicit(authURL string, scopes map[string]string) SecuritySchemeConfig {
90-
return SecuritySchemeConfig{Type: "oauth2", AuthorizationURL: authURL, Scopes: scopes}
110+
return SecuritySchemeConfig{Type: SecurityTypeOAuth2, AuthorizationURL: authURL, Scopes: scopes}
91111
}
92112

93113
// Config holds global settings for gswag.
94114
type Config struct {
95115
Title string
96116
Version string
117+
OpenAPIVersion string // default: "3.1.2"
97118
Description string
98119
TermsOfService string
99120
Contact *ContactConfig
@@ -153,6 +174,9 @@ func Init(cfg *Config) {
153174
if cfg.Version == "" {
154175
cfg.Version = "0.1.0"
155176
}
177+
if cfg.OpenAPIVersion == "" {
178+
cfg.OpenAPIVersion = OpenAPI312
179+
}
156180
globalConfig = cfg
157181
globalCollector = newSpecCollector(cfg)
158182
// Reset the flush gate so re-initialisation (e.g. across test suites in the

dsl_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func TestFlushPendingDSLOpsRegisters(t *testing.T) {
9191
flushPendingDSLOps()
9292

9393
// check spec has path
94-
if _, ok := sc.reflector.Spec.Paths.MapOfPathItemValues["/x"]; !ok {
94+
if _, ok := sc.doc.Paths["/x"]; !ok {
9595
t.Fatalf("expected pending op to be registered into spec")
9696
}
9797

examples/chi/docs/openapi.yaml

Lines changed: 66 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,135 @@
1-
openapi: 3.0.3
1+
openapi: 3.1.2
22
info:
33
title: Orders API (Chi)
44
version: 1.0.0
55
paths:
66
/orders:
77
get:
8+
tags:
9+
- orders
10+
summary: List all orders
11+
description: List all orders
812
parameters:
9-
- in: query
10-
name: status
11-
schema:
12-
type: string
13-
- in: query
14-
name: limit
15-
schema:
16-
type: integer
13+
- name: status
14+
in: query
15+
schema:
16+
type: string
17+
- name: limit
18+
in: query
19+
schema:
20+
type: integer
21+
format: int32
1722
responses:
18-
"200":
23+
'200':
24+
description: list of orders
1925
content:
2026
application/json:
2127
schema:
28+
type: array
2229
items:
2330
$ref: '#/components/schemas/ApiOrder'
24-
type: array
25-
description: OK
2631
security:
27-
- apiKey: []
28-
summary: List all orders
29-
tags:
30-
- orders
32+
- apiKey: []
3133
post:
34+
tags:
35+
- orders
36+
summary: Place an order
37+
description: Place an order
3238
requestBody:
3339
content:
3440
application/json:
3541
schema:
3642
$ref: '#/components/schemas/ApiCreateOrderRequest'
3743
responses:
38-
"201":
44+
'201':
45+
description: order placed
3946
content:
4047
application/json:
4148
schema:
4249
$ref: '#/components/schemas/ApiOrder'
43-
description: Created
44-
"400":
50+
'400':
51+
description: bad request
4552
content:
4653
application/json:
4754
schema:
55+
type: object
4856
properties:
4957
error:
5058
type: string
51-
type: object
52-
description: Bad Request
5359
security:
54-
- apiKey: []
55-
summary: Place an order
56-
tags:
57-
- orders
60+
- apiKey: []
5861
/orders/{id}:
59-
delete:
60-
deprecated: true
61-
parameters:
62-
- in: path
63-
name: id
64-
required: true
65-
schema:
66-
type: string
67-
responses:
68-
"204":
69-
description: No Content
70-
security:
71-
- apiKey: []
72-
summary: Cancel an order
73-
tags:
74-
- orders
7562
get:
63+
tags:
64+
- orders
65+
summary: Get order by ID
66+
description: Get order by ID
7667
parameters:
77-
- in: path
78-
name: id
79-
required: true
80-
schema:
81-
type: string
68+
- name: id
69+
in: path
70+
required: true
71+
schema:
72+
type: string
8273
responses:
83-
"200":
74+
'200':
75+
description: order found
8476
content:
8577
application/json:
8678
schema:
8779
$ref: '#/components/schemas/ApiOrder'
88-
description: OK
89-
"404":
80+
'404':
81+
description: order not found
9082
content:
9183
application/json:
9284
schema:
85+
type: object
9386
properties:
9487
error:
9588
type: string
96-
type: object
97-
description: Not Found
9889
security:
99-
- apiKey: []
100-
summary: Get order by ID
90+
- apiKey: []
91+
delete:
10192
tags:
102-
- orders
93+
- orders
94+
summary: Cancel an order
95+
description: Cancel an order
96+
parameters:
97+
- name: id
98+
in: path
99+
required: true
100+
schema:
101+
type: string
102+
responses:
103+
'204':
104+
description: order cancelled
105+
deprecated: true
106+
security:
107+
- apiKey: []
103108
components:
104109
schemas:
105110
ApiCreateOrderRequest:
111+
type: object
106112
properties:
107113
product:
108114
type: string
109115
quantity:
110116
type: integer
111-
type: object
117+
format: int32
112118
ApiOrder:
119+
type: object
113120
properties:
114121
id:
115122
type: string
116123
product:
117124
type: string
118125
quantity:
119126
type: integer
127+
format: int32
120128
total:
121-
format: double
122129
type: number
123-
type: object
130+
format: double
124131
securitySchemes:
125132
apiKey:
126-
in: header
127-
name: X-API-Key
128133
type: apiKey
134+
name: X-API-Key
135+
in: header

examples/chi/go.mod

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ require (
1414
github.com/Masterminds/semver/v3 v3.4.0 // indirect
1515
github.com/go-logr/logr v1.4.3 // indirect
1616
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
17+
github.com/goccy/go-yaml v1.19.2 // indirect
1718
github.com/google/go-cmp v0.7.0 // indirect
1819
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect
19-
github.com/swaggest/jsonschema-go v0.3.79 // indirect
20-
github.com/swaggest/openapi-go v0.2.60 // indirect
21-
github.com/swaggest/refl v1.4.0 // indirect
20+
github.com/oaswrap/spec v0.5.0 // indirect
2221
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
2322
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
2423
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
@@ -29,7 +28,6 @@ require (
2928
golang.org/x/sys v0.40.0 // indirect
3029
golang.org/x/text v0.33.0 // indirect
3130
golang.org/x/tools v0.41.0 // indirect
32-
gopkg.in/yaml.v2 v2.4.0 // indirect
3331
)
3432

3533
replace github.com/oaswrap/gswag => ../../

0 commit comments

Comments
 (0)