@terreno/api 0.0.1
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/LICENSE +202 -0
- package/README.md +170 -0
- package/biome.jsonc +22 -0
- package/bunfig.toml +4 -0
- package/dist/api.d.ts +227 -0
- package/dist/api.js +1024 -0
- package/dist/api.test.d.ts +1 -0
- package/dist/api.test.js +2143 -0
- package/dist/auth.d.ts +50 -0
- package/dist/auth.js +512 -0
- package/dist/auth.test.d.ts +1 -0
- package/dist/auth.test.js +778 -0
- package/dist/errors.d.ts +75 -0
- package/dist/errors.js +216 -0
- package/dist/example.d.ts +1 -0
- package/dist/example.js +118 -0
- package/dist/expressServer.d.ts +35 -0
- package/dist/expressServer.js +436 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +30 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.js +249 -0
- package/dist/middleware.d.ts +10 -0
- package/dist/middleware.js +52 -0
- package/dist/notifiers/googleChatNotifier.d.ts +5 -0
- package/dist/notifiers/googleChatNotifier.js +130 -0
- package/dist/notifiers/googleChatNotifier.test.d.ts +1 -0
- package/dist/notifiers/googleChatNotifier.test.js +260 -0
- package/dist/notifiers/index.d.ts +3 -0
- package/dist/notifiers/index.js +19 -0
- package/dist/notifiers/slackNotifier.d.ts +5 -0
- package/dist/notifiers/slackNotifier.js +130 -0
- package/dist/notifiers/slackNotifier.test.d.ts +1 -0
- package/dist/notifiers/slackNotifier.test.js +259 -0
- package/dist/notifiers/zoomNotifier.d.ts +34 -0
- package/dist/notifiers/zoomNotifier.js +181 -0
- package/dist/notifiers/zoomNotifier.test.d.ts +1 -0
- package/dist/notifiers/zoomNotifier.test.js +370 -0
- package/dist/openApi.d.ts +60 -0
- package/dist/openApi.js +441 -0
- package/dist/openApi.test.d.ts +1 -0
- package/dist/openApi.test.js +445 -0
- package/dist/openApiBuilder.d.ts +419 -0
- package/dist/openApiBuilder.js +424 -0
- package/dist/openApiBuilder.test.d.ts +1 -0
- package/dist/openApiBuilder.test.js +509 -0
- package/dist/openApiEtag.d.ts +7 -0
- package/dist/openApiEtag.js +38 -0
- package/dist/permissions.d.ts +26 -0
- package/dist/permissions.js +331 -0
- package/dist/permissions.test.d.ts +1 -0
- package/dist/permissions.test.js +413 -0
- package/dist/plugins.d.ts +67 -0
- package/dist/plugins.js +315 -0
- package/dist/plugins.test.d.ts +1 -0
- package/dist/plugins.test.js +639 -0
- package/dist/populate.d.ts +14 -0
- package/dist/populate.js +315 -0
- package/dist/populate.test.d.ts +1 -0
- package/dist/populate.test.js +133 -0
- package/dist/response.d.ts +0 -0
- package/dist/response.js +1 -0
- package/dist/tests/bunSetup.d.ts +1 -0
- package/dist/tests/bunSetup.js +297 -0
- package/dist/tests/index.d.ts +1 -0
- package/dist/tests/index.js +17 -0
- package/dist/tests.d.ts +99 -0
- package/dist/tests.js +273 -0
- package/dist/transformers.d.ts +25 -0
- package/dist/transformers.js +217 -0
- package/dist/transformers.test.d.ts +1 -0
- package/dist/transformers.test.js +370 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +143 -0
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +14 -0
- package/index.ts +1 -0
- package/package.json +88 -0
- package/src/__snapshots__/openApi.test.ts.snap +4814 -0
- package/src/__snapshots__/openApiBuilder.test.ts.snap +1485 -0
- package/src/api.test.ts +1661 -0
- package/src/api.ts +1036 -0
- package/src/auth.test.ts +550 -0
- package/src/auth.ts +408 -0
- package/src/errors.ts +225 -0
- package/src/example.ts +99 -0
- package/src/express.d.ts +5 -0
- package/src/expressServer.ts +387 -0
- package/src/index.ts +14 -0
- package/src/logger.ts +190 -0
- package/src/middleware.ts +18 -0
- package/src/notifiers/googleChatNotifier.test.ts +114 -0
- package/src/notifiers/googleChatNotifier.ts +47 -0
- package/src/notifiers/index.ts +3 -0
- package/src/notifiers/slackNotifier.test.ts +113 -0
- package/src/notifiers/slackNotifier.ts +55 -0
- package/src/notifiers/zoomNotifier.test.ts +207 -0
- package/src/notifiers/zoomNotifier.ts +111 -0
- package/src/openApi.test.ts +331 -0
- package/src/openApi.ts +494 -0
- package/src/openApiBuilder.test.ts +442 -0
- package/src/openApiBuilder.ts +636 -0
- package/src/openApiEtag.ts +40 -0
- package/src/permissions.test.ts +219 -0
- package/src/permissions.ts +228 -0
- package/src/plugins.test.ts +390 -0
- package/src/plugins.ts +289 -0
- package/src/populate.test.ts +65 -0
- package/src/populate.ts +258 -0
- package/src/response.ts +0 -0
- package/src/tests/bunSetup.ts +234 -0
- package/src/tests/index.ts +1 -0
- package/src/tests.ts +218 -0
- package/src/transformers.test.ts +202 -0
- package/src/transformers.ts +170 -0
- package/src/utils.test.ts +14 -0
- package/src/utils.ts +47 -0
- package/tsconfig.json +60 -0
- package/types.d.ts +17 -0
package/src/openApi.ts
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import flatten from "lodash/flatten";
|
|
2
|
+
import merge from "lodash/merge";
|
|
3
|
+
import type {Model} from "mongoose";
|
|
4
|
+
import m2s from "mongoose-to-swagger";
|
|
5
|
+
|
|
6
|
+
import type {modelRouterOptions} from "./api";
|
|
7
|
+
import {logger} from "./logger";
|
|
8
|
+
import {getOpenApiSpecForModel} from "./populate";
|
|
9
|
+
|
|
10
|
+
const noop = (_a, _b, next) => next();
|
|
11
|
+
|
|
12
|
+
const m2sOptions = {
|
|
13
|
+
props: ["readOnly", "required", "enum", "default"],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const apiErrorContent = {
|
|
17
|
+
"application/json": {
|
|
18
|
+
schema: {$ref: "#/components/schemas/APIError"},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Default error responses
|
|
23
|
+
export const defaultOpenApiErrorResponses = {
|
|
24
|
+
400: {
|
|
25
|
+
content: apiErrorContent,
|
|
26
|
+
description: "Bad request",
|
|
27
|
+
},
|
|
28
|
+
401: {
|
|
29
|
+
description: "The user must be authenticated",
|
|
30
|
+
},
|
|
31
|
+
403: {
|
|
32
|
+
content: apiErrorContent,
|
|
33
|
+
description: "The user is not allowed to perform this action on this document",
|
|
34
|
+
},
|
|
35
|
+
404: {
|
|
36
|
+
content: apiErrorContent,
|
|
37
|
+
description: "Document not found",
|
|
38
|
+
},
|
|
39
|
+
405: {
|
|
40
|
+
content: apiErrorContent,
|
|
41
|
+
description: "The user is not allowed to perform this action on any document",
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// We repeat this constantly, so we make it a component so we only have to define it once.
|
|
46
|
+
function createAPIErrorComponent(openApi: any) {
|
|
47
|
+
// Create a schema component called APIError
|
|
48
|
+
openApi?.component("schemas", "APIError", {
|
|
49
|
+
properties: {
|
|
50
|
+
code: {
|
|
51
|
+
description: "An application-specific error code, expressed as a string value.",
|
|
52
|
+
type: "string",
|
|
53
|
+
},
|
|
54
|
+
detail: {
|
|
55
|
+
description:
|
|
56
|
+
"A human-readable explanation specific to this occurrence of the problem. Like title, this field’s value can be localized.",
|
|
57
|
+
type: "string",
|
|
58
|
+
},
|
|
59
|
+
id: {
|
|
60
|
+
description: "A unique identifier for this particular occurrence of the problem.",
|
|
61
|
+
type: "string",
|
|
62
|
+
},
|
|
63
|
+
links: {
|
|
64
|
+
properties: {
|
|
65
|
+
about: {
|
|
66
|
+
description:
|
|
67
|
+
"A link that leads to further details about this particular occurrence of the problem. When derefenced, this URI SHOULD return a human-readable description of the error.",
|
|
68
|
+
type: "string",
|
|
69
|
+
},
|
|
70
|
+
type: {
|
|
71
|
+
description:
|
|
72
|
+
"A link that identifies the type of error that this particular error is an instance of. This URI SHOULD be dereferencable to a human-readable explanation of the general error.",
|
|
73
|
+
type: "string",
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
type: "object",
|
|
77
|
+
},
|
|
78
|
+
meta: {
|
|
79
|
+
description: "A meta object containing non-standard meta-information about the error.",
|
|
80
|
+
type: "object",
|
|
81
|
+
},
|
|
82
|
+
source: {
|
|
83
|
+
properties: {
|
|
84
|
+
header: {
|
|
85
|
+
description:
|
|
86
|
+
"A string indicating the name of a single request header which caused the error.",
|
|
87
|
+
type: "string",
|
|
88
|
+
},
|
|
89
|
+
parameter: {
|
|
90
|
+
description: "A string indicating which URI query parameter caused the error.",
|
|
91
|
+
type: "string",
|
|
92
|
+
},
|
|
93
|
+
pointer: {
|
|
94
|
+
description:
|
|
95
|
+
'A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data" for a primary data object, or "/data/attributes/title" for a specific attribute].',
|
|
96
|
+
type: "string",
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
type: "object",
|
|
100
|
+
},
|
|
101
|
+
status: {
|
|
102
|
+
description:
|
|
103
|
+
"The HTTP status code applicable to this problem, expressed as a string value.",
|
|
104
|
+
type: "number",
|
|
105
|
+
},
|
|
106
|
+
title: {
|
|
107
|
+
description: "The error message",
|
|
108
|
+
type: "string",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
type: "object",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function getOpenApiMiddleware<T>(model: Model<T>, options: Partial<modelRouterOptions<T>>) {
|
|
116
|
+
createAPIErrorComponent(options.openApi);
|
|
117
|
+
if (!options.openApi?.path) {
|
|
118
|
+
// Just log this once rather than for each middleware.
|
|
119
|
+
logger.debug("No options.openApi provided, skipping *OpenApiMiddleware");
|
|
120
|
+
return noop;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (options.permissions?.read?.length === 0) {
|
|
124
|
+
return noop;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const {properties, required} = getOpenApiSpecForModel(model, {
|
|
128
|
+
extraModelProperties: options.openApiExtraModelProperties,
|
|
129
|
+
populatePaths: options.populatePaths,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return options.openApi.path(
|
|
133
|
+
merge(
|
|
134
|
+
{
|
|
135
|
+
responses: {
|
|
136
|
+
200: {
|
|
137
|
+
content: {
|
|
138
|
+
"application/json": {
|
|
139
|
+
schema: {
|
|
140
|
+
properties,
|
|
141
|
+
required: [...required, "_id", "created", "updated"],
|
|
142
|
+
type: "object",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
description: "Successful read",
|
|
147
|
+
},
|
|
148
|
+
...defaultOpenApiErrorResponses,
|
|
149
|
+
},
|
|
150
|
+
tags: [model.collection.collectionName],
|
|
151
|
+
},
|
|
152
|
+
options.openApiOverwrite?.get ?? {}
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function listOpenApiMiddleware<T>(model: Model<T>, options: Partial<modelRouterOptions<T>>) {
|
|
158
|
+
if (!options.openApi?.path) {
|
|
159
|
+
return noop;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (options.permissions?.list?.length === 0) {
|
|
163
|
+
return noop;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const modelSwagger = m2s(model, m2sOptions);
|
|
167
|
+
|
|
168
|
+
// TODO: handle permissions
|
|
169
|
+
|
|
170
|
+
// Convert modelRouter queryFields into OpenAPI parameters
|
|
171
|
+
const defaultQueryParams = [
|
|
172
|
+
{
|
|
173
|
+
in: "query",
|
|
174
|
+
name: "_id",
|
|
175
|
+
schema: {
|
|
176
|
+
properties: {
|
|
177
|
+
$in: {
|
|
178
|
+
items: {
|
|
179
|
+
type: "string",
|
|
180
|
+
},
|
|
181
|
+
type: "array",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
type: "object",
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
const modelQueryParams = flatten(
|
|
189
|
+
options.queryFields
|
|
190
|
+
// Remove _id from queryFields, we handle that above.
|
|
191
|
+
?.filter((field) => field !== "_id")
|
|
192
|
+
.map((field) => {
|
|
193
|
+
const params: {name: string; in: "query"; schema: any}[] = [];
|
|
194
|
+
|
|
195
|
+
// Check for datetime/number to support gt/gte/lt/lte
|
|
196
|
+
if (
|
|
197
|
+
modelSwagger.properties[field]?.type === "number" ||
|
|
198
|
+
modelSwagger.properties[field]?.format === "date-time"
|
|
199
|
+
) {
|
|
200
|
+
params.push({
|
|
201
|
+
in: "query",
|
|
202
|
+
name: field,
|
|
203
|
+
schema: {
|
|
204
|
+
oneOf: [
|
|
205
|
+
modelSwagger.properties[field],
|
|
206
|
+
{
|
|
207
|
+
properties: {
|
|
208
|
+
$gt: modelSwagger.properties[field],
|
|
209
|
+
$gte: modelSwagger.properties[field],
|
|
210
|
+
$lt: modelSwagger.properties[field],
|
|
211
|
+
$lte: modelSwagger.properties[field],
|
|
212
|
+
},
|
|
213
|
+
type: "object",
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
} else {
|
|
219
|
+
params.push({
|
|
220
|
+
in: "query",
|
|
221
|
+
name: field,
|
|
222
|
+
schema: {
|
|
223
|
+
oneOf: [
|
|
224
|
+
modelSwagger.properties[field],
|
|
225
|
+
{
|
|
226
|
+
properties: {
|
|
227
|
+
$in: {
|
|
228
|
+
items: {
|
|
229
|
+
type: modelSwagger.properties[field]?.type,
|
|
230
|
+
},
|
|
231
|
+
type: "array",
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
type: "object",
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return params;
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const {properties, required} = getOpenApiSpecForModel(model, {
|
|
246
|
+
extraModelProperties: options.openApiExtraModelProperties,
|
|
247
|
+
populatePaths: options.populatePaths,
|
|
248
|
+
});
|
|
249
|
+
return options.openApi.path(
|
|
250
|
+
merge(
|
|
251
|
+
{
|
|
252
|
+
parameters: [
|
|
253
|
+
...defaultQueryParams,
|
|
254
|
+
...(modelQueryParams ?? []),
|
|
255
|
+
// pagination
|
|
256
|
+
{
|
|
257
|
+
in: "query",
|
|
258
|
+
name: "page",
|
|
259
|
+
schema: {
|
|
260
|
+
type: "number",
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
in: "query",
|
|
265
|
+
name: "sort",
|
|
266
|
+
schema: {
|
|
267
|
+
type: "string",
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
in: "query",
|
|
272
|
+
name: "limit",
|
|
273
|
+
schema: {
|
|
274
|
+
type: "number",
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
responses: {
|
|
279
|
+
200: {
|
|
280
|
+
content: {
|
|
281
|
+
"application/json": {
|
|
282
|
+
schema: {
|
|
283
|
+
properties: {
|
|
284
|
+
data: {
|
|
285
|
+
items: {
|
|
286
|
+
properties,
|
|
287
|
+
required: [...required, "_id", "created", "updated"],
|
|
288
|
+
type: "object",
|
|
289
|
+
},
|
|
290
|
+
type: "array",
|
|
291
|
+
},
|
|
292
|
+
limit: {
|
|
293
|
+
type: "number",
|
|
294
|
+
},
|
|
295
|
+
more: {
|
|
296
|
+
type: "boolean",
|
|
297
|
+
},
|
|
298
|
+
page: {
|
|
299
|
+
type: "number",
|
|
300
|
+
},
|
|
301
|
+
total: {
|
|
302
|
+
type: "number",
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
type: "object",
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
description: "Successful list",
|
|
310
|
+
},
|
|
311
|
+
...defaultOpenApiErrorResponses,
|
|
312
|
+
},
|
|
313
|
+
tags: [model.collection.collectionName],
|
|
314
|
+
},
|
|
315
|
+
options.openApiOverwrite?.list ?? {}
|
|
316
|
+
)
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function createOpenApiMiddleware<T>(
|
|
321
|
+
model: Model<T>,
|
|
322
|
+
options: Partial<modelRouterOptions<T>>
|
|
323
|
+
) {
|
|
324
|
+
if (!options.openApi?.path) {
|
|
325
|
+
return noop;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (options.permissions?.create?.length === 0) {
|
|
329
|
+
return noop;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const {properties, required} = getOpenApiSpecForModel(model, {
|
|
333
|
+
extraModelProperties: options.openApiExtraModelProperties,
|
|
334
|
+
populatePaths: options.populatePaths,
|
|
335
|
+
});
|
|
336
|
+
return options.openApi.path(
|
|
337
|
+
merge(
|
|
338
|
+
{
|
|
339
|
+
requestBody: {
|
|
340
|
+
content: {
|
|
341
|
+
"application/json": {
|
|
342
|
+
schema: {
|
|
343
|
+
properties,
|
|
344
|
+
type: "object",
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
required: true,
|
|
349
|
+
},
|
|
350
|
+
responses: {
|
|
351
|
+
201: {
|
|
352
|
+
content: {
|
|
353
|
+
"application/json": {
|
|
354
|
+
schema: {
|
|
355
|
+
properties,
|
|
356
|
+
required: [...required, "_id", "created", "updated"],
|
|
357
|
+
type: "object",
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
description: "Successful create",
|
|
362
|
+
},
|
|
363
|
+
...defaultOpenApiErrorResponses,
|
|
364
|
+
},
|
|
365
|
+
tags: [model.collection.collectionName],
|
|
366
|
+
},
|
|
367
|
+
options.openApiOverwrite?.create ?? {}
|
|
368
|
+
)
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function patchOpenApiMiddleware<T>(
|
|
373
|
+
model: Model<T>,
|
|
374
|
+
options: Partial<modelRouterOptions<T>>
|
|
375
|
+
) {
|
|
376
|
+
if (!options.openApi?.path) {
|
|
377
|
+
return noop;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (options.permissions?.update?.length === 0) {
|
|
381
|
+
return noop;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const {properties, required} = getOpenApiSpecForModel(model, {
|
|
385
|
+
extraModelProperties: options.openApiExtraModelProperties,
|
|
386
|
+
populatePaths: options.populatePaths,
|
|
387
|
+
});
|
|
388
|
+
return options.openApi.path(
|
|
389
|
+
merge(
|
|
390
|
+
{
|
|
391
|
+
requestBody: {
|
|
392
|
+
content: {
|
|
393
|
+
"application/json": {
|
|
394
|
+
schema: {
|
|
395
|
+
properties,
|
|
396
|
+
type: "object",
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
required: true,
|
|
401
|
+
},
|
|
402
|
+
responses: {
|
|
403
|
+
200: {
|
|
404
|
+
content: {
|
|
405
|
+
"application/json": {
|
|
406
|
+
schema: {
|
|
407
|
+
properties,
|
|
408
|
+
required: [...required, "_id", "created", "updated"],
|
|
409
|
+
type: "object",
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
description: "Successful update",
|
|
414
|
+
},
|
|
415
|
+
...defaultOpenApiErrorResponses,
|
|
416
|
+
},
|
|
417
|
+
tags: [model.collection.collectionName],
|
|
418
|
+
},
|
|
419
|
+
options.openApiOverwrite?.update ?? {}
|
|
420
|
+
)
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export function deleteOpenApiMiddleware<T>(
|
|
425
|
+
model: Model<T>,
|
|
426
|
+
options: Partial<modelRouterOptions<T>>
|
|
427
|
+
) {
|
|
428
|
+
if (!options.openApi?.path) {
|
|
429
|
+
return noop;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (options.permissions?.delete?.length === 0) {
|
|
433
|
+
return noop;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return options.openApi.path(
|
|
437
|
+
merge(
|
|
438
|
+
{
|
|
439
|
+
responses: {
|
|
440
|
+
204: {
|
|
441
|
+
description: "Successful delete",
|
|
442
|
+
},
|
|
443
|
+
...defaultOpenApiErrorResponses,
|
|
444
|
+
},
|
|
445
|
+
tags: [model.collection.collectionName],
|
|
446
|
+
},
|
|
447
|
+
options.openApiOverwrite?.delete ?? {}
|
|
448
|
+
)
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// This is a generic OpenAPI wrapper for a read that returns any object described by `properties`.
|
|
453
|
+
// Useful for endpoints that don't directly map to a model.
|
|
454
|
+
export function readOpenApiMiddleware<T>(
|
|
455
|
+
options: Partial<modelRouterOptions<T>>,
|
|
456
|
+
properties: any,
|
|
457
|
+
required: string[],
|
|
458
|
+
queryParameters: any
|
|
459
|
+
): any {
|
|
460
|
+
if (!options.openApi?.path) {
|
|
461
|
+
// Just log this once rather than for each middleware.
|
|
462
|
+
logger.debug("No options.openApi provided, skipping *OpenApiMiddleware");
|
|
463
|
+
return noop;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (options.permissions?.read?.length === 0) {
|
|
467
|
+
return noop;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return options.openApi.path(
|
|
471
|
+
merge(
|
|
472
|
+
{
|
|
473
|
+
parameters: queryParameters,
|
|
474
|
+
responses: {
|
|
475
|
+
200: {
|
|
476
|
+
content: {
|
|
477
|
+
"application/json": {
|
|
478
|
+
schema: {
|
|
479
|
+
properties,
|
|
480
|
+
required,
|
|
481
|
+
type: "object",
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
description: "Successful read",
|
|
486
|
+
},
|
|
487
|
+
...defaultOpenApiErrorResponses,
|
|
488
|
+
},
|
|
489
|
+
tags: [],
|
|
490
|
+
},
|
|
491
|
+
options.openApiOverwrite?.get ?? {}
|
|
492
|
+
)
|
|
493
|
+
);
|
|
494
|
+
}
|