adorn-api 1.1.7 → 1.1.9
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.
|
@@ -136,8 +136,20 @@ function coerceArrayValue(value, schema, mode) {
|
|
|
136
136
|
if (value === undefined || value === null) {
|
|
137
137
|
return { value, ok: true, changed: false };
|
|
138
138
|
}
|
|
139
|
-
|
|
140
|
-
let changed
|
|
139
|
+
let input;
|
|
140
|
+
let changed;
|
|
141
|
+
if (Array.isArray(value)) {
|
|
142
|
+
input = value;
|
|
143
|
+
changed = false;
|
|
144
|
+
}
|
|
145
|
+
else if (typeof value === "string" && value.includes(",")) {
|
|
146
|
+
input = value.split(",").map((s) => s.trim());
|
|
147
|
+
changed = true;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
input = [value];
|
|
151
|
+
changed = true;
|
|
152
|
+
}
|
|
141
153
|
let ok = true;
|
|
142
154
|
const output = input.map((entry) => {
|
|
143
155
|
const result = coerceValue(entry, schema.items, mode);
|
package/dist/core/openapi.js
CHANGED
|
@@ -216,13 +216,29 @@ function buildParameters(location, input, context) {
|
|
|
216
216
|
if (!fieldEntries.length) {
|
|
217
217
|
return [];
|
|
218
218
|
}
|
|
219
|
-
return fieldEntries.map((entry) =>
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
219
|
+
return fieldEntries.map((entry) => {
|
|
220
|
+
const param = {
|
|
221
|
+
name: entry.name,
|
|
222
|
+
in: location,
|
|
223
|
+
required: location === "path" ? true : entry.required,
|
|
224
|
+
description: entry.description,
|
|
225
|
+
schema: (0, schema_builder_1.buildSchemaFromSource)(entry.schema, context)
|
|
226
|
+
};
|
|
227
|
+
if (location === "query" && isSchemaNode(entry.schema)) {
|
|
228
|
+
if (entry.schema.kind === "array") {
|
|
229
|
+
param.style = "form";
|
|
230
|
+
param.explode = true;
|
|
231
|
+
}
|
|
232
|
+
else if (entry.schema.kind === "object") {
|
|
233
|
+
param.style = "deepObject";
|
|
234
|
+
param.explode = true;
|
|
235
|
+
}
|
|
236
|
+
if (entry.schema.examples && entry.schema.examples.length > 0) {
|
|
237
|
+
param.example = entry.schema.examples[0];
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return param;
|
|
241
|
+
});
|
|
226
242
|
}
|
|
227
243
|
function extractFields(schema) {
|
|
228
244
|
if (isSchemaNode(schema)) {
|
package/package.json
CHANGED
|
@@ -192,8 +192,18 @@ function coerceArrayValue(
|
|
|
192
192
|
if (value === undefined || value === null) {
|
|
193
193
|
return { value, ok: true, changed: false };
|
|
194
194
|
}
|
|
195
|
-
|
|
196
|
-
let changed
|
|
195
|
+
let input: unknown[];
|
|
196
|
+
let changed: boolean;
|
|
197
|
+
if (Array.isArray(value)) {
|
|
198
|
+
input = value;
|
|
199
|
+
changed = false;
|
|
200
|
+
} else if (typeof value === "string" && value.includes(",")) {
|
|
201
|
+
input = value.split(",").map((s) => s.trim());
|
|
202
|
+
changed = true;
|
|
203
|
+
} else {
|
|
204
|
+
input = [value];
|
|
205
|
+
changed = true;
|
|
206
|
+
}
|
|
197
207
|
let ok = true;
|
|
198
208
|
const output = input.map((entry) => {
|
|
199
209
|
const result = coerceValue(entry, schema.items, mode);
|
package/src/core/openapi.ts
CHANGED
|
@@ -317,13 +317,31 @@ function buildParameters(
|
|
|
317
317
|
if (!fieldEntries.length) {
|
|
318
318
|
return [];
|
|
319
319
|
}
|
|
320
|
-
return fieldEntries.map((entry) =>
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
320
|
+
return fieldEntries.map((entry) => {
|
|
321
|
+
const param: Record<string, unknown> = {
|
|
322
|
+
name: entry.name,
|
|
323
|
+
in: location,
|
|
324
|
+
required: location === "path" ? true : entry.required,
|
|
325
|
+
description: entry.description,
|
|
326
|
+
schema: buildSchemaFromSource(entry.schema, context)
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
if (location === "query" && isSchemaNode(entry.schema)) {
|
|
330
|
+
if (entry.schema.kind === "array") {
|
|
331
|
+
param.style = "form";
|
|
332
|
+
param.explode = true;
|
|
333
|
+
} else if (entry.schema.kind === "object") {
|
|
334
|
+
param.style = "deepObject";
|
|
335
|
+
param.explode = true;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (entry.schema.examples && entry.schema.examples.length > 0) {
|
|
339
|
+
param.example = entry.schema.examples[0];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return param;
|
|
344
|
+
});
|
|
327
345
|
}
|
|
328
346
|
|
|
329
347
|
function extractFields(
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach } from "vitest";
|
|
2
|
+
import { t } from "../../src/core/schema";
|
|
3
|
+
import { registerController, registerDto } from "../../src/core/metadata";
|
|
4
|
+
import { buildOpenApi } from "../../src/core/openapi";
|
|
5
|
+
import { createInputCoercer } from "../../src/adapter/express/coercion";
|
|
6
|
+
|
|
7
|
+
describe("OpenAPI query parameter serialization", () => {
|
|
8
|
+
class QueryArrayController {}
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
registerController({
|
|
12
|
+
basePath: "/items",
|
|
13
|
+
controller: QueryArrayController,
|
|
14
|
+
routes: [
|
|
15
|
+
{
|
|
16
|
+
httpMethod: "get",
|
|
17
|
+
path: "/",
|
|
18
|
+
handlerName: "list",
|
|
19
|
+
query: {
|
|
20
|
+
schema: {
|
|
21
|
+
kind: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
ids: t.array(t.string()),
|
|
24
|
+
nums: t.array(t.integer()),
|
|
25
|
+
tags: t.array(t.string(), { examples: [["a", "b"]] })
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
responses: [{ status: 200 }]
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("query array<string> generates style=form + explode=true", () => {
|
|
36
|
+
const doc = buildOpenApi({
|
|
37
|
+
info: { title: "test", version: "1.0.0" },
|
|
38
|
+
controllers: [QueryArrayController]
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const params = (doc.paths["/items"] as any).get.parameters as any[];
|
|
42
|
+
const idsParam = params.find((p: any) => p.name === "ids");
|
|
43
|
+
|
|
44
|
+
expect(idsParam).toBeDefined();
|
|
45
|
+
expect(idsParam.style).toBe("form");
|
|
46
|
+
expect(idsParam.explode).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("query array<integer> generates style=form + explode=true", () => {
|
|
50
|
+
const doc = buildOpenApi({
|
|
51
|
+
info: { title: "test", version: "1.0.0" },
|
|
52
|
+
controllers: [QueryArrayController]
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const params = (doc.paths["/items"] as any).get.parameters as any[];
|
|
56
|
+
const numsParam = params.find((p: any) => p.name === "nums");
|
|
57
|
+
|
|
58
|
+
expect(numsParam).toBeDefined();
|
|
59
|
+
expect(numsParam.style).toBe("form");
|
|
60
|
+
expect(numsParam.explode).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("projects example from schema.examples to parameter.example", () => {
|
|
64
|
+
const doc = buildOpenApi({
|
|
65
|
+
info: { title: "test", version: "1.0.0" },
|
|
66
|
+
controllers: [QueryArrayController]
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const params = (doc.paths["/items"] as any).get.parameters as any[];
|
|
70
|
+
const tagsParam = params.find((p: any) => p.name === "tags");
|
|
71
|
+
|
|
72
|
+
expect(tagsParam).toBeDefined();
|
|
73
|
+
expect(tagsParam.example).toEqual(["a", "b"]);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("Query array coercion – CSV support", () => {
|
|
78
|
+
it("?ids=1&ids=2 -> [1,2] via repeated keys", () => {
|
|
79
|
+
const coerce = createInputCoercer(
|
|
80
|
+
{ schema: { kind: "object", properties: { ids: t.array(t.integer()) } } },
|
|
81
|
+
{ mode: "safe", location: "query" }
|
|
82
|
+
)!;
|
|
83
|
+
|
|
84
|
+
const result = coerce({ ids: ["1", "2"] });
|
|
85
|
+
expect(result.ids).toEqual([1, 2]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("?ids=1,2 -> [1,2] via CSV string", () => {
|
|
89
|
+
const coerce = createInputCoercer(
|
|
90
|
+
{ schema: { kind: "object", properties: { ids: t.array(t.integer()) } } },
|
|
91
|
+
{ mode: "safe", location: "query" }
|
|
92
|
+
)!;
|
|
93
|
+
|
|
94
|
+
const result = coerce({ ids: "1,2" });
|
|
95
|
+
expect(result.ids).toEqual([1, 2]);
|
|
96
|
+
});
|
|
97
|
+
});
|