@visulima/crud 1.0.0

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +101 -0
  4. package/dist/chunk-FJWRITBO.js +52 -0
  5. package/dist/chunk-FJWRITBO.js.map +1 -0
  6. package/dist/chunk-UBXIGP5H.mjs +52 -0
  7. package/dist/chunk-UBXIGP5H.mjs.map +1 -0
  8. package/dist/index.d.ts +155 -0
  9. package/dist/index.js +1101 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/index.mjs +1101 -0
  12. package/dist/index.mjs.map +1 -0
  13. package/dist/next/index.d.ts +8 -0
  14. package/dist/next/index.js +729 -0
  15. package/dist/next/index.js.map +1 -0
  16. package/dist/next/index.mjs +729 -0
  17. package/dist/next/index.mjs.map +1 -0
  18. package/dist/types.d-6817d247.d.ts +155 -0
  19. package/package.json +136 -0
  20. package/src/adapter/prisma/index.ts +241 -0
  21. package/src/adapter/prisma/types.d.ts +46 -0
  22. package/src/adapter/prisma/utils/models-to-route-names.ts +12 -0
  23. package/src/adapter/prisma/utils/parse-cursor.ts +26 -0
  24. package/src/adapter/prisma/utils/parse-order-by.ts +21 -0
  25. package/src/adapter/prisma/utils/parse-recursive.ts +26 -0
  26. package/src/adapter/prisma/utils/parse-where.ts +197 -0
  27. package/src/base-crud-handler.ts +181 -0
  28. package/src/handler/create.ts +21 -0
  29. package/src/handler/delete.ts +27 -0
  30. package/src/handler/list.ts +62 -0
  31. package/src/handler/read.ts +27 -0
  32. package/src/handler/update.ts +29 -0
  33. package/src/index.ts +27 -0
  34. package/src/next/api/edge/index.ts +23 -0
  35. package/src/next/api/node/index.ts +27 -0
  36. package/src/next/index.ts +2 -0
  37. package/src/query-parser.ts +94 -0
  38. package/src/swagger/adapter/prisma/index.ts +95 -0
  39. package/src/swagger/json-schema-parser.ts +456 -0
  40. package/src/swagger/parameters.ts +83 -0
  41. package/src/swagger/types.d.ts +53 -0
  42. package/src/swagger/utils/format-example-ref.ts +4 -0
  43. package/src/swagger/utils/format-schema-ref.ts +4 -0
  44. package/src/swagger/utils/get-models-accessible-routes.ts +23 -0
  45. package/src/swagger/utils/get-swagger-paths.ts +244 -0
  46. package/src/swagger/utils/get-swagger-tags.ts +13 -0
  47. package/src/types.d.ts +124 -0
  48. package/src/utils/format-resource-id.ts +3 -0
  49. package/src/utils/get-accessible-routes.ts +18 -0
  50. package/src/utils/get-resource-name-from-url.ts +23 -0
  51. package/src/utils/get-route-type.ts +99 -0
  52. package/src/utils/is-primitive.ts +5 -0
  53. package/src/utils/validate-adapter-methods.ts +15 -0
@@ -0,0 +1,27 @@
1
+ import type { NextApiRequest, NextApiResponse } from "next";
2
+
3
+ import baseHandler from "../../../base-crud-handler";
4
+ import type {
5
+ Adapter, ExecuteHandler, HandlerOptions, ParsedQueryParameters,
6
+ } from "../../../types.d";
7
+
8
+ async function handler<
9
+ T,
10
+ Q extends ParsedQueryParameters = any,
11
+ R extends NextApiRequest = NextApiRequest,
12
+ Response extends NextApiResponse = NextApiResponse,
13
+ M extends string = string,
14
+ >(adapter: Adapter<T, Q>, options?: HandlerOptions<M>): Promise<ExecuteHandler<R, Response>> {
15
+ return baseHandler<R, Response, T, Q, M>(
16
+ async (response, responseConfig) => {
17
+ response.status(responseConfig.status).send(responseConfig.data);
18
+ },
19
+ async (response) => {
20
+ (response as Response).end();
21
+ },
22
+ adapter,
23
+ options,
24
+ );
25
+ }
26
+
27
+ export default handler;
@@ -0,0 +1,2 @@
1
+ export { default as edgeHandler } from "./api/edge";
2
+ export { default as nodeHandler } from "./api/node";
@@ -0,0 +1,94 @@
1
+ import set from "lodash.set";
2
+ import { parse } from "qs";
3
+
4
+ import type {
5
+ OrderByField, ParsedQueryParameters, RecursiveField, WhereField,
6
+ } from "./types.d";
7
+
8
+ const parseRecursive = (select: string): RecursiveField => {
9
+ if (typeof select === "string") {
10
+ const selectFields: RecursiveField = {};
11
+
12
+ const fields = select.split(",");
13
+
14
+ fields.forEach((field) => {
15
+ set(selectFields, field, true);
16
+ });
17
+
18
+ return selectFields;
19
+ }
20
+
21
+ throw new Error("select query param must be a string");
22
+ };
23
+
24
+ const parseWhere = (where: string): WhereField => {
25
+ const whereObject = JSON.parse(where);
26
+ const parsed: WhereField = {};
27
+
28
+ Object.keys(whereObject).forEach((key) => {
29
+ set(parsed, key, whereObject[key]);
30
+ });
31
+
32
+ return parsed;
33
+ };
34
+
35
+ const parseOrderBy = (orderBy: string): OrderByField => {
36
+ const parsed: OrderByField = {};
37
+ const orderByObject = JSON.parse(orderBy);
38
+
39
+ if (Object.keys(orderByObject).length > 0) {
40
+ const key = Object.keys(orderByObject)[0];
41
+
42
+ if (orderByObject[key as keyof typeof orderByObject] === "$asc" || orderByObject[key as keyof typeof orderByObject] === "$desc") {
43
+ parsed[key as string] = orderByObject[key as keyof typeof orderByObject];
44
+ }
45
+ }
46
+
47
+ if (Object.keys(parsed).length !== 1) {
48
+ throw new Error("orderBy needs to be an object with exactly 1 property with either $asc or $desc value");
49
+ }
50
+
51
+ return parsed;
52
+ };
53
+
54
+ // eslint-disable-next-line radar/cognitive-complexity
55
+ const parseQuery = (queryString?: string): ParsedQueryParameters => {
56
+ if (queryString) {
57
+ const query = parse(queryString);
58
+ const parsedQuery: ParsedQueryParameters = {};
59
+
60
+ if (query.select) {
61
+ parsedQuery.select = parseRecursive(query.select as string);
62
+ }
63
+ if (query.include) {
64
+ parsedQuery.include = parseRecursive(query.include as string);
65
+ }
66
+ if (query.where) {
67
+ parsedQuery.where = parseWhere(query.where as string);
68
+ }
69
+ if (query.orderBy) {
70
+ parsedQuery.orderBy = parseOrderBy(query.orderBy as string);
71
+ }
72
+ if (typeof query.limit !== "undefined") {
73
+ parsedQuery.limit = Number.isFinite(+query.limit) ? +query.limit : undefined;
74
+ }
75
+ if (typeof query.skip !== "undefined") {
76
+ parsedQuery.skip = Number.isFinite(+query.skip) ? +query.skip : undefined;
77
+ }
78
+ if (query.distinct) {
79
+ parsedQuery.distinct = query.distinct as string;
80
+ }
81
+ if (query.page) {
82
+ parsedQuery.page = Number.isFinite(+query.page) ? +query.page : undefined;
83
+ }
84
+
85
+ return {
86
+ originalQuery: query,
87
+ ...parsedQuery,
88
+ };
89
+ }
90
+
91
+ return {};
92
+ };
93
+
94
+ export default parseQuery;
@@ -0,0 +1,95 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+ import {
3
+ // @ts-ignore
4
+ PrismaClient,
5
+ } from "@prisma/client";
6
+
7
+ import modelsToRouteNames from "../../../adapter/prisma/utils/models-to-route-names";
8
+ import type { ModelsOptions } from "../../../types.d";
9
+ import PrismaJsonSchemaParser from "../../json-schema-parser";
10
+ import type { SwaggerModelsConfig } from "../../types.d";
11
+ import getModelsAccessibleRoutes from "../../utils/get-models-accessible-routes";
12
+ import getSwaggerPaths from "../../utils/get-swagger-paths";
13
+ import getSwaggerTags from "../../utils/get-swagger-tags";
14
+
15
+ const modelsToOpenApi = async <M extends string = string>({
16
+ prismaClient,
17
+ models: ctorModels,
18
+ swagger = { models: {}, allowedMediaTypes: { "application/json": true } },
19
+ crud = { models: {} },
20
+ defaultExposeStrategy = "all",
21
+ }: ModelsToOpenApiParameters<M>) => {
22
+ let dmmf: any;
23
+ let prismaDmmfModels: any;
24
+
25
+ // eslint-disable-next-line no-underscore-dangle
26
+ if (prismaClient._dmmf) {
27
+ // eslint-disable-next-line no-underscore-dangle
28
+ dmmf = prismaClient._dmmf;
29
+ prismaDmmfModels = dmmf?.mappingsMap;
30
+ // eslint-disable-next-line no-underscore-dangle
31
+ } else if (prismaClient._getDmmf) {
32
+ // eslint-disable-next-line no-underscore-dangle
33
+ dmmf = await prismaClient._getDmmf();
34
+ prismaDmmfModels = dmmf.mappingsMap;
35
+ }
36
+
37
+ if (typeof dmmf === undefined) {
38
+ throw new TypeError("Couldn't get prisma client models");
39
+ }
40
+
41
+ const parser = new PrismaJsonSchemaParser(dmmf);
42
+
43
+ const definitions = parser.parseModels();
44
+ const dModels = Object.keys(definitions);
45
+
46
+ const schema = JSON.stringify({
47
+ ...definitions,
48
+ ...parser.parseInputTypes(dModels),
49
+ ...parser.getPaginationDataSchema(),
50
+ ...parser.getPaginatedModelsSchemas(dModels),
51
+ });
52
+
53
+ if (typeof ctorModels !== "undefined") {
54
+ ctorModels.forEach((model) => {
55
+ if (!Object.keys(prismaDmmfModels).includes(model)) {
56
+ throw new Error(`Model name ${model} is invalid.`);
57
+ }
58
+ });
59
+ }
60
+
61
+ // @ts-ignore
62
+ const models = ctorModels ?? (Object.keys(prismaDmmfModels) as M[]);
63
+
64
+ const swaggerRoutes = getModelsAccessibleRoutes(models, crud.models || {}, defaultExposeStrategy);
65
+ const swaggerTags = getSwaggerTags(models, swagger?.models || {});
66
+ const swaggerPaths = getSwaggerPaths({
67
+ routes: swaggerRoutes,
68
+ modelsConfig: swagger?.models || {},
69
+ models: crud.models || {},
70
+ routesMap: modelsToRouteNames(prismaDmmfModels, models),
71
+ });
72
+ const schemas = JSON.parse(schema.replace(/#\/definitions/g, "#/components/schemas"));
73
+
74
+ return {
75
+ schemas,
76
+ examples: parser.getExampleModelsSchemas(dModels, schemas),
77
+ tags: swaggerTags,
78
+ paths: swaggerPaths,
79
+ };
80
+ };
81
+
82
+ export interface ModelsToOpenApiParameters<M extends string = string> {
83
+ prismaClient: PrismaClient;
84
+ defaultExposeStrategy?: "all" | "none";
85
+ models?: M[];
86
+ swagger?: Partial<{
87
+ models: SwaggerModelsConfig<M>;
88
+ allowedMediaTypes: { [key: string]: boolean };
89
+ }>;
90
+ crud?: {
91
+ models: ModelsOptions<M>;
92
+ };
93
+ }
94
+
95
+ export default modelsToOpenApi;
@@ -0,0 +1,456 @@
1
+ import { getJSONSchemaProperty, transformDMMF } from "@visulima/prisma-dmmf-transformer";
2
+ import type { JSONSchema7 } from "json-schema";
3
+ import type { OpenAPIV3 } from "openapi-types";
4
+
5
+ import formatSchemaReference from "./utils/format-schema-ref";
6
+
7
+ const getJSONSchemaScalar = (fieldType: string | object) => {
8
+ switch (fieldType) {
9
+ case "Int":
10
+ case "BigInt": {
11
+ return "integer";
12
+ }
13
+ case "DateTime":
14
+ case "Bytes":
15
+ case "String": {
16
+ return "string";
17
+ }
18
+ case "Float":
19
+ case "Decimal": {
20
+ return "number";
21
+ }
22
+ case "Json": {
23
+ return "object";
24
+ }
25
+ case "Boolean": {
26
+ return "boolean";
27
+ }
28
+ case "Null": {
29
+ return "null";
30
+ }
31
+ default: {
32
+ return "";
33
+ }
34
+ }
35
+ };
36
+
37
+ const PAGINATION_SCHEMA_NAME = "PaginationData";
38
+
39
+ const methodsNames = [
40
+ { methodStart: "createOne", schemaNameStart: "Create" },
41
+ { methodStart: "updateOne", schemaNameStart: "Update" },
42
+ ];
43
+
44
+ class PrismaJsonSchemaParser {
45
+ schemaInputTypes: Map<string, any> = new Map<string, any>();
46
+
47
+ constructor(private dmmf: any) {}
48
+
49
+ public parseModels(): {
50
+ [key: string]: JSONSchema7;
51
+ } {
52
+ const modelsDefinitions = transformDMMF(this.dmmf).definitions as {
53
+ [key: string]: JSONSchema7;
54
+ };
55
+
56
+ Object.keys(modelsDefinitions || {})?.forEach((definition: string | number) => {
57
+ // @TODO: added the correct type
58
+ // @ts-ignore
59
+ const { properties } = modelsDefinitions[definition];
60
+
61
+ Object.keys(properties).forEach((property: string) => {
62
+ if (Array.isArray(properties[property].type) && properties[property].type.includes("null")) {
63
+ properties[property].type = properties[property].type.filter((type: string) => type !== "null");
64
+
65
+ if (properties[property].type.length === 1) {
66
+ // eslint-disable-next-line prefer-destructuring
67
+ properties[property].type = properties[property].type[0];
68
+ }
69
+
70
+ properties[property].nullable = true;
71
+ }
72
+ });
73
+ });
74
+
75
+ return modelsDefinitions;
76
+ }
77
+
78
+ // eslint-disable-next-line radar/cognitive-complexity
79
+ public parseInputTypes(models: string[]) {
80
+ // eslint-disable-next-line radar/cognitive-complexity
81
+ const definitions = models.reduce((accumulator: { [key: string]: any }, modelName) => {
82
+ const methods = methodsNames.map((method) => {
83
+ return {
84
+ name: `${method.methodStart}${modelName}`,
85
+ schemaName: `${method.schemaNameStart}${modelName}`,
86
+ };
87
+ });
88
+
89
+ methods.forEach(({ name: method, schemaName }) => {
90
+ // @ts-ignore
91
+ const dataFields = this.dmmf.mutationType.fieldMap[method].args[0].inputTypes[0].type.fields;
92
+ const requiredProperties: string[] = [];
93
+ const properties = dataFields.reduce((propertiesAccumulator: any, field: any) => {
94
+ if (field.inputTypes[0].kind === "scalar") {
95
+ const schema = getJSONSchemaProperty(
96
+ this.dmmf.datamodel,
97
+ {},
98
+ )({
99
+ name: field.name,
100
+ ...field.inputTypes[0],
101
+ });
102
+
103
+ // @TODO: added the correct type
104
+ // @ts-ignore
105
+ const { type: schemaType } = schema[1];
106
+
107
+ if (schemaType && Array.isArray(schemaType)) {
108
+ if (schemaType.includes("null")) {
109
+ // eslint-disable-next-line no-param-reassign
110
+ propertiesAccumulator[field.name] = {
111
+ ...schemaType,
112
+ type: schemaType.filter((type: string) => type !== "null"),
113
+ nullable: true,
114
+ };
115
+ if (propertiesAccumulator[field.name].type.length === 1) {
116
+ // eslint-disable-next-line no-param-reassign
117
+ propertiesAccumulator[field.name] = {
118
+ ...propertiesAccumulator[field.name],
119
+ type: propertiesAccumulator[field.name].type[0],
120
+ };
121
+ }
122
+ }
123
+ } else {
124
+ // eslint-disable-next-line no-param-reassign,prefer-destructuring
125
+ propertiesAccumulator[field.name] = schema[1];
126
+ }
127
+ } else {
128
+ const typeName = this.parseObjectInputType(field.inputTypes[0]);
129
+
130
+ // eslint-disable-next-line no-param-reassign
131
+ propertiesAccumulator[field.name] = {
132
+ ...typeName,
133
+ nullable: field.isNullable,
134
+ };
135
+ }
136
+
137
+ if (field.isRequired) {
138
+ requiredProperties.push(field.name);
139
+ }
140
+
141
+ return propertiesAccumulator;
142
+ }, {});
143
+
144
+ accumulator[schemaName] = {
145
+ type: "object",
146
+ xml: {
147
+ name: schemaName,
148
+ },
149
+ properties,
150
+ };
151
+
152
+ if (requiredProperties.length > 0) {
153
+ accumulator[schemaName].required = requiredProperties;
154
+ }
155
+ });
156
+
157
+ return accumulator;
158
+ }, {});
159
+
160
+ this.schemaInputTypes.forEach((value, key) => {
161
+ definitions[key] = {
162
+ type: "object",
163
+ xml: {
164
+ name: key,
165
+ },
166
+ properties: value,
167
+ };
168
+ });
169
+
170
+ return definitions;
171
+ }
172
+
173
+ // eslint-disable-next-line class-methods-use-this
174
+ public formatInputTypeData(inputType: any) {
175
+ if (inputType.kind === "object") {
176
+ const reference = formatSchemaReference(inputType.type.name);
177
+
178
+ if (inputType.isList) {
179
+ return {
180
+ type: "array",
181
+ xml: {
182
+ name: inputType.type.name,
183
+ wrapped: true,
184
+ },
185
+ items: {
186
+ $ref: reference,
187
+ },
188
+ };
189
+ }
190
+
191
+ return { $ref: reference };
192
+ }
193
+
194
+ const type = getJSONSchemaScalar(inputType.type);
195
+
196
+ if (inputType.isList) {
197
+ return {
198
+ type: "array",
199
+ xml: {
200
+ name: inputType.type.name,
201
+ wrapped: true,
202
+ },
203
+ items: {
204
+ type,
205
+ },
206
+ };
207
+ }
208
+
209
+ return { type };
210
+ }
211
+
212
+ // eslint-disable-next-line radar/cognitive-complexity
213
+ public parseObjectInputType(fieldType: any) {
214
+ if (fieldType.kind === "object") {
215
+ if (!this.schemaInputTypes.has(fieldType.type.name)) {
216
+ this.schemaInputTypes.set(fieldType.type.name, {});
217
+
218
+ fieldType.type.fields.forEach((field: any) => {
219
+ let fieldData: Record<string, any> = {};
220
+
221
+ if (field.inputTypes.length > 1) {
222
+ let nullable = false;
223
+
224
+ const anyOf = field.inputTypes
225
+ .map((inputType: any) => {
226
+ const inputTypeData = this.formatInputTypeData(inputType);
227
+
228
+ if (inputTypeData.type === "null") {
229
+ nullable = true;
230
+
231
+ return;
232
+ }
233
+
234
+ // eslint-disable-next-line consistent-return
235
+ return inputTypeData;
236
+ })
237
+ .filter(Boolean);
238
+
239
+ if (anyOf.length === 1) {
240
+ // eslint-disable-next-line prefer-destructuring
241
+ fieldData = anyOf[0];
242
+ } else {
243
+ fieldData.anyOf = anyOf;
244
+ }
245
+
246
+ if (nullable) {
247
+ fieldData.nullable = true;
248
+ }
249
+ } else {
250
+ const inputType = field.inputTypes[0];
251
+
252
+ fieldData = this.formatInputTypeData(inputType);
253
+ }
254
+
255
+ this.schemaInputTypes.set(fieldType.type.name, {
256
+ ...this.schemaInputTypes.get(fieldType.type.name),
257
+ [field.name]: fieldData,
258
+ });
259
+
260
+ field.inputTypes.forEach((inputType: any) => {
261
+ if (inputType.kind === "object") {
262
+ this.parseObjectInputType(inputType);
263
+ }
264
+ });
265
+ });
266
+ }
267
+
268
+ return { $ref: formatSchemaReference(fieldType.type.name) };
269
+ }
270
+
271
+ return { type: getJSONSchemaScalar(fieldType.type) };
272
+ }
273
+
274
+ // eslint-disable-next-line class-methods-use-this
275
+ public getPaginationDataSchema() {
276
+ return {
277
+ [PAGINATION_SCHEMA_NAME]: {
278
+ type: "object",
279
+ xml: {
280
+ name: PAGINATION_SCHEMA_NAME,
281
+ },
282
+ properties: {
283
+ total: {
284
+ type: "integer",
285
+ minimum: 0,
286
+ description: "Holds the value for the total number of rows in the database",
287
+ },
288
+ perPage: {
289
+ type: "integer",
290
+ minimum: 0,
291
+ description: "Returns the value for the limit passed to the paginate method",
292
+ },
293
+ page: {
294
+ type: "integer",
295
+ minimum: 1,
296
+ description: "Current page number",
297
+ },
298
+ lastPage: {
299
+ type: "integer",
300
+ minimum: 0,
301
+ description: "Returns the value for the last page by taking the total of rows into account",
302
+ },
303
+ firstPage: {
304
+ type: "integer",
305
+ minimum: 0,
306
+ description: "Returns the number for the first page. It is always 1",
307
+ },
308
+ firstPageUrl: {
309
+ type: "string",
310
+ description: "The URL for the first page",
311
+ },
312
+ lastPageUrl: {
313
+ type: "string",
314
+ description: "The URL for the last page",
315
+ },
316
+ nextPageUrl: {
317
+ type: "string",
318
+ description: "The URL for the next page",
319
+ },
320
+ previousPageUrl: {
321
+ type: "string",
322
+ description: "The URL for the previous page",
323
+ },
324
+ },
325
+ },
326
+ };
327
+ }
328
+
329
+ public getExampleModelsSchemas(
330
+ modelNames: string[],
331
+ schemas: {
332
+ [key: string]: OpenAPIV3.SchemaObject;
333
+ },
334
+ ) {
335
+ const referenceToSchema = (reference: string) => {
336
+ const name = reference.replace("#/components/schemas/", "");
337
+ const model = schemas[name] as OpenAPIV3.SchemaObject;
338
+
339
+ const values: { [key: string]: string | object[] } = {};
340
+
341
+ Object.entries((model?.properties as OpenAPIV3.SchemaObject) || {}).forEach(([key, v]) => {
342
+ const type = (v as OpenAPIV3.SchemaObject).type as string;
343
+
344
+ if (type === "array") {
345
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
346
+ values[key] = [arrayItemsToSchema(v.items)];
347
+ } else {
348
+ values[key] = type;
349
+ }
350
+ });
351
+
352
+ return values;
353
+ };
354
+
355
+ const objectPropertiesToSchema = (objectProperties: { [name: string]: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject }) => {
356
+ const values: { [key: string]: string | object | object[] } = {};
357
+
358
+ Object.entries(objectProperties).forEach(([key, value]) => {
359
+ if (typeof (value as OpenAPIV3.ReferenceObject).$ref !== "undefined") {
360
+ values[key] = referenceToSchema((value as OpenAPIV3.ReferenceObject).$ref);
361
+ } else {
362
+ values[key] = (value as OpenAPIV3.SchemaObject).type as string;
363
+ }
364
+ });
365
+
366
+ return values;
367
+ };
368
+
369
+ const arrayItemsToSchema = (items: OpenAPIV3.ArraySchemaObject) => {
370
+ const values: { [key: string]: object | object[] } = {};
371
+
372
+ Object.entries(items).forEach(([key, value]) => {
373
+ if (typeof value.items.$ref !== "undefined") {
374
+ values[key] = [referenceToSchema(value.items.$ref)];
375
+ } else if (value.type === "array") {
376
+ values[key] = [arrayItemsToSchema(value.items)];
377
+ } else if (value.type === "object") {
378
+ values[key] = objectPropertiesToSchema(value.properties);
379
+ } else {
380
+ values[key] = value.type;
381
+ }
382
+ });
383
+
384
+ return values;
385
+ };
386
+
387
+ return modelNames.reduce((accumulator, modelName) => {
388
+ const value: { [key: string]: string | object | object[] } = {};
389
+ const model = schemas[modelName] as OpenAPIV3.SchemaObject;
390
+
391
+ Object.entries(model.properties as OpenAPIV3.SchemaObject).forEach(([key, v]) => {
392
+ const type = (v as OpenAPIV3.SchemaObject).type as string;
393
+
394
+ if (type === "array") {
395
+ value[key] = [referenceToSchema(v.items.$ref)];
396
+ } else if (type === "object") {
397
+ value[key] = objectPropertiesToSchema(v.properties);
398
+ } else {
399
+ value[key] = type;
400
+ }
401
+ });
402
+
403
+ const pagination = this.getPaginationDataSchema()[PAGINATION_SCHEMA_NAME];
404
+ const meta: { [key: string]: string } = {};
405
+
406
+ Object.entries(pagination.properties as OpenAPIV3.SchemaObject).forEach(([key, v]) => {
407
+ meta[key] = (v as OpenAPIV3.SchemaObject).type as string;
408
+ });
409
+
410
+ return {
411
+ ...accumulator,
412
+ [`${modelName}`]: {
413
+ value,
414
+ },
415
+ [`${modelName}Page`]: {
416
+ value: {
417
+ data: [value],
418
+ meta,
419
+ },
420
+ },
421
+ };
422
+ }, {});
423
+ }
424
+
425
+ // eslint-disable-next-line class-methods-use-this
426
+ public getPaginatedModelsSchemas(modelNames: string[]) {
427
+ return modelNames.reduce((accumulator, modelName) => {
428
+ return {
429
+ ...accumulator,
430
+ [`${modelName}Page`]: {
431
+ type: "object",
432
+ xml: {
433
+ name: `${modelName}Page`,
434
+ },
435
+ properties: {
436
+ data: {
437
+ type: "array",
438
+ xml: {
439
+ name: "Data",
440
+ wrapped: true,
441
+ },
442
+ items: {
443
+ $ref: formatSchemaReference(modelName),
444
+ },
445
+ },
446
+ meta: {
447
+ $ref: formatSchemaReference(PAGINATION_SCHEMA_NAME),
448
+ },
449
+ },
450
+ },
451
+ };
452
+ }, {});
453
+ }
454
+ }
455
+
456
+ export default PrismaJsonSchemaParser;