@pikku/openapi-parser 0.12.20 → 0.12.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/auth-config.d.ts +3 -3
- package/package.json +8 -6
- package/src/auth-config.ts +0 -94
- package/src/codegen.test.ts +0 -973
- package/src/codegen.ts +0 -1670
- package/src/index.ts +0 -24
- package/src/naming.test.ts +0 -260
- package/src/naming.ts +0 -369
- package/src/openapi-to-zod-schema.test.ts +0 -664
- package/src/openapi-to-zod-schema.ts +0 -583
- package/src/parse-openapi.test.ts +0 -316
- package/src/parse-openapi.ts +0 -467
- package/src/zod-codegen.ts +0 -9
- package/tsconfig.json +0 -12
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'assert'
|
|
2
|
-
import { describe, test } from 'node:test'
|
|
3
|
-
import { writeFile, rm, mkdtemp } from 'fs/promises'
|
|
4
|
-
import { join } from 'path'
|
|
5
|
-
import { tmpdir } from 'os'
|
|
6
|
-
import { computeContractHash, parseOpenAPISpec } from './parse-openapi.js'
|
|
7
|
-
import type { ParsedSpec } from './parse-openapi.js'
|
|
8
|
-
|
|
9
|
-
function makeSpec(overrides: Partial<ParsedSpec> = {}): ParsedSpec {
|
|
10
|
-
return {
|
|
11
|
-
info: { title: 'Test API', version: '1.0.0' },
|
|
12
|
-
baseUrl: 'https://api.example.com',
|
|
13
|
-
serverUrls: ['https://api.example.com'],
|
|
14
|
-
authType: 'bearer',
|
|
15
|
-
operations: [
|
|
16
|
-
{
|
|
17
|
-
method: 'get',
|
|
18
|
-
path: '/users/{id}',
|
|
19
|
-
tags: [],
|
|
20
|
-
pathParams: [
|
|
21
|
-
{ name: 'id', required: true, schema: { type: 'string' } },
|
|
22
|
-
],
|
|
23
|
-
queryParams: [],
|
|
24
|
-
headerParams: [],
|
|
25
|
-
responseSchema: {
|
|
26
|
-
type: 'object',
|
|
27
|
-
properties: { id: { type: 'string' }, name: { type: 'string' } },
|
|
28
|
-
},
|
|
29
|
-
errorResponses: [{ statusCode: 404, description: 'Not found' }],
|
|
30
|
-
deprecated: false,
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
componentSchemas: {
|
|
34
|
-
User: {
|
|
35
|
-
type: 'object',
|
|
36
|
-
properties: { id: { type: 'string' }, name: { type: 'string' } },
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
securitySchemes: {},
|
|
40
|
-
tagDescriptions: {},
|
|
41
|
-
...overrides,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
describe('computeContractHash', () => {
|
|
46
|
-
test('returns a 16-char hex string', () => {
|
|
47
|
-
const hash = computeContractHash(makeSpec())
|
|
48
|
-
assert.match(hash, /^[0-9a-f]{16}$/)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
test('is stable for the same spec', () => {
|
|
52
|
-
const spec = makeSpec()
|
|
53
|
-
assert.strictEqual(computeContractHash(spec), computeContractHash(spec))
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
test('changes when a path is added', () => {
|
|
57
|
-
const base = makeSpec()
|
|
58
|
-
const modified = makeSpec({
|
|
59
|
-
operations: [
|
|
60
|
-
...base.operations,
|
|
61
|
-
{
|
|
62
|
-
method: 'post',
|
|
63
|
-
path: '/users',
|
|
64
|
-
tags: [],
|
|
65
|
-
pathParams: [],
|
|
66
|
-
queryParams: [],
|
|
67
|
-
headerParams: [],
|
|
68
|
-
requestBody: {
|
|
69
|
-
type: 'object',
|
|
70
|
-
properties: { name: { type: 'string' } },
|
|
71
|
-
},
|
|
72
|
-
requestBodyRequired: true,
|
|
73
|
-
errorResponses: [],
|
|
74
|
-
deprecated: false,
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
})
|
|
78
|
-
assert.notStrictEqual(
|
|
79
|
-
computeContractHash(base),
|
|
80
|
-
computeContractHash(modified)
|
|
81
|
-
)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test('changes when a schema changes', () => {
|
|
85
|
-
const base = makeSpec()
|
|
86
|
-
const modified = makeSpec({
|
|
87
|
-
componentSchemas: {
|
|
88
|
-
User: {
|
|
89
|
-
type: 'object',
|
|
90
|
-
properties: {
|
|
91
|
-
id: { type: 'string' },
|
|
92
|
-
name: { type: 'string' },
|
|
93
|
-
email: { type: 'string' },
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
})
|
|
98
|
-
assert.notStrictEqual(
|
|
99
|
-
computeContractHash(base),
|
|
100
|
-
computeContractHash(modified)
|
|
101
|
-
)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
test('does not change when only presentation fields differ', () => {
|
|
105
|
-
const base = makeSpec()
|
|
106
|
-
const sameContract = makeSpec({
|
|
107
|
-
info: { title: 'Renamed API', version: '2.0.0', description: 'New desc' },
|
|
108
|
-
tagDescriptions: { users: 'User management' },
|
|
109
|
-
operations: base.operations.map((op) => ({
|
|
110
|
-
...op,
|
|
111
|
-
summary: 'Get a user by ID',
|
|
112
|
-
description: 'Retrieves a single user resource',
|
|
113
|
-
operationId: 'getUserById',
|
|
114
|
-
tags: ['users'],
|
|
115
|
-
})),
|
|
116
|
-
})
|
|
117
|
-
assert.strictEqual(
|
|
118
|
-
computeContractHash(base),
|
|
119
|
-
computeContractHash(sameContract)
|
|
120
|
-
)
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
test('changes when server URLs change', () => {
|
|
124
|
-
const base = makeSpec()
|
|
125
|
-
const modified = makeSpec({
|
|
126
|
-
serverUrls: ['https://api-v2.example.com'],
|
|
127
|
-
})
|
|
128
|
-
assert.notStrictEqual(
|
|
129
|
-
computeContractHash(base),
|
|
130
|
-
computeContractHash(modified)
|
|
131
|
-
)
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
test('changes when security schemes change', () => {
|
|
135
|
-
const base = makeSpec()
|
|
136
|
-
const modified = makeSpec({
|
|
137
|
-
securitySchemes: {
|
|
138
|
-
oauth2: {
|
|
139
|
-
type: 'oauth2',
|
|
140
|
-
flows: {
|
|
141
|
-
authorizationUrl: 'https://auth.example.com/authorize',
|
|
142
|
-
tokenUrl: 'https://auth.example.com/token',
|
|
143
|
-
scopes: { read: 'Read access' },
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
})
|
|
148
|
-
assert.notStrictEqual(
|
|
149
|
-
computeContractHash(base),
|
|
150
|
-
computeContractHash(modified)
|
|
151
|
-
)
|
|
152
|
-
})
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
describe('parseOpenAPISpec — Swagger 2.0', () => {
|
|
156
|
-
const swagger2Spec = {
|
|
157
|
-
swagger: '2.0',
|
|
158
|
-
info: { title: 'Test API', version: '1.0.0' },
|
|
159
|
-
host: 'api.example.com',
|
|
160
|
-
basePath: '/v1',
|
|
161
|
-
schemes: ['https'],
|
|
162
|
-
definitions: {
|
|
163
|
-
Pet: {
|
|
164
|
-
type: 'object',
|
|
165
|
-
required: ['name'],
|
|
166
|
-
properties: {
|
|
167
|
-
id: { type: 'integer', format: 'int64' },
|
|
168
|
-
name: { type: 'string' },
|
|
169
|
-
status: {
|
|
170
|
-
type: 'string',
|
|
171
|
-
enum: ['available', 'sold'],
|
|
172
|
-
description: 'pet status',
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
securityDefinitions: {
|
|
178
|
-
api_key: { type: 'apiKey', name: 'api_key', in: 'header' },
|
|
179
|
-
},
|
|
180
|
-
paths: {
|
|
181
|
-
'/pets': {
|
|
182
|
-
get: {
|
|
183
|
-
operationId: 'listPets',
|
|
184
|
-
summary: 'List all pets',
|
|
185
|
-
parameters: [
|
|
186
|
-
{
|
|
187
|
-
in: 'query',
|
|
188
|
-
name: 'status',
|
|
189
|
-
type: 'string',
|
|
190
|
-
description: 'Filter by status',
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
responses: {
|
|
194
|
-
'200': {
|
|
195
|
-
description: 'A list of pets',
|
|
196
|
-
schema: { type: 'array', items: { $ref: '#/definitions/Pet' } },
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
post: {
|
|
201
|
-
operationId: 'createPet',
|
|
202
|
-
summary: 'Create a pet',
|
|
203
|
-
parameters: [
|
|
204
|
-
{
|
|
205
|
-
in: 'body',
|
|
206
|
-
name: 'body',
|
|
207
|
-
description: 'Pet to create',
|
|
208
|
-
required: true,
|
|
209
|
-
schema: { $ref: '#/definitions/Pet' },
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
responses: {
|
|
213
|
-
'201': {
|
|
214
|
-
description: 'Created',
|
|
215
|
-
schema: { $ref: '#/definitions/Pet' },
|
|
216
|
-
},
|
|
217
|
-
'400': { description: 'Bad request' },
|
|
218
|
-
},
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
'/pets/{petId}': {
|
|
222
|
-
get: {
|
|
223
|
-
operationId: 'getPet',
|
|
224
|
-
parameters: [
|
|
225
|
-
{ in: 'path', name: 'petId', type: 'integer', required: true },
|
|
226
|
-
],
|
|
227
|
-
responses: {
|
|
228
|
-
'200': {
|
|
229
|
-
description: 'A pet',
|
|
230
|
-
schema: { $ref: '#/definitions/Pet' },
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
let tmpDir: string
|
|
239
|
-
let specPath: string
|
|
240
|
-
|
|
241
|
-
test('setup', async () => {
|
|
242
|
-
tmpDir = await mkdtemp(join(tmpdir(), 'openapi-test-'))
|
|
243
|
-
specPath = join(tmpDir, 'spec.json')
|
|
244
|
-
await writeFile(specPath, JSON.stringify(swagger2Spec))
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
test('extracts component schemas from definitions', async () => {
|
|
248
|
-
const spec = await parseOpenAPISpec(specPath)
|
|
249
|
-
assert.ok(spec.componentSchemas.Pet)
|
|
250
|
-
assert.equal(spec.componentSchemas.Pet.type, 'object')
|
|
251
|
-
assert.ok(spec.componentSchemas.Pet.properties?.name)
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
test('extracts server URLs from host/basePath/schemes', async () => {
|
|
255
|
-
const spec = await parseOpenAPISpec(specPath)
|
|
256
|
-
assert.deepEqual(spec.serverUrls, ['https://api.example.com/v1'])
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
test('detects apiKey auth from securityDefinitions', async () => {
|
|
260
|
-
const spec = await parseOpenAPISpec(specPath)
|
|
261
|
-
assert.equal(spec.authType, 'apiKey')
|
|
262
|
-
})
|
|
263
|
-
|
|
264
|
-
test('extracts requestBody from body parameter', async () => {
|
|
265
|
-
const spec = await parseOpenAPISpec(specPath)
|
|
266
|
-
const createPet = spec.operations.find(
|
|
267
|
-
(op) => op.operationId === 'createPet'
|
|
268
|
-
)
|
|
269
|
-
assert.ok(createPet?.requestBody)
|
|
270
|
-
assert.equal(createPet.requestBody.type, 'object')
|
|
271
|
-
assert.ok(createPet.requestBody.properties?.name)
|
|
272
|
-
assert.equal(createPet.requestBodyRequired, true)
|
|
273
|
-
assert.equal(createPet.requestBodyDescription, 'Pet to create')
|
|
274
|
-
})
|
|
275
|
-
|
|
276
|
-
test('extracts responseSchema from Swagger 2.0 response', async () => {
|
|
277
|
-
const spec = await parseOpenAPISpec(specPath)
|
|
278
|
-
const createPet = spec.operations.find(
|
|
279
|
-
(op) => op.operationId === 'createPet'
|
|
280
|
-
)
|
|
281
|
-
assert.ok(createPet?.responseSchema)
|
|
282
|
-
assert.equal(createPet.responseSchema.type, 'object')
|
|
283
|
-
|
|
284
|
-
const listPets = spec.operations.find((op) => op.operationId === 'listPets')
|
|
285
|
-
assert.ok(listPets?.responseSchema)
|
|
286
|
-
assert.equal(listPets.responseSchema.type, 'array')
|
|
287
|
-
})
|
|
288
|
-
|
|
289
|
-
test('extracts query params', async () => {
|
|
290
|
-
const spec = await parseOpenAPISpec(specPath)
|
|
291
|
-
const listPets = spec.operations.find((op) => op.operationId === 'listPets')
|
|
292
|
-
assert.equal(listPets?.queryParams.length, 1)
|
|
293
|
-
assert.equal(listPets?.queryParams[0].name, 'status')
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
test('extracts path params', async () => {
|
|
297
|
-
const spec = await parseOpenAPISpec(specPath)
|
|
298
|
-
const getPet = spec.operations.find((op) => op.operationId === 'getPet')
|
|
299
|
-
assert.equal(getPet?.pathParams.length, 1)
|
|
300
|
-
assert.equal(getPet?.pathParams[0].name, 'petId')
|
|
301
|
-
assert.equal(getPet?.pathParams[0].required, true)
|
|
302
|
-
})
|
|
303
|
-
|
|
304
|
-
test('extracts error responses', async () => {
|
|
305
|
-
const spec = await parseOpenAPISpec(specPath)
|
|
306
|
-
const createPet = spec.operations.find(
|
|
307
|
-
(op) => op.operationId === 'createPet'
|
|
308
|
-
)
|
|
309
|
-
assert.equal(createPet?.errorResponses.length, 1)
|
|
310
|
-
assert.equal(createPet?.errorResponses[0].statusCode, 400)
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
test('cleanup', async () => {
|
|
314
|
-
await rm(tmpDir, { recursive: true, force: true })
|
|
315
|
-
})
|
|
316
|
-
})
|