rads-db 3.2.23 → 3.2.24
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/integrations/getOpenApiSpec.cjs +227 -44
- package/integrations/getOpenApiSpec.mjs +145 -29
- package/package.json +1 -1
|
@@ -94,14 +94,174 @@ function buildObjectSchema(entity, schema, allOptional = false) {
|
|
|
94
94
|
} : {})
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
const stringOps = name => {
|
|
98
|
+
const s = {
|
|
99
|
+
type: "string"
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
[`${name}_ieq`]: s,
|
|
103
|
+
[`${name}_istartsWith`]: s,
|
|
104
|
+
[`${name}_startsWith`]: s,
|
|
105
|
+
[`${name}_icontains`]: s,
|
|
106
|
+
[`${name}_contains`]: s,
|
|
107
|
+
[`${name}_iendsWith`]: s,
|
|
108
|
+
[`${name}_endsWith`]: s,
|
|
109
|
+
[`${name}_gt`]: s,
|
|
110
|
+
[`${name}_gte`]: s,
|
|
111
|
+
[`${name}_lt`]: s,
|
|
112
|
+
[`${name}_lte`]: s
|
|
113
|
+
};
|
|
100
114
|
};
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
const numberOps = name => {
|
|
116
|
+
const n = {
|
|
117
|
+
type: "number"
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
[`${name}_gt`]: n,
|
|
121
|
+
[`${name}_gte`]: n,
|
|
122
|
+
[`${name}_lt`]: n,
|
|
123
|
+
[`${name}_lte`]: n
|
|
124
|
+
};
|
|
104
125
|
};
|
|
126
|
+
const relationWhereOas = () => ({
|
|
127
|
+
type: "object",
|
|
128
|
+
properties: {
|
|
129
|
+
id: {
|
|
130
|
+
type: "string"
|
|
131
|
+
},
|
|
132
|
+
id_in: {
|
|
133
|
+
type: "array",
|
|
134
|
+
items: {
|
|
135
|
+
type: "string"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
function addPrimitiveWhereProps(props, name, type) {
|
|
141
|
+
props[name] = primitiveToOas(type);
|
|
142
|
+
if (type !== "boolean") props[`${name}_in`] = {
|
|
143
|
+
type: "array",
|
|
144
|
+
items: primitiveToOas(type)
|
|
145
|
+
};
|
|
146
|
+
if (type === "string") Object.assign(props, stringOps(name));else if (type === "number") Object.assign(props, numberOps(name));else if (type === "Record<string, any>") props[`${name}_jsonContains`] = {
|
|
147
|
+
$ref: "#/components/schemas/WhereJsonContains"
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function buildWhereProps(typeDef, schema) {
|
|
151
|
+
const props = {};
|
|
152
|
+
for (const [, field] of Object.entries(typeDef.fields ?? {})) {
|
|
153
|
+
if (field.isInverseRelation) continue;
|
|
154
|
+
const {
|
|
155
|
+
name,
|
|
156
|
+
type,
|
|
157
|
+
isRelation,
|
|
158
|
+
isRequired,
|
|
159
|
+
isArray
|
|
160
|
+
} = field;
|
|
161
|
+
const entry = schema[type];
|
|
162
|
+
const isPrimitive = !entry;
|
|
163
|
+
const isEnum = !!entry?.enumValues;
|
|
164
|
+
const isObject = !!entry?.fields;
|
|
165
|
+
const optional = !isRequired;
|
|
166
|
+
const enumSchema = isEnum ? {
|
|
167
|
+
type: "string",
|
|
168
|
+
enum: Object.keys(entry.enumValues)
|
|
169
|
+
} : {};
|
|
170
|
+
if (isArray) {
|
|
171
|
+
if (isPrimitive || isEnum) props[`${name}_arrayContains`] = isEnum ? enumSchema : primitiveToOas(type);else if (isRelation) props[`${name}_some`] = relationWhereOas();else if (isObject) props[`${name}_some`] = {
|
|
172
|
+
$ref: `#/components/schemas/${type}Where`
|
|
173
|
+
};
|
|
174
|
+
props[`${name}_isEmpty`] = {
|
|
175
|
+
type: "boolean"
|
|
176
|
+
};
|
|
177
|
+
if (optional) props[`${name}_isNull`] = {
|
|
178
|
+
type: "boolean"
|
|
179
|
+
};
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (isRelation) {
|
|
183
|
+
props[name] = relationWhereOas();
|
|
184
|
+
} else if (isObject) {
|
|
185
|
+
props[name] = {
|
|
186
|
+
$ref: `#/components/schemas/${type}Where`
|
|
187
|
+
};
|
|
188
|
+
} else if (isEnum) {
|
|
189
|
+
props[name] = enumSchema;
|
|
190
|
+
props[`${name}_in`] = {
|
|
191
|
+
type: "array",
|
|
192
|
+
items: enumSchema
|
|
193
|
+
};
|
|
194
|
+
Object.assign(props, stringOps(name));
|
|
195
|
+
} else {
|
|
196
|
+
addPrimitiveWhereProps(props, name, type);
|
|
197
|
+
}
|
|
198
|
+
if (optional) props[`${name}_isNull`] = {
|
|
199
|
+
type: "boolean"
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return props;
|
|
203
|
+
}
|
|
204
|
+
function buildWhereSchema(typeName, typeDef, schema) {
|
|
205
|
+
const selfRef = `#/components/schemas/${typeName}Where`;
|
|
206
|
+
return {
|
|
207
|
+
type: "object",
|
|
208
|
+
properties: {
|
|
209
|
+
_not: {
|
|
210
|
+
$ref: selfRef
|
|
211
|
+
},
|
|
212
|
+
_and: {
|
|
213
|
+
type: "array",
|
|
214
|
+
items: {
|
|
215
|
+
$ref: selfRef
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
_or: {
|
|
219
|
+
type: "array",
|
|
220
|
+
items: {
|
|
221
|
+
$ref: selfRef
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
...buildWhereProps(typeDef, schema)
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function buildIncludeSchema(typeDef, schema) {
|
|
229
|
+
const primitiveFields = [];
|
|
230
|
+
const includeProps = {};
|
|
231
|
+
for (const [, field] of Object.entries(typeDef.fields ?? {})) {
|
|
232
|
+
if (field.isInverseRelation) continue;
|
|
233
|
+
const entry = schema[field.type];
|
|
234
|
+
const canSubInclude = field.isRelation || entry?.fields && !entry.enumValues;
|
|
235
|
+
if (canSubInclude) {
|
|
236
|
+
includeProps[field.name] = {
|
|
237
|
+
$ref: `#/components/schemas/${field.type}Include`
|
|
238
|
+
};
|
|
239
|
+
} else {
|
|
240
|
+
primitiveFields.push(field.name);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
type: "object",
|
|
245
|
+
properties: {
|
|
246
|
+
_pick: {
|
|
247
|
+
type: "array",
|
|
248
|
+
items: {
|
|
249
|
+
type: "string",
|
|
250
|
+
enum: primitiveFields
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
...includeProps
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function buildAggEnum(typeDef) {
|
|
258
|
+
const values = ["_count"];
|
|
259
|
+
for (const [, field] of Object.entries(typeDef.fields ?? {})) {
|
|
260
|
+
if (field.isInverseRelation || field.isArray || field.isRelation) continue;
|
|
261
|
+
if (field.type === "number") values.push(`${field.name}_min`, `${field.name}_max`, `${field.name}_sum`);
|
|
262
|
+
}
|
|
263
|
+
return values;
|
|
264
|
+
}
|
|
105
265
|
const paginationProps = {
|
|
106
266
|
cursor: {
|
|
107
267
|
type: "string"
|
|
@@ -121,36 +281,6 @@ const paginationProps = {
|
|
|
121
281
|
description: "Multiple order-by expressions"
|
|
122
282
|
}
|
|
123
283
|
};
|
|
124
|
-
const getRequestSchema = {
|
|
125
|
-
type: "object",
|
|
126
|
-
properties: {
|
|
127
|
-
where: whereSchema,
|
|
128
|
-
include: includeSchema
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
const getManyRequestSchema = {
|
|
132
|
-
type: "object",
|
|
133
|
-
properties: {
|
|
134
|
-
where: whereSchema,
|
|
135
|
-
include: includeSchema,
|
|
136
|
-
...paginationProps
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
const getAggRequestSchema = {
|
|
140
|
-
type: "object",
|
|
141
|
-
required: ["agg"],
|
|
142
|
-
properties: {
|
|
143
|
-
where: whereSchema,
|
|
144
|
-
agg: {
|
|
145
|
-
type: "array",
|
|
146
|
-
items: {
|
|
147
|
-
type: "string"
|
|
148
|
-
},
|
|
149
|
-
description: 'Aggregation operations: "_count", "field_min", "field_max", "field_sum"'
|
|
150
|
-
},
|
|
151
|
-
...paginationProps
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
284
|
function jsonBody(s) {
|
|
155
285
|
return {
|
|
156
286
|
required: true,
|
|
@@ -182,6 +312,22 @@ function getOpenApiSpec(options, openApiOptions) {
|
|
|
182
312
|
const base = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
183
313
|
const paths = {};
|
|
184
314
|
const schemas = {};
|
|
315
|
+
schemas.WhereJsonContains = {
|
|
316
|
+
type: "object",
|
|
317
|
+
required: ["path"],
|
|
318
|
+
properties: {
|
|
319
|
+
path: {
|
|
320
|
+
type: "string",
|
|
321
|
+
description: 'JSON path, e.g. "meta.countries[0].name"'
|
|
322
|
+
},
|
|
323
|
+
isNull: {
|
|
324
|
+
type: "boolean"
|
|
325
|
+
},
|
|
326
|
+
value: {
|
|
327
|
+
description: "Expected value at path (string, number, or boolean)"
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
};
|
|
185
331
|
paths[`${base}/me`] = {
|
|
186
332
|
get: {
|
|
187
333
|
summary: "Get current user",
|
|
@@ -260,17 +406,52 @@ function getOpenApiSpec(options, openApiOptions) {
|
|
|
260
406
|
handlePlural
|
|
261
407
|
} = entity;
|
|
262
408
|
const tag = name;
|
|
263
|
-
schemas[name] = buildObjectSchema(entity, schema);
|
|
264
|
-
schemas[`${name}Put`] = buildObjectSchema(entity, schema, true);
|
|
265
|
-
schemas[`${name}Put`].required = ["id"];
|
|
266
409
|
const ref = n => ({
|
|
267
410
|
$ref: `#/components/schemas/${n}`
|
|
268
411
|
});
|
|
412
|
+
schemas[name] = buildObjectSchema(entity, schema);
|
|
413
|
+
schemas[`${name}Put`] = buildObjectSchema(entity, schema, true);
|
|
414
|
+
schemas[`${name}Put`].required = ["id"];
|
|
415
|
+
schemas[`${name}Where`] = buildWhereSchema(name, entity, schema);
|
|
416
|
+
schemas[`${name}Include`] = buildIncludeSchema(entity, schema);
|
|
417
|
+
const aggValues = buildAggEnum(entity);
|
|
418
|
+
const aggSchema = {
|
|
419
|
+
type: "array",
|
|
420
|
+
items: {
|
|
421
|
+
type: "string",
|
|
422
|
+
enum: aggValues
|
|
423
|
+
},
|
|
424
|
+
description: "Aggregation operations to compute"
|
|
425
|
+
};
|
|
426
|
+
const getReqBody = {
|
|
427
|
+
type: "object",
|
|
428
|
+
properties: {
|
|
429
|
+
where: ref(`${name}Where`),
|
|
430
|
+
include: ref(`${name}Include`)
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
const getManyReqBody = {
|
|
434
|
+
type: "object",
|
|
435
|
+
properties: {
|
|
436
|
+
where: ref(`${name}Where`),
|
|
437
|
+
include: ref(`${name}Include`),
|
|
438
|
+
...paginationProps
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
const getAggReqBody = {
|
|
442
|
+
type: "object",
|
|
443
|
+
required: ["agg"],
|
|
444
|
+
properties: {
|
|
445
|
+
where: ref(`${name}Where`),
|
|
446
|
+
agg: aggSchema,
|
|
447
|
+
...paginationProps
|
|
448
|
+
}
|
|
449
|
+
};
|
|
269
450
|
paths[`${base}/${handle}`] = {
|
|
270
451
|
post: {
|
|
271
452
|
summary: `Get ${name}`,
|
|
272
453
|
tags: [tag],
|
|
273
|
-
requestBody: jsonBody(
|
|
454
|
+
requestBody: jsonBody(getReqBody),
|
|
274
455
|
responses: jsonOk(ref(name), `${name} object`)
|
|
275
456
|
},
|
|
276
457
|
put: {
|
|
@@ -290,7 +471,7 @@ function getOpenApiSpec(options, openApiOptions) {
|
|
|
290
471
|
post: {
|
|
291
472
|
summary: `Get many ${name} objects`,
|
|
292
473
|
tags: [tag],
|
|
293
|
-
requestBody: jsonBody(
|
|
474
|
+
requestBody: jsonBody(getManyReqBody),
|
|
294
475
|
responses: jsonOk({
|
|
295
476
|
type: "object",
|
|
296
477
|
required: ["nodes"],
|
|
@@ -329,7 +510,7 @@ function getOpenApiSpec(options, openApiOptions) {
|
|
|
329
510
|
post: {
|
|
330
511
|
summary: `Aggregate ${name} data`,
|
|
331
512
|
tags: [tag],
|
|
332
|
-
requestBody: jsonBody(
|
|
513
|
+
requestBody: jsonBody(getAggReqBody),
|
|
333
514
|
responses: jsonOk({
|
|
334
515
|
type: "object",
|
|
335
516
|
additionalProperties: {
|
|
@@ -342,7 +523,7 @@ function getOpenApiSpec(options, openApiOptions) {
|
|
|
342
523
|
}
|
|
343
524
|
for (const key in schema) {
|
|
344
525
|
const entry = schema[key];
|
|
345
|
-
if (entry.decorators?.entity
|
|
526
|
+
if (entry.decorators?.entity) continue;
|
|
346
527
|
if (entry.enumValues) {
|
|
347
528
|
schemas[key] = {
|
|
348
529
|
type: "string",
|
|
@@ -352,7 +533,9 @@ function getOpenApiSpec(options, openApiOptions) {
|
|
|
352
533
|
} : {})
|
|
353
534
|
};
|
|
354
535
|
} else if (entry.fields) {
|
|
355
|
-
schemas[key] = buildObjectSchema(entry, schema);
|
|
536
|
+
if (!schemas[key]) schemas[key] = buildObjectSchema(entry, schema);
|
|
537
|
+
schemas[`${key}Where`] = buildWhereSchema(key, entry, schema);
|
|
538
|
+
schemas[`${key}Include`] = buildIncludeSchema(entry, schema);
|
|
356
539
|
}
|
|
357
540
|
}
|
|
358
541
|
return {
|
|
@@ -52,35 +52,119 @@ function buildObjectSchema(entity, schema, allOptional = false) {
|
|
|
52
52
|
...required.length > 0 ? { required } : {}
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
-
const
|
|
56
|
-
const
|
|
55
|
+
const stringOps = (name) => {
|
|
56
|
+
const s = { type: "string" };
|
|
57
|
+
return {
|
|
58
|
+
[`${name}_ieq`]: s,
|
|
59
|
+
[`${name}_istartsWith`]: s,
|
|
60
|
+
[`${name}_startsWith`]: s,
|
|
61
|
+
[`${name}_icontains`]: s,
|
|
62
|
+
[`${name}_contains`]: s,
|
|
63
|
+
[`${name}_iendsWith`]: s,
|
|
64
|
+
[`${name}_endsWith`]: s,
|
|
65
|
+
[`${name}_gt`]: s,
|
|
66
|
+
[`${name}_gte`]: s,
|
|
67
|
+
[`${name}_lt`]: s,
|
|
68
|
+
[`${name}_lte`]: s
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
const numberOps = (name) => {
|
|
72
|
+
const n = { type: "number" };
|
|
73
|
+
return { [`${name}_gt`]: n, [`${name}_gte`]: n, [`${name}_lt`]: n, [`${name}_lte`]: n };
|
|
74
|
+
};
|
|
75
|
+
const relationWhereOas = () => ({
|
|
76
|
+
type: "object",
|
|
77
|
+
properties: { id: { type: "string" }, id_in: { type: "array", items: { type: "string" } } }
|
|
78
|
+
});
|
|
79
|
+
function addPrimitiveWhereProps(props, name, type) {
|
|
80
|
+
props[name] = primitiveToOas(type);
|
|
81
|
+
if (type !== "boolean") props[`${name}_in`] = { type: "array", items: primitiveToOas(type) };
|
|
82
|
+
if (type === "string") Object.assign(props, stringOps(name));
|
|
83
|
+
else if (type === "number") Object.assign(props, numberOps(name));
|
|
84
|
+
else if (type === "Record<string, any>")
|
|
85
|
+
props[`${name}_jsonContains`] = { $ref: "#/components/schemas/WhereJsonContains" };
|
|
86
|
+
}
|
|
87
|
+
function buildWhereProps(typeDef, schema) {
|
|
88
|
+
const props = {};
|
|
89
|
+
for (const [, field] of Object.entries(typeDef.fields ?? {})) {
|
|
90
|
+
if (field.isInverseRelation) continue;
|
|
91
|
+
const { name, type, isRelation, isRequired, isArray } = field;
|
|
92
|
+
const entry = schema[type];
|
|
93
|
+
const isPrimitive = !entry;
|
|
94
|
+
const isEnum = !!entry?.enumValues;
|
|
95
|
+
const isObject = !!entry?.fields;
|
|
96
|
+
const optional = !isRequired;
|
|
97
|
+
const enumSchema = isEnum ? { type: "string", enum: Object.keys(entry.enumValues) } : {};
|
|
98
|
+
if (isArray) {
|
|
99
|
+
if (isPrimitive || isEnum) props[`${name}_arrayContains`] = isEnum ? enumSchema : primitiveToOas(type);
|
|
100
|
+
else if (isRelation) props[`${name}_some`] = relationWhereOas();
|
|
101
|
+
else if (isObject) props[`${name}_some`] = { $ref: `#/components/schemas/${type}Where` };
|
|
102
|
+
props[`${name}_isEmpty`] = { type: "boolean" };
|
|
103
|
+
if (optional) props[`${name}_isNull`] = { type: "boolean" };
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (isRelation) {
|
|
107
|
+
props[name] = relationWhereOas();
|
|
108
|
+
} else if (isObject) {
|
|
109
|
+
props[name] = { $ref: `#/components/schemas/${type}Where` };
|
|
110
|
+
} else if (isEnum) {
|
|
111
|
+
props[name] = enumSchema;
|
|
112
|
+
props[`${name}_in`] = { type: "array", items: enumSchema };
|
|
113
|
+
Object.assign(props, stringOps(name));
|
|
114
|
+
} else {
|
|
115
|
+
addPrimitiveWhereProps(props, name, type);
|
|
116
|
+
}
|
|
117
|
+
if (optional) props[`${name}_isNull`] = { type: "boolean" };
|
|
118
|
+
}
|
|
119
|
+
return props;
|
|
120
|
+
}
|
|
121
|
+
function buildWhereSchema(typeName, typeDef, schema) {
|
|
122
|
+
const selfRef = `#/components/schemas/${typeName}Where`;
|
|
123
|
+
return {
|
|
124
|
+
type: "object",
|
|
125
|
+
properties: {
|
|
126
|
+
_not: { $ref: selfRef },
|
|
127
|
+
_and: { type: "array", items: { $ref: selfRef } },
|
|
128
|
+
_or: { type: "array", items: { $ref: selfRef } },
|
|
129
|
+
...buildWhereProps(typeDef, schema)
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function buildIncludeSchema(typeDef, schema) {
|
|
134
|
+
const primitiveFields = [];
|
|
135
|
+
const includeProps = {};
|
|
136
|
+
for (const [, field] of Object.entries(typeDef.fields ?? {})) {
|
|
137
|
+
if (field.isInverseRelation) continue;
|
|
138
|
+
const entry = schema[field.type];
|
|
139
|
+
const canSubInclude = field.isRelation || entry?.fields && !entry.enumValues;
|
|
140
|
+
if (canSubInclude) {
|
|
141
|
+
includeProps[field.name] = { $ref: `#/components/schemas/${field.type}Include` };
|
|
142
|
+
} else {
|
|
143
|
+
primitiveFields.push(field.name);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
_pick: { type: "array", items: { type: "string", enum: primitiveFields } },
|
|
150
|
+
...includeProps
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function buildAggEnum(typeDef) {
|
|
155
|
+
const values = ["_count"];
|
|
156
|
+
for (const [, field] of Object.entries(typeDef.fields ?? {})) {
|
|
157
|
+
if (field.isInverseRelation || field.isArray || field.isRelation) continue;
|
|
158
|
+
if (field.type === "number") values.push(`${field.name}_min`, `${field.name}_max`, `${field.name}_sum`);
|
|
159
|
+
}
|
|
160
|
+
return values;
|
|
161
|
+
}
|
|
57
162
|
const paginationProps = {
|
|
58
163
|
cursor: { type: "string" },
|
|
59
164
|
maxItemCount: { type: "integer" },
|
|
60
165
|
orderBy: { type: "string", description: 'Single order-by expression, e.g. "name_asc"' },
|
|
61
166
|
orderByArray: { type: "array", items: { type: "string" }, description: "Multiple order-by expressions" }
|
|
62
167
|
};
|
|
63
|
-
const getRequestSchema = {
|
|
64
|
-
type: "object",
|
|
65
|
-
properties: { where: whereSchema, include: includeSchema }
|
|
66
|
-
};
|
|
67
|
-
const getManyRequestSchema = {
|
|
68
|
-
type: "object",
|
|
69
|
-
properties: { where: whereSchema, include: includeSchema, ...paginationProps }
|
|
70
|
-
};
|
|
71
|
-
const getAggRequestSchema = {
|
|
72
|
-
type: "object",
|
|
73
|
-
required: ["agg"],
|
|
74
|
-
properties: {
|
|
75
|
-
where: whereSchema,
|
|
76
|
-
agg: {
|
|
77
|
-
type: "array",
|
|
78
|
-
items: { type: "string" },
|
|
79
|
-
description: 'Aggregation operations: "_count", "field_min", "field_max", "field_sum"'
|
|
80
|
-
},
|
|
81
|
-
...paginationProps
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
168
|
function jsonBody(s) {
|
|
85
169
|
return { required: true, content: { "application/json": { schema: s } } };
|
|
86
170
|
}
|
|
@@ -93,6 +177,15 @@ export function getOpenApiSpec(options, openApiOptions) {
|
|
|
93
177
|
const base = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
94
178
|
const paths = {};
|
|
95
179
|
const schemas = {};
|
|
180
|
+
schemas.WhereJsonContains = {
|
|
181
|
+
type: "object",
|
|
182
|
+
required: ["path"],
|
|
183
|
+
properties: {
|
|
184
|
+
path: { type: "string", description: 'JSON path, e.g. "meta.countries[0].name"' },
|
|
185
|
+
isNull: { type: "boolean" },
|
|
186
|
+
value: { description: "Expected value at path (string, number, or boolean)" }
|
|
187
|
+
}
|
|
188
|
+
};
|
|
96
189
|
paths[`${base}/me`] = {
|
|
97
190
|
get: {
|
|
98
191
|
summary: "Get current user",
|
|
@@ -143,15 +236,36 @@ export function getOpenApiSpec(options, openApiOptions) {
|
|
|
143
236
|
if (!entity.decorators?.entity) continue;
|
|
144
237
|
const { name, handle, handlePlural } = entity;
|
|
145
238
|
const tag = name;
|
|
239
|
+
const ref = (n) => ({ $ref: `#/components/schemas/${n}` });
|
|
146
240
|
schemas[name] = buildObjectSchema(entity, schema);
|
|
147
241
|
schemas[`${name}Put`] = buildObjectSchema(entity, schema, true);
|
|
148
242
|
schemas[`${name}Put`].required = ["id"];
|
|
149
|
-
|
|
243
|
+
schemas[`${name}Where`] = buildWhereSchema(name, entity, schema);
|
|
244
|
+
schemas[`${name}Include`] = buildIncludeSchema(entity, schema);
|
|
245
|
+
const aggValues = buildAggEnum(entity);
|
|
246
|
+
const aggSchema = {
|
|
247
|
+
type: "array",
|
|
248
|
+
items: { type: "string", enum: aggValues },
|
|
249
|
+
description: "Aggregation operations to compute"
|
|
250
|
+
};
|
|
251
|
+
const getReqBody = {
|
|
252
|
+
type: "object",
|
|
253
|
+
properties: { where: ref(`${name}Where`), include: ref(`${name}Include`) }
|
|
254
|
+
};
|
|
255
|
+
const getManyReqBody = {
|
|
256
|
+
type: "object",
|
|
257
|
+
properties: { where: ref(`${name}Where`), include: ref(`${name}Include`), ...paginationProps }
|
|
258
|
+
};
|
|
259
|
+
const getAggReqBody = {
|
|
260
|
+
type: "object",
|
|
261
|
+
required: ["agg"],
|
|
262
|
+
properties: { where: ref(`${name}Where`), agg: aggSchema, ...paginationProps }
|
|
263
|
+
};
|
|
150
264
|
paths[`${base}/${handle}`] = {
|
|
151
265
|
post: {
|
|
152
266
|
summary: `Get ${name}`,
|
|
153
267
|
tags: [tag],
|
|
154
|
-
requestBody: jsonBody(
|
|
268
|
+
requestBody: jsonBody(getReqBody),
|
|
155
269
|
responses: jsonOk(ref(name), `${name} object`)
|
|
156
270
|
},
|
|
157
271
|
put: {
|
|
@@ -169,7 +283,7 @@ export function getOpenApiSpec(options, openApiOptions) {
|
|
|
169
283
|
post: {
|
|
170
284
|
summary: `Get many ${name} objects`,
|
|
171
285
|
tags: [tag],
|
|
172
|
-
requestBody: jsonBody(
|
|
286
|
+
requestBody: jsonBody(getManyReqBody),
|
|
173
287
|
responses: jsonOk(
|
|
174
288
|
{
|
|
175
289
|
type: "object",
|
|
@@ -197,7 +311,7 @@ export function getOpenApiSpec(options, openApiOptions) {
|
|
|
197
311
|
post: {
|
|
198
312
|
summary: `Aggregate ${name} data`,
|
|
199
313
|
tags: [tag],
|
|
200
|
-
requestBody: jsonBody(
|
|
314
|
+
requestBody: jsonBody(getAggReqBody),
|
|
201
315
|
responses: jsonOk(
|
|
202
316
|
{ type: "object", additionalProperties: { type: "number", nullable: true } },
|
|
203
317
|
`${name} aggregation result`
|
|
@@ -207,7 +321,7 @@ export function getOpenApiSpec(options, openApiOptions) {
|
|
|
207
321
|
}
|
|
208
322
|
for (const key in schema) {
|
|
209
323
|
const entry = schema[key];
|
|
210
|
-
if (entry.decorators?.entity
|
|
324
|
+
if (entry.decorators?.entity) continue;
|
|
211
325
|
if (entry.enumValues) {
|
|
212
326
|
schemas[key] = {
|
|
213
327
|
type: "string",
|
|
@@ -215,7 +329,9 @@ export function getOpenApiSpec(options, openApiOptions) {
|
|
|
215
329
|
...entry.comment ? { description: entry.comment } : {}
|
|
216
330
|
};
|
|
217
331
|
} else if (entry.fields) {
|
|
218
|
-
schemas[key] = buildObjectSchema(entry, schema);
|
|
332
|
+
if (!schemas[key]) schemas[key] = buildObjectSchema(entry, schema);
|
|
333
|
+
schemas[`${key}Where`] = buildWhereSchema(key, entry, schema);
|
|
334
|
+
schemas[`${key}Include`] = buildIncludeSchema(entry, schema);
|
|
219
335
|
}
|
|
220
336
|
}
|
|
221
337
|
return {
|