@lobb-js/core 0.13.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 (86) hide show
  1. package/package.json +48 -0
  2. package/src/Lobb.ts +150 -0
  3. package/src/LobbError.ts +105 -0
  4. package/src/TypesGenerator.ts +11 -0
  5. package/src/api/WebServer.ts +126 -0
  6. package/src/api/collections/CollectionControllers.ts +485 -0
  7. package/src/api/collections/CollectionService.ts +162 -0
  8. package/src/api/collections/collectionRoutes.ts +105 -0
  9. package/src/api/collections/collectionStore.ts +647 -0
  10. package/src/api/collections/transactions.ts +166 -0
  11. package/src/api/collections/utils.ts +73 -0
  12. package/src/api/errorHandler.ts +73 -0
  13. package/src/api/events/index.ts +129 -0
  14. package/src/api/meta/route.ts +66 -0
  15. package/src/api/meta/service.ts +163 -0
  16. package/src/api/middlewares.ts +71 -0
  17. package/src/api/openApiRoute.ts +1017 -0
  18. package/src/api/schema/SchemaService.ts +71 -0
  19. package/src/api/schema/schemaRoutes.ts +13 -0
  20. package/src/config/ConfigManager.ts +252 -0
  21. package/src/config/validations.ts +49 -0
  22. package/src/coreCollections/collectionsCollection.ts +56 -0
  23. package/src/coreCollections/index.ts +14 -0
  24. package/src/coreCollections/migrationsCollection.ts +36 -0
  25. package/src/coreCollections/queryCollection.ts +26 -0
  26. package/src/coreCollections/workflowsCollection.ts +73 -0
  27. package/src/coreDbSetup/index.ts +72 -0
  28. package/src/coreMigrations/index.ts +3 -0
  29. package/src/database/DatabaseService.ts +44 -0
  30. package/src/database/DatabaseSyncManager.ts +173 -0
  31. package/src/database/MigrationsManager.ts +95 -0
  32. package/src/database/drivers/MongoDriver.ts +750 -0
  33. package/src/database/drivers/pgDriver/PGDriver.ts +655 -0
  34. package/src/database/drivers/pgDriver/QueryBuilder.ts +474 -0
  35. package/src/database/drivers/pgDriver/utils.ts +6 -0
  36. package/src/events/EventSystem.ts +191 -0
  37. package/src/events/coreEvents/index.ts +218 -0
  38. package/src/events/studioEvents/index.ts +32 -0
  39. package/src/extension/ExtensionSystem.ts +236 -0
  40. package/src/extension/dashboardRoute.ts +35 -0
  41. package/src/fields/ArrayField.ts +33 -0
  42. package/src/fields/BoolField.ts +34 -0
  43. package/src/fields/DateField.ts +13 -0
  44. package/src/fields/DateTimeField.ts +13 -0
  45. package/src/fields/DecimalField.ts +13 -0
  46. package/src/fields/FieldUtils.ts +56 -0
  47. package/src/fields/FloatField.ts +13 -0
  48. package/src/fields/IntegerField.ts +13 -0
  49. package/src/fields/LongField.ts +13 -0
  50. package/src/fields/ObjectField.ts +15 -0
  51. package/src/fields/StringField.ts +13 -0
  52. package/src/fields/TextField.ts +13 -0
  53. package/src/fields/TimeField.ts +13 -0
  54. package/src/index.ts +53 -0
  55. package/src/studio/Studio.ts +108 -0
  56. package/src/types/CollectionControllers.ts +15 -0
  57. package/src/types/DatabaseDriver.ts +115 -0
  58. package/src/types/Extension.ts +46 -0
  59. package/src/types/Field.ts +29 -0
  60. package/src/types/apiSchema.ts +12 -0
  61. package/src/types/collectionServiceSchema.ts +18 -0
  62. package/src/types/config/collectionFields.ts +85 -0
  63. package/src/types/config/collectionsConfig.ts +50 -0
  64. package/src/types/config/config.ts +66 -0
  65. package/src/types/config/relations.ts +17 -0
  66. package/src/types/filterSchema.ts +88 -0
  67. package/src/types/index.ts +38 -0
  68. package/src/types/migrations.ts +12 -0
  69. package/src/types/websockets.ts +34 -0
  70. package/src/types/workflows/processors.ts +1 -0
  71. package/src/utils/lockCollectionToObject.ts +204 -0
  72. package/src/utils/utils.ts +310 -0
  73. package/src/workflows/WorkflowSystem.ts +182 -0
  74. package/src/workflows/coreWorkflows/collectionsTable/index.ts +118 -0
  75. package/src/workflows/coreWorkflows/index.ts +18 -0
  76. package/src/workflows/coreWorkflows/processors/postOperationsWorkflows.ts +46 -0
  77. package/src/workflows/coreWorkflows/processors/preOperationsWorkflows.ts +27 -0
  78. package/src/workflows/coreWorkflows/processors/processorForDB.ts +13 -0
  79. package/src/workflows/coreWorkflows/processors/processors/processor.ts +23 -0
  80. package/src/workflows/coreWorkflows/processors/processors/processorsFunctions.ts +47 -0
  81. package/src/workflows/coreWorkflows/processors/utils.ts +102 -0
  82. package/src/workflows/coreWorkflows/processors/validator/validator.ts +19 -0
  83. package/src/workflows/coreWorkflows/processors/validator/validatorsFunction.ts +52 -0
  84. package/src/workflows/coreWorkflows/queryCoreWorkflows.ts +31 -0
  85. package/src/workflows/coreWorkflows/utilsCoreWorkflows.ts +40 -0
  86. package/src/workflows/coreWorkflows/workflowsCollection/workflowsCollectionWorkflows.ts +101 -0
@@ -0,0 +1,1017 @@
1
+ import type { Context } from "hono";
2
+ import type { OpenAPIV3_1 } from "openapi-types";
3
+
4
+ import { Hono } from "hono";
5
+ import { apiReference } from "@scalar/hono-api-reference";
6
+ import { LobbError } from "../LobbError.ts";
7
+ import { metaRoutePathDescription } from "./meta/route.ts";
8
+ import _ from "lodash";
9
+ import { Lobb } from "../Lobb.ts";
10
+
11
+ export function getOpenApiRoute() {
12
+ const route = new Hono();
13
+ const config = Lobb.instance.configManager.config;
14
+ const apiReferenceAsAny = apiReference as any;
15
+
16
+ route.get(
17
+ "/",
18
+ (c: Context) => {
19
+ const schema: OpenAPIV3_1.Document = {
20
+ openapi: "3.1.0",
21
+ info: config.project.info as any,
22
+ servers: config.project.servers,
23
+ tags: [
24
+ {
25
+ name: "meta",
26
+ description: "Meta data of the lobb API",
27
+ },
28
+ ...generateCollectionsTags(),
29
+ ],
30
+ components: generateCollectionsComponents(),
31
+ paths: generatePaths(),
32
+ };
33
+ return c.json(schema);
34
+ },
35
+ );
36
+
37
+ route.get(
38
+ "/ui",
39
+ apiReferenceAsAny({
40
+ theme: "saturn",
41
+ spec: {
42
+ url: "/api/openapi",
43
+ },
44
+ }),
45
+ );
46
+
47
+ return route;
48
+ }
49
+
50
+ function generateCollectionsTags() {
51
+ const tags: {
52
+ name: string;
53
+ description: string;
54
+ }[] = [];
55
+ const collectionConfig = Lobb.instance.configManager.getCollections();
56
+ for (const [collectionName, collection] of Object.entries(collectionConfig)) {
57
+ tags.push({
58
+ name: collectionName,
59
+ description: (collection as any).description,
60
+ });
61
+ }
62
+
63
+ return tags;
64
+ }
65
+
66
+ function generateCollectionsComponents() {
67
+ const components: any = {
68
+ schemas: {},
69
+ };
70
+ const collectionConfig = Lobb.instance.configManager.getCollections();
71
+ for (const [collectionName, collection] of Object.entries(collectionConfig)) {
72
+ components.schemas[collectionName] = {
73
+ type: "object",
74
+ properties: {},
75
+ };
76
+ for (
77
+ const [fieldName, field] of Object.entries((collection as any).fields)
78
+ ) {
79
+ components.schemas[collectionName]["properties"][fieldName] =
80
+ generateModelProperty(field);
81
+ }
82
+ }
83
+
84
+ components.schemas["internalErrorReponse"] = {
85
+ type: "object",
86
+ properties: {
87
+ status: {
88
+ type: "integer",
89
+ example: 500,
90
+ },
91
+ code: {
92
+ type: "string",
93
+ example: "INTERNAL_ERROR",
94
+ },
95
+ message: {
96
+ type: "string",
97
+ example: "An unexpected error occurred. Please contact support.",
98
+ },
99
+ },
100
+ };
101
+
102
+ const extensionSystem = Lobb.instance.extensionSystem;
103
+ components.schemas = {
104
+ ...components.schemas,
105
+ ...extensionSystem.getExtensionsOpenApiComponents(),
106
+ };
107
+
108
+ return components;
109
+ }
110
+
111
+ function generatePaths() {
112
+ let paths: any = {};
113
+ const collectionConfig = Lobb.instance.configManager.getCollections();
114
+ for (const [collectionName, collection] of Object.entries(collectionConfig)) {
115
+ paths[`/api/collections/${collectionName}/{id}`] = {
116
+ get: {
117
+ tags: [
118
+ collectionName,
119
+ ],
120
+ summary: `Get a record`,
121
+ description: `Get a record from ${collectionName}`,
122
+ parameters: [
123
+ {
124
+ "name": "id",
125
+ "in": "path",
126
+ "required": true,
127
+ "description": `The record ID of ${collectionName}.`,
128
+ "schema": {
129
+ "type": "integer",
130
+ "example": 10,
131
+ },
132
+ },
133
+ ],
134
+ responses: {
135
+ "200": {
136
+ description: "Successful operation",
137
+ content: {
138
+ "application/json": {
139
+ schema: {
140
+ type: "object",
141
+ properties: {
142
+ data: {
143
+ $ref: `#/components/schemas/${collectionName}`,
144
+ },
145
+ },
146
+ },
147
+ },
148
+ },
149
+ },
150
+ "400": {
151
+ description: "Bad Request",
152
+ content: {
153
+ "application/json": {
154
+ schema: {
155
+ type: "object",
156
+ properties: {
157
+ status: {
158
+ type: "integer",
159
+ example: 400,
160
+ },
161
+ code: {
162
+ type: "string",
163
+ example: "BAD_REQUEST",
164
+ },
165
+ message: {
166
+ type: "string",
167
+ example: "Invalid ID format",
168
+ },
169
+ },
170
+ },
171
+ },
172
+ },
173
+ },
174
+ "404": {
175
+ description: "Not found",
176
+ content: {
177
+ "application/json": {
178
+ schema: {
179
+ type: "object",
180
+ properties: {
181
+ status: {
182
+ type: "integer",
183
+ example: 404,
184
+ },
185
+ code: {
186
+ type: "string",
187
+ example: "NOT_FOUND",
188
+ },
189
+ message: {
190
+ type: "string",
191
+ example: "The record is not found",
192
+ },
193
+ },
194
+ },
195
+ },
196
+ },
197
+ },
198
+ "500": {
199
+ description: "Internal Error",
200
+ content: {
201
+ "application/json": {
202
+ schema: {
203
+ $ref: `#/components/schemas/internalErrorReponse`,
204
+ },
205
+ },
206
+ },
207
+ },
208
+ },
209
+ },
210
+ patch: {
211
+ tags: [
212
+ collectionName,
213
+ ],
214
+ summary: `Update a record`,
215
+ description: `Update an existing ${collectionName} by id`,
216
+ parameters: [
217
+ {
218
+ "name": "id",
219
+ "in": "path",
220
+ "required": true,
221
+ "description": "The record ID.",
222
+ "schema": {
223
+ "type": "integer",
224
+ "example": 10,
225
+ },
226
+ },
227
+ ],
228
+ requestBody: {
229
+ required: true,
230
+ content: {
231
+ "application/json": {
232
+ schema: {
233
+ type: "object",
234
+ properties: {
235
+ data: {
236
+ required: true,
237
+ $ref: `#/components/schemas/${collectionName}`,
238
+ },
239
+ },
240
+ },
241
+ },
242
+ },
243
+ },
244
+ responses: {
245
+ "200": {
246
+ description: "Successful operation",
247
+ content: {
248
+ "application/json": {
249
+ schema: {
250
+ type: "object",
251
+ properties: {
252
+ data: {
253
+ $ref: `#/components/schemas/${collectionName}`,
254
+ },
255
+ },
256
+ },
257
+ },
258
+ },
259
+ },
260
+ "400": {
261
+ description: "Bad Request",
262
+ content: {
263
+ "application/json": {
264
+ schema: {
265
+ type: "object",
266
+ properties: {
267
+ status: {
268
+ type: "integer",
269
+ example: 400,
270
+ },
271
+ code: {
272
+ type: "string",
273
+ example: "INCOMPATIBLE_VALUE_TYPE",
274
+ },
275
+ message: {
276
+ type: "string",
277
+ example:
278
+ "The value provided for the (published) field is incompatible.",
279
+ },
280
+ },
281
+ },
282
+ },
283
+ },
284
+ },
285
+ "403": {
286
+ description: "Forbidden",
287
+ content: {
288
+ "application/json": {
289
+ schema: {
290
+ type: "object",
291
+ properties: {
292
+ status: {
293
+ type: "integer",
294
+ example: 403,
295
+ },
296
+ code: {
297
+ type: "string",
298
+ example: "FORBIDDEN",
299
+ },
300
+ message: {
301
+ type: "string",
302
+ example: "Updating the ID proprety is not allowed.",
303
+ },
304
+ },
305
+ },
306
+ },
307
+ },
308
+ },
309
+ "404": {
310
+ description: "Not found",
311
+ content: {
312
+ "application/json": {
313
+ schema: {
314
+ type: "object",
315
+ properties: {
316
+ status: {
317
+ type: "integer",
318
+ example: 404,
319
+ },
320
+ code: {
321
+ type: "string",
322
+ example: "NOT_FOUND",
323
+ },
324
+ message: {
325
+ type: "string",
326
+ example: "The record is not found",
327
+ },
328
+ },
329
+ },
330
+ },
331
+ },
332
+ },
333
+ "500": {
334
+ description: "Internal Error",
335
+ content: {
336
+ "application/json": {
337
+ schema: {
338
+ $ref: `#/components/schemas/internalErrorReponse`,
339
+ },
340
+ },
341
+ },
342
+ },
343
+ },
344
+ },
345
+ delete: {
346
+ tags: [
347
+ collectionName,
348
+ ],
349
+ summary: `Delete a record`,
350
+ description: `Delete a record from ${collectionName}`,
351
+ parameters: [
352
+ {
353
+ "name": "id",
354
+ "in": "path",
355
+ "required": true,
356
+ "description": "The record ID.",
357
+ "schema": {
358
+ "type": "integer",
359
+ "example": 10,
360
+ },
361
+ },
362
+ ],
363
+ responses: {
364
+ "200": {
365
+ description: "Successful operation",
366
+ content: {
367
+ "application/json": {
368
+ schema: {
369
+ type: "object",
370
+ properties: {
371
+ data: {
372
+ $ref: `#/components/schemas/${collectionName}`,
373
+ },
374
+ },
375
+ },
376
+ },
377
+ },
378
+ },
379
+ "400": {
380
+ description: "Bad Request",
381
+ content: {
382
+ "application/json": {
383
+ schema: {
384
+ type: "object",
385
+ properties: {
386
+ status: {
387
+ type: "integer",
388
+ example: 400,
389
+ },
390
+ code: {
391
+ type: "string",
392
+ example: "BAD_REQUEST",
393
+ },
394
+ message: {
395
+ type: "string",
396
+ example: "Invalid ID format",
397
+ },
398
+ },
399
+ },
400
+ },
401
+ },
402
+ },
403
+ "404": {
404
+ description: "Not found",
405
+ content: {
406
+ "application/json": {
407
+ schema: {
408
+ type: "object",
409
+ properties: {
410
+ status: {
411
+ type: "integer",
412
+ example: 404,
413
+ },
414
+ code: {
415
+ type: "string",
416
+ example: "NOT_FOUND",
417
+ },
418
+ message: {
419
+ type: "string",
420
+ example: "The record is not found",
421
+ },
422
+ },
423
+ },
424
+ },
425
+ },
426
+ },
427
+ "500": {
428
+ description: "Internal Error",
429
+ content: {
430
+ "application/json": {
431
+ schema: {
432
+ $ref: `#/components/schemas/internalErrorReponse`,
433
+ },
434
+ },
435
+ },
436
+ },
437
+ },
438
+ },
439
+ };
440
+ paths[`/api/collections/${collectionName}`] = {
441
+ post: {
442
+ tags: [
443
+ collectionName,
444
+ ],
445
+ summary: `Create a record`,
446
+ description: `Add new record to ${collectionName}`,
447
+ requestBody: {
448
+ required: true,
449
+ content: {
450
+ "application/json": {
451
+ schema: {
452
+ type: "object",
453
+ properties: {
454
+ data: {
455
+ required: true,
456
+ $ref: `#/components/schemas/${collectionName}`,
457
+ },
458
+ },
459
+ },
460
+ },
461
+ },
462
+ },
463
+ responses: {
464
+ "201": {
465
+ description: "Successful operation",
466
+ content: {
467
+ "application/json": {
468
+ schema: {
469
+ type: "object",
470
+ properties: {
471
+ data: {
472
+ $ref: `#/components/schemas/${collectionName}`,
473
+ },
474
+ },
475
+ },
476
+ },
477
+ },
478
+ },
479
+ "400": {
480
+ description: "Bad Request",
481
+ content: {
482
+ "application/json": {
483
+ schema: {
484
+ type: "object",
485
+ properties: {
486
+ status: {
487
+ type: "integer",
488
+ example: 400,
489
+ },
490
+ code: {
491
+ type: "string",
492
+ example: "INCOMPATIBLE_VALUE_TYPE",
493
+ },
494
+ message: {
495
+ type: "string",
496
+ example:
497
+ "The value provided for the (published) field is incompatible.",
498
+ },
499
+ },
500
+ },
501
+ },
502
+ },
503
+ },
504
+ "500": {
505
+ description: "Internal Error",
506
+ content: {
507
+ "application/json": {
508
+ schema: {
509
+ $ref: `#/components/schemas/internalErrorReponse`,
510
+ },
511
+ },
512
+ },
513
+ },
514
+ },
515
+ },
516
+ patch: {
517
+ tags: [
518
+ collectionName,
519
+ ],
520
+ summary: `Update many records`,
521
+ description: `Update many records of ${collectionName}`,
522
+ requestBody: {
523
+ required: true,
524
+ content: {
525
+ "application/json": {
526
+ schema: {
527
+ type: "object",
528
+ properties: {
529
+ filter: {
530
+ required: true,
531
+ type: "object",
532
+ description: "Filter criteria object",
533
+ example: {
534
+ "published": {
535
+ $eq: false,
536
+ },
537
+ "title": {
538
+ $eq: "test",
539
+ },
540
+ },
541
+ },
542
+ data: {
543
+ required: true,
544
+ $ref: `#/components/schemas/${collectionName}`,
545
+ },
546
+ },
547
+ },
548
+ },
549
+ },
550
+ },
551
+ responses: {
552
+ "200": {
553
+ description: "Successful operation",
554
+ content: {
555
+ "application/json": {
556
+ schema: {
557
+ type: "object",
558
+ properties: {
559
+ affectedCount: {
560
+ type: "integer",
561
+ example: 11,
562
+ },
563
+ },
564
+ },
565
+ },
566
+ },
567
+ },
568
+ "500": {
569
+ description: "Internal Error",
570
+ content: {
571
+ "application/json": {
572
+ schema: {
573
+ $ref: `#/components/schemas/internalErrorReponse`,
574
+ },
575
+ },
576
+ },
577
+ },
578
+ },
579
+ },
580
+ delete: {
581
+ tags: [
582
+ collectionName,
583
+ ],
584
+ summary: `Delete many records`,
585
+ description: `Delete many records of ${collectionName}`,
586
+ requestBody: {
587
+ required: true,
588
+ content: {
589
+ "application/json": {
590
+ schema: {
591
+ type: "object",
592
+ properties: {
593
+ filter: {
594
+ type: "object",
595
+ description: "Filter criteria object",
596
+ required: true,
597
+ example: {
598
+ "published": {
599
+ $eq: false,
600
+ },
601
+ "title": {
602
+ $eq: "test",
603
+ },
604
+ },
605
+ },
606
+ },
607
+ },
608
+ },
609
+ },
610
+ },
611
+ responses: {
612
+ "200": {
613
+ description: "Successful operation",
614
+ content: {
615
+ "application/json": {
616
+ schema: {
617
+ type: "object",
618
+ properties: {
619
+ affectedCount: {
620
+ type: "integer",
621
+ example: 11,
622
+ },
623
+ },
624
+ },
625
+ },
626
+ },
627
+ },
628
+ "500": {
629
+ description: "Internal Error",
630
+ content: {
631
+ "application/json": {
632
+ schema: {
633
+ $ref: `#/components/schemas/internalErrorReponse`,
634
+ },
635
+ },
636
+ },
637
+ },
638
+ },
639
+ },
640
+ get: {
641
+ tags: [
642
+ collectionName,
643
+ ],
644
+ summary: `Get many records`,
645
+ description: `Get many records of ${collectionName}`,
646
+ parameters: [
647
+ {
648
+ "name": "sort",
649
+ "in": "query",
650
+ "required": false,
651
+ "description":
652
+ "Fields to sort by, separated by commas. Use a minus sign ('-') for descending order. E.g., '-created_at,likes_count'",
653
+ "schema": {
654
+ "type": "string",
655
+ "example": "-created_at,likes_count",
656
+ },
657
+ },
658
+ {
659
+ "name": "limit",
660
+ "in": "query",
661
+ "required": false,
662
+ "description": "The number of records to return",
663
+ "schema": {
664
+ "type": "integer",
665
+ "default": 100,
666
+ "example": 100,
667
+ },
668
+ },
669
+ {
670
+ "name": "offset",
671
+ "in": "query",
672
+ "required": false,
673
+ "description": "The number of records to skip (for pagination)",
674
+ "schema": {
675
+ "type": "integer",
676
+ "default": 0,
677
+ "example": 0,
678
+ },
679
+ },
680
+ {
681
+ "name": "page",
682
+ "in": "query",
683
+ "required": false,
684
+ "description": "The page number for pagination",
685
+ "schema": {
686
+ "type": "integer",
687
+ "example": 1,
688
+ },
689
+ },
690
+ {
691
+ "name": "fields",
692
+ "in": "query",
693
+ "required": false,
694
+ "description":
695
+ "Comma-separated list of fields to include in the response",
696
+ "schema": {
697
+ "type": "string",
698
+ "default": "*",
699
+ "example": "id,title,body",
700
+ },
701
+ },
702
+ {
703
+ "name": "filter",
704
+ "in": "query",
705
+ "required": false,
706
+ "description":
707
+ "Filter criteria object to apply to the records in a query string format",
708
+ "schema": {
709
+ "type": "object",
710
+ "example": "filter[published][$eq]=false&filter[title][$eq]=test",
711
+ },
712
+ },
713
+ ],
714
+ responses: {
715
+ "200": {
716
+ description: "Successful operation",
717
+ content: {
718
+ "application/json": {
719
+ schema: {
720
+ type: "object",
721
+ properties: {
722
+ data: {
723
+ type: "array",
724
+ items: {
725
+ $ref: `#/components/schemas/${collectionName}`,
726
+ },
727
+ },
728
+ meta: {
729
+ type: "object",
730
+ properties: {
731
+ totalCount: {
732
+ type: "integer",
733
+ example: 10,
734
+ },
735
+ },
736
+ },
737
+ },
738
+ },
739
+ },
740
+ },
741
+ },
742
+ "500": {
743
+ description: "Internal Error",
744
+ content: {
745
+ "application/json": {
746
+ schema: {
747
+ $ref: `#/components/schemas/internalErrorReponse`,
748
+ },
749
+ },
750
+ },
751
+ },
752
+ },
753
+ },
754
+ };
755
+ paths[`/api/collections/${collectionName}/search`] = {
756
+ post: {
757
+ tags: [
758
+ collectionName,
759
+ ],
760
+ summary: `Get many records`,
761
+ description: `Get many records of ${collectionName}`,
762
+ requestBody: {
763
+ required: true,
764
+ content: {
765
+ "application/json": {
766
+ schema: {
767
+ type: "object",
768
+ properties: {
769
+ sort: {
770
+ type: "string",
771
+ description:
772
+ "Fields to sort by, separated by commas. Use a minus sign ('-') for descending order. E.g., '-created_at,likes_count'",
773
+ example: "-created_at,likes_count",
774
+ },
775
+ limit: {
776
+ type: "integer",
777
+ description: "The number of records to return",
778
+ default: 100,
779
+ example: 100,
780
+ },
781
+ offset: {
782
+ type: "integer",
783
+ description:
784
+ "The number of records to skip (for pagination)",
785
+ default: 0,
786
+ example: 0,
787
+ },
788
+ page: {
789
+ type: "integer",
790
+ description: "The page number for pagination",
791
+ example: 1,
792
+ },
793
+ fields: {
794
+ type: "string",
795
+ description:
796
+ "Comma-separated list of fields to include in the response",
797
+ default: "*",
798
+ example: "id,title,body",
799
+ },
800
+ filter: {
801
+ type: "object",
802
+ description: "Filter criteria object",
803
+ example: {
804
+ "published": {
805
+ $eq: false,
806
+ },
807
+ "title": {
808
+ $eq: "test",
809
+ },
810
+ },
811
+ },
812
+ },
813
+ },
814
+ },
815
+ },
816
+ },
817
+ responses: {
818
+ "200": {
819
+ description: "Successful operation",
820
+ content: {
821
+ "application/json": {
822
+ schema: {
823
+ type: "object",
824
+ properties: {
825
+ data: {
826
+ type: "array",
827
+ items: {
828
+ $ref: `#/components/schemas/${collectionName}`,
829
+ },
830
+ },
831
+ meta: {
832
+ type: "object",
833
+ properties: {
834
+ totalCount: {
835
+ type: "integer",
836
+ example: 10,
837
+ },
838
+ },
839
+ },
840
+ },
841
+ },
842
+ },
843
+ },
844
+ },
845
+ "500": {
846
+ description: "Internal Error",
847
+ content: {
848
+ "application/json": {
849
+ schema: {
850
+ $ref: `#/components/schemas/internalErrorReponse`,
851
+ },
852
+ },
853
+ },
854
+ },
855
+ },
856
+ },
857
+ };
858
+ paths[`/api/collections/${collectionName}/many`] = {
859
+ post: {
860
+ tags: [
861
+ collectionName,
862
+ ],
863
+ summary: `Create many records`,
864
+ description: `Create many records of ${collectionName}`,
865
+ requestBody: {
866
+ required: true,
867
+ content: {
868
+ "application/json": {
869
+ schema: {
870
+ type: "object",
871
+ properties: {
872
+ data: {
873
+ required: true,
874
+ type: "array",
875
+ items: {
876
+ $ref: `#/components/schemas/${collectionName}`,
877
+ },
878
+ },
879
+ },
880
+ },
881
+ },
882
+ },
883
+ },
884
+ responses: {
885
+ "201": {
886
+ description: "Successful operation",
887
+ content: {
888
+ "application/json": {
889
+ schema: {
890
+ type: "object",
891
+ properties: {
892
+ affectedCount: {
893
+ type: "integer",
894
+ example: 11,
895
+ },
896
+ },
897
+ },
898
+ },
899
+ },
900
+ },
901
+ "500": {
902
+ description: "Internal Error",
903
+ content: {
904
+ "application/json": {
905
+ schema: {
906
+ $ref: `#/components/schemas/internalErrorReponse`,
907
+ },
908
+ },
909
+ },
910
+ },
911
+ },
912
+ },
913
+ };
914
+ }
915
+
916
+ // adding the meta paths
917
+ for (const [path, value] of Object.entries(metaRoutePathDescription)) {
918
+ paths[path] = value;
919
+ }
920
+
921
+ // adding the extensions paths
922
+ const extensionSystem = Lobb.instance.extensionSystem;
923
+ const extensionsPaths = extensionSystem.getExtensionsOpenApiPaths();
924
+ for (const [path, value] of Object.entries(extensionsPaths)) {
925
+ if (paths[path]) {
926
+ _.merge(paths[path], value);
927
+ } else {
928
+ paths[path] = value;
929
+ }
930
+ }
931
+
932
+ _.merge(paths, extensionsPaths);
933
+
934
+ // remove the null routes and the null methods from the paths object
935
+ for (const [path, pathValue] of Object.entries(paths)) {
936
+ if (pathValue === null || pathValue === undefined) {
937
+ delete paths[path];
938
+ continue;
939
+ }
940
+ for (const [methodName, methodValue] of Object.entries(pathValue)) {
941
+ if (methodValue === null) {
942
+ delete paths[path][methodName];
943
+ continue;
944
+ }
945
+ }
946
+ }
947
+
948
+ return paths;
949
+ }
950
+
951
+ function generateModelProperty(field: any) {
952
+ if (field.type === "string") {
953
+ return {
954
+ type: "string",
955
+ example: "example string",
956
+ };
957
+ } else if (field.type === "text") {
958
+ return {
959
+ type: "text",
960
+ example:
961
+ "Officia ad ad amet ex commodo consectetur exercitation minim ipsum laboris.",
962
+ };
963
+ } else if (field.type === "bool") {
964
+ return {
965
+ type: "boolean",
966
+ example: true,
967
+ };
968
+ } else if (field.type === "date") {
969
+ return {
970
+ type: "string",
971
+ format: "date",
972
+ example: "2025-03-17",
973
+ };
974
+ } else if (field.type === "datetime") {
975
+ return {
976
+ type: "string",
977
+ format: "date-time",
978
+ example: "2025-03-17T14:30:00Z",
979
+ };
980
+ } else if (field.type === "time") {
981
+ return {
982
+ type: "string",
983
+ format: "time",
984
+ example: "14:30:00",
985
+ };
986
+ } else if (field.type === "integer") {
987
+ return {
988
+ type: "integer",
989
+ format: "int32",
990
+ example: 10,
991
+ };
992
+ } else if (field.type === "long") {
993
+ return {
994
+ type: "integer",
995
+ format: "int64",
996
+ example: 100,
997
+ };
998
+ } else if (field.type === "float") {
999
+ return {
1000
+ type: "number",
1001
+ format: "float",
1002
+ example: 10.1,
1003
+ };
1004
+ } else if (field.type === "decimal") {
1005
+ return {
1006
+ type: "number",
1007
+ format: "double",
1008
+ example: 10.01,
1009
+ };
1010
+ } else {
1011
+ throw new LobbError({
1012
+ code: "NOT_IMPLEMENTED",
1013
+ message:
1014
+ `The field type of (${field.type}) in openapi specs is not implemented`,
1015
+ });
1016
+ }
1017
+ }