@oneuptime/common 7.0.4349 → 7.0.4372
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/Models/AnalyticsModels/ExceptionInstance.ts +2 -2
- package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.ts +2 -2
- package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.ts +2 -2
- package/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.ts +2 -2
- package/Models/DatabaseModels/OnCallDutyPolicyTimeLog.ts +2 -2
- package/Models/DatabaseModels/Probe.ts +7 -1
- package/Models/DatabaseModels/ServiceCatalog.ts +2 -2
- package/Models/DatabaseModels/ServiceCopilotCodeRepository.ts +2 -2
- package/Models/DatabaseModels/StatusPage.ts +37 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +60 -0
- package/Server/API/StatusPageAPI.ts +104 -10
- package/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/ScheduledMaintenanceService.ts +21 -0
- package/Server/Services/StatusPageSubscriberService.ts +116 -1
- package/Server/Utils/OpenAPI.ts +738 -11
- package/Server/Utils/Workspace/Slack/Slack.ts +14 -0
- package/Utils/Schema/AnalyticsModelSchema.ts +764 -0
- package/Utils/Schema/BaseSchema.ts +450 -0
- package/Utils/Schema/ModelSchema.ts +1099 -38
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +2 -2
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleSchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleTeam.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyEscalationRuleUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +2 -2
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Probe.js +7 -1
- package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceCatalog.js +2 -2
- package/build/dist/Models/DatabaseModels/ServiceCatalog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js +2 -2
- package/build/dist/Models/DatabaseModels/ServiceCopilotCodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +39 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +62 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +73 -10
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +19 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +98 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Utils/OpenAPI.js +578 -11
- package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +15 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Utils/Schema/AnalyticsModelSchema.js +636 -0
- package/build/dist/Utils/Schema/AnalyticsModelSchema.js.map +1 -0
- package/build/dist/Utils/Schema/BaseSchema.js +295 -0
- package/build/dist/Utils/Schema/BaseSchema.js.map +1 -0
- package/build/dist/Utils/Schema/ModelSchema.js +940 -27
- package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,20 +1,56 @@
|
|
|
1
1
|
import z from "./Zod";
|
|
2
2
|
import TableColumnType from "../../Types/Database/TableColumnType";
|
|
3
3
|
import { getTableColumns, } from "../../Types/Database/TableColumn";
|
|
4
|
-
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
5
4
|
import logger from "../../Server/Utils/Logger";
|
|
6
5
|
import Color from "../../Types/Color";
|
|
7
|
-
|
|
6
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
7
|
+
import { PermissionHelper } from "../../Types/Permission";
|
|
8
|
+
import { BaseSchema } from "./BaseSchema";
|
|
9
|
+
export class ModelSchema extends BaseSchema {
|
|
10
|
+
/**
|
|
11
|
+
* Format permissions array into a human-readable string for OpenAPI documentation
|
|
12
|
+
*/
|
|
13
|
+
static formatPermissionsForSchema(permissions) {
|
|
14
|
+
if (!permissions || permissions.length === 0) {
|
|
15
|
+
return "No access - you don't have permission for this operation";
|
|
16
|
+
}
|
|
17
|
+
return PermissionHelper.getPermissionTitles(permissions).join(", ");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Get permissions description for a column to add to OpenAPI schema
|
|
21
|
+
*/
|
|
22
|
+
static getColumnPermissionsDescription(model, key) {
|
|
23
|
+
const accessControl = model.getColumnAccessControlForAllColumns()[key];
|
|
24
|
+
if (!accessControl) {
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
const createPermissions = this.formatPermissionsForSchema(accessControl.create);
|
|
28
|
+
const readPermissions = this.formatPermissionsForSchema(accessControl.read);
|
|
29
|
+
const updatePermissions = this.formatPermissionsForSchema(accessControl.update);
|
|
30
|
+
return `Permissions - Create: [${createPermissions}], Read: [${readPermissions}], Update: [${updatePermissions}]`;
|
|
31
|
+
}
|
|
8
32
|
static getModelSchema(data) {
|
|
9
33
|
const modelType = data.modelType;
|
|
10
34
|
const model = new modelType();
|
|
11
35
|
const columns = getTableColumns(model);
|
|
12
36
|
const shape = {};
|
|
37
|
+
// Get column access control for permission filtering
|
|
38
|
+
const columnAccessControl = model.getColumnAccessControlForAllColumns();
|
|
13
39
|
for (const key in columns) {
|
|
14
40
|
const column = columns[key];
|
|
15
41
|
if (!column) {
|
|
16
42
|
continue;
|
|
17
43
|
}
|
|
44
|
+
// Filter out columns with no permissions (root-only access)
|
|
45
|
+
const accessControl = columnAccessControl[key];
|
|
46
|
+
if (accessControl) {
|
|
47
|
+
// Check if column has any permissions defined for read operation (general schema assumes read access)
|
|
48
|
+
const hasReadPermissions = accessControl.read && accessControl.read.length > 0;
|
|
49
|
+
// If no read permissions are defined, exclude the column from general schema
|
|
50
|
+
if (!hasReadPermissions) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
18
54
|
let zodType;
|
|
19
55
|
if (column.type === TableColumnType.ObjectID) {
|
|
20
56
|
zodType = z.string().openapi({
|
|
@@ -280,6 +316,11 @@ export class ModelSchema {
|
|
|
280
316
|
if (column.title) {
|
|
281
317
|
zodType = zodType.describe(column.title);
|
|
282
318
|
}
|
|
319
|
+
// Add permissions description to the schema
|
|
320
|
+
const permissionsDescription = this.getColumnPermissionsDescription(model, key);
|
|
321
|
+
if (permissionsDescription) {
|
|
322
|
+
zodType = zodType.describe(permissionsDescription);
|
|
323
|
+
}
|
|
283
324
|
shape[key] = zodType;
|
|
284
325
|
}
|
|
285
326
|
const schema = z.object(shape);
|
|
@@ -301,63 +342,935 @@ export class ModelSchema {
|
|
|
301
342
|
}
|
|
302
343
|
static getQueryModelSchema(data) {
|
|
303
344
|
const modelType = data.modelType;
|
|
345
|
+
const model = new modelType();
|
|
346
|
+
return this.generateQuerySchema({
|
|
347
|
+
model,
|
|
348
|
+
tableName: model.tableName || "model",
|
|
349
|
+
getColumns: (model) => {
|
|
350
|
+
const columns = getTableColumns(model);
|
|
351
|
+
return Object.keys(columns)
|
|
352
|
+
.map((key) => {
|
|
353
|
+
const column = columns[key];
|
|
354
|
+
return column ? { key, type: column.type } : null;
|
|
355
|
+
})
|
|
356
|
+
.filter((col) => {
|
|
357
|
+
return col !== null;
|
|
358
|
+
});
|
|
359
|
+
},
|
|
360
|
+
getValidOperatorsForColumnType: (columnType) => {
|
|
361
|
+
return this.getValidOperatorsForColumnType(columnType);
|
|
362
|
+
},
|
|
363
|
+
getOperatorSchema: (operatorType, columnType) => {
|
|
364
|
+
return this.getOperatorSchema(operatorType, columnType);
|
|
365
|
+
},
|
|
366
|
+
getQuerySchemaExample: () => {
|
|
367
|
+
return this.getQuerySchemaExample(modelType);
|
|
368
|
+
},
|
|
369
|
+
getExampleValueForColumn: (columnType) => {
|
|
370
|
+
return this.getExampleValueForColumn(columnType);
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
static getValidOperatorsForColumnType(columnType) {
|
|
375
|
+
const commonOperators = [
|
|
376
|
+
"EqualTo",
|
|
377
|
+
"NotEqual",
|
|
378
|
+
"IsNull",
|
|
379
|
+
"NotNull",
|
|
380
|
+
"EqualToOrNull",
|
|
381
|
+
];
|
|
382
|
+
switch (columnType) {
|
|
383
|
+
case TableColumnType.ObjectID:
|
|
384
|
+
case TableColumnType.Email:
|
|
385
|
+
case TableColumnType.Phone:
|
|
386
|
+
case TableColumnType.HashedString:
|
|
387
|
+
case TableColumnType.Slug:
|
|
388
|
+
return [...commonOperators, "Includes"];
|
|
389
|
+
case TableColumnType.ShortText:
|
|
390
|
+
case TableColumnType.LongText:
|
|
391
|
+
case TableColumnType.VeryLongText:
|
|
392
|
+
case TableColumnType.Name:
|
|
393
|
+
case TableColumnType.Description:
|
|
394
|
+
case TableColumnType.Domain:
|
|
395
|
+
case TableColumnType.Markdown:
|
|
396
|
+
return [...commonOperators, "Search", "Includes"];
|
|
397
|
+
case TableColumnType.Number:
|
|
398
|
+
case TableColumnType.PositiveNumber:
|
|
399
|
+
case TableColumnType.SmallNumber:
|
|
400
|
+
case TableColumnType.SmallPositiveNumber:
|
|
401
|
+
case TableColumnType.BigNumber:
|
|
402
|
+
case TableColumnType.BigPositiveNumber:
|
|
403
|
+
return [
|
|
404
|
+
...commonOperators,
|
|
405
|
+
"GreaterThan",
|
|
406
|
+
"LessThan",
|
|
407
|
+
"GreaterThanOrEqual",
|
|
408
|
+
"LessThanOrEqual",
|
|
409
|
+
"InBetween",
|
|
410
|
+
];
|
|
411
|
+
case TableColumnType.Date:
|
|
412
|
+
return [
|
|
413
|
+
...commonOperators,
|
|
414
|
+
"GreaterThan",
|
|
415
|
+
"LessThan",
|
|
416
|
+
"GreaterThanOrEqual",
|
|
417
|
+
"LessThanOrEqual",
|
|
418
|
+
"InBetween",
|
|
419
|
+
];
|
|
420
|
+
case TableColumnType.Boolean:
|
|
421
|
+
return commonOperators;
|
|
422
|
+
case TableColumnType.JSON:
|
|
423
|
+
case TableColumnType.Array:
|
|
424
|
+
return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
|
|
425
|
+
case TableColumnType.Entity:
|
|
426
|
+
case TableColumnType.EntityArray:
|
|
427
|
+
return ["EqualTo", "NotEqual", "IsNull", "NotNull", "Includes"];
|
|
428
|
+
case TableColumnType.Color:
|
|
429
|
+
case TableColumnType.File:
|
|
430
|
+
case TableColumnType.Buffer:
|
|
431
|
+
return commonOperators;
|
|
432
|
+
default:
|
|
433
|
+
return commonOperators;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
static getOperatorSchema(operatorType, columnType) {
|
|
437
|
+
const baseValue = this.getBaseValueSchemaForColumnType(columnType);
|
|
438
|
+
switch (operatorType) {
|
|
439
|
+
case "EqualTo":
|
|
440
|
+
case "NotEqual":
|
|
441
|
+
case "EqualToOrNull":
|
|
442
|
+
return z.object({
|
|
443
|
+
_type: z.literal(operatorType),
|
|
444
|
+
value: baseValue,
|
|
445
|
+
});
|
|
446
|
+
case "GreaterThan":
|
|
447
|
+
case "LessThan":
|
|
448
|
+
case "GreaterThanOrEqual":
|
|
449
|
+
case "LessThanOrEqual":
|
|
450
|
+
return z.object({
|
|
451
|
+
_type: z.literal(operatorType),
|
|
452
|
+
value: baseValue,
|
|
453
|
+
});
|
|
454
|
+
case "Search":
|
|
455
|
+
return z.object({
|
|
456
|
+
_type: z.literal("Search"),
|
|
457
|
+
value: z.string(),
|
|
458
|
+
});
|
|
459
|
+
case "InBetween":
|
|
460
|
+
return z.object({
|
|
461
|
+
_type: z.literal("InBetween"),
|
|
462
|
+
startValue: baseValue,
|
|
463
|
+
endValue: baseValue,
|
|
464
|
+
});
|
|
465
|
+
case "IsNull":
|
|
466
|
+
case "NotNull":
|
|
467
|
+
return z.object({
|
|
468
|
+
_type: z.literal(operatorType),
|
|
469
|
+
});
|
|
470
|
+
case "Includes":
|
|
471
|
+
return z.object({
|
|
472
|
+
_type: z.literal("Includes"),
|
|
473
|
+
value: z.array(baseValue),
|
|
474
|
+
});
|
|
475
|
+
default:
|
|
476
|
+
return z.object({
|
|
477
|
+
_type: z.literal(operatorType),
|
|
478
|
+
value: baseValue,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
static getBaseValueSchemaForColumnType(columnType) {
|
|
483
|
+
switch (columnType) {
|
|
484
|
+
case TableColumnType.ObjectID:
|
|
485
|
+
return z.string();
|
|
486
|
+
case TableColumnType.Email:
|
|
487
|
+
return z.string().email();
|
|
488
|
+
case TableColumnType.Phone:
|
|
489
|
+
case TableColumnType.HashedString:
|
|
490
|
+
case TableColumnType.Slug:
|
|
491
|
+
case TableColumnType.ShortText:
|
|
492
|
+
case TableColumnType.LongText:
|
|
493
|
+
case TableColumnType.VeryLongText:
|
|
494
|
+
case TableColumnType.Name:
|
|
495
|
+
case TableColumnType.Description:
|
|
496
|
+
case TableColumnType.Domain:
|
|
497
|
+
case TableColumnType.Markdown:
|
|
498
|
+
case TableColumnType.HTML:
|
|
499
|
+
case TableColumnType.JavaScript:
|
|
500
|
+
case TableColumnType.CSS:
|
|
501
|
+
case TableColumnType.LongURL:
|
|
502
|
+
case TableColumnType.ShortURL:
|
|
503
|
+
case TableColumnType.OTP:
|
|
504
|
+
case TableColumnType.Password:
|
|
505
|
+
case TableColumnType.Version:
|
|
506
|
+
return z.string();
|
|
507
|
+
case TableColumnType.Number:
|
|
508
|
+
case TableColumnType.PositiveNumber:
|
|
509
|
+
case TableColumnType.SmallNumber:
|
|
510
|
+
case TableColumnType.SmallPositiveNumber:
|
|
511
|
+
case TableColumnType.BigNumber:
|
|
512
|
+
case TableColumnType.BigPositiveNumber:
|
|
513
|
+
return z.number();
|
|
514
|
+
case TableColumnType.Date:
|
|
515
|
+
return z.date();
|
|
516
|
+
case TableColumnType.Boolean:
|
|
517
|
+
return z.boolean();
|
|
518
|
+
case TableColumnType.JSON:
|
|
519
|
+
case TableColumnType.Array:
|
|
520
|
+
case TableColumnType.Permission:
|
|
521
|
+
case TableColumnType.CustomFieldType:
|
|
522
|
+
return z.any();
|
|
523
|
+
case TableColumnType.Color:
|
|
524
|
+
return Color.getSchema();
|
|
525
|
+
case TableColumnType.Entity:
|
|
526
|
+
return z.string(); // Entity IDs are typically strings
|
|
527
|
+
case TableColumnType.EntityArray:
|
|
528
|
+
return z.array(z.string()); // Array of entity IDs
|
|
529
|
+
case TableColumnType.File:
|
|
530
|
+
case TableColumnType.Buffer:
|
|
531
|
+
return z.any();
|
|
532
|
+
case TableColumnType.MonitorType:
|
|
533
|
+
case TableColumnType.WorkflowStatus:
|
|
534
|
+
return z.string();
|
|
535
|
+
default:
|
|
536
|
+
return z.string();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
static getSortModelSchema(data) {
|
|
540
|
+
const modelType = data.modelType;
|
|
541
|
+
const model = new modelType();
|
|
542
|
+
return this.generateSortSchema({
|
|
543
|
+
model,
|
|
544
|
+
tableName: model.tableName || "model",
|
|
545
|
+
getSortableTypes: () => {
|
|
546
|
+
return this.getSortableTypes();
|
|
547
|
+
},
|
|
548
|
+
getColumnsForSorting: (model) => {
|
|
549
|
+
const columns = getTableColumns(model);
|
|
550
|
+
return Object.keys(columns)
|
|
551
|
+
.map((key) => {
|
|
552
|
+
const column = columns[key];
|
|
553
|
+
return column ? { key, type: column.type } : null;
|
|
554
|
+
})
|
|
555
|
+
.filter((col) => {
|
|
556
|
+
return col !== null;
|
|
557
|
+
});
|
|
558
|
+
},
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
static getSelectModelSchema(data) {
|
|
562
|
+
const modelType = data.modelType;
|
|
563
|
+
const model = new modelType();
|
|
564
|
+
return this.generateSelectSchema({
|
|
565
|
+
model,
|
|
566
|
+
tableName: model.tableName || "model",
|
|
567
|
+
getColumns: (model) => {
|
|
568
|
+
const columns = getTableColumns(model);
|
|
569
|
+
return Object.keys(columns)
|
|
570
|
+
.map((key) => {
|
|
571
|
+
const column = columns[key];
|
|
572
|
+
return column ? { key, type: column.type } : null;
|
|
573
|
+
})
|
|
574
|
+
.filter((col) => {
|
|
575
|
+
return col !== null;
|
|
576
|
+
});
|
|
577
|
+
},
|
|
578
|
+
getSelectSchemaExample: () => {
|
|
579
|
+
return this.getSelectSchemaExample(modelType);
|
|
580
|
+
},
|
|
581
|
+
allowNested: !data.isNested,
|
|
582
|
+
getNestedSchema: (key, model) => {
|
|
583
|
+
const columns = getTableColumns(model);
|
|
584
|
+
const column = columns[key];
|
|
585
|
+
if (column &&
|
|
586
|
+
column.modelType &&
|
|
587
|
+
(column.type === TableColumnType.EntityArray ||
|
|
588
|
+
column.type === TableColumnType.Entity)) {
|
|
589
|
+
return this.getSelectModelSchema({
|
|
590
|
+
modelType: column.modelType,
|
|
591
|
+
isNested: true,
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
return null;
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
static getGroupByModelSchema(data) {
|
|
599
|
+
const modelType = data.modelType;
|
|
600
|
+
const model = new modelType();
|
|
601
|
+
return this.generateGroupBySchema({
|
|
602
|
+
model,
|
|
603
|
+
tableName: model.tableName || "model",
|
|
604
|
+
getColumns: (model) => {
|
|
605
|
+
const columns = getTableColumns(model);
|
|
606
|
+
return Object.keys(columns)
|
|
607
|
+
.map((key) => {
|
|
608
|
+
const column = columns[key];
|
|
609
|
+
return column ? { key, type: column.type } : null;
|
|
610
|
+
})
|
|
611
|
+
.filter((col) => {
|
|
612
|
+
return col !== null;
|
|
613
|
+
});
|
|
614
|
+
},
|
|
615
|
+
getGroupableTypes: () => {
|
|
616
|
+
return [
|
|
617
|
+
TableColumnType.ShortText,
|
|
618
|
+
TableColumnType.LongText,
|
|
619
|
+
TableColumnType.Name,
|
|
620
|
+
TableColumnType.Email,
|
|
621
|
+
TableColumnType.Slug,
|
|
622
|
+
TableColumnType.ObjectID,
|
|
623
|
+
TableColumnType.Boolean,
|
|
624
|
+
TableColumnType.Date,
|
|
625
|
+
TableColumnType.Number,
|
|
626
|
+
TableColumnType.PositiveNumber,
|
|
627
|
+
TableColumnType.SmallNumber,
|
|
628
|
+
TableColumnType.SmallPositiveNumber,
|
|
629
|
+
TableColumnType.BigNumber,
|
|
630
|
+
TableColumnType.BigPositiveNumber,
|
|
631
|
+
];
|
|
632
|
+
},
|
|
633
|
+
getGroupBySchemaExample: () => {
|
|
634
|
+
return this.getGroupBySchemaExample(modelType);
|
|
635
|
+
},
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
static getExampleValueForColumn(columnType, isSecondValue = false) {
|
|
639
|
+
switch (columnType) {
|
|
640
|
+
case TableColumnType.ObjectID:
|
|
641
|
+
return isSecondValue
|
|
642
|
+
? "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
|
|
643
|
+
: "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
|
|
644
|
+
case TableColumnType.Email:
|
|
645
|
+
return isSecondValue ? "jane@example.com" : "john@example.com";
|
|
646
|
+
case TableColumnType.Phone:
|
|
647
|
+
return isSecondValue ? "+1-555-987-6543" : "+1-555-123-4567";
|
|
648
|
+
case TableColumnType.Number:
|
|
649
|
+
case TableColumnType.PositiveNumber:
|
|
650
|
+
return isSecondValue ? 100 : 50;
|
|
651
|
+
case TableColumnType.SmallNumber:
|
|
652
|
+
return isSecondValue ? 20 : 10;
|
|
653
|
+
case TableColumnType.SmallPositiveNumber:
|
|
654
|
+
return isSecondValue ? 25 : 15;
|
|
655
|
+
case TableColumnType.BigNumber:
|
|
656
|
+
return isSecondValue ? 2000000 : 1000000;
|
|
657
|
+
case TableColumnType.BigPositiveNumber:
|
|
658
|
+
return isSecondValue ? 2500000 : 1500000;
|
|
659
|
+
case TableColumnType.Date:
|
|
660
|
+
return isSecondValue
|
|
661
|
+
? "2023-12-31T23:59:59.000Z"
|
|
662
|
+
: "2023-01-01T00:00:00.000Z";
|
|
663
|
+
case TableColumnType.Boolean:
|
|
664
|
+
return !isSecondValue;
|
|
665
|
+
case TableColumnType.ShortText:
|
|
666
|
+
case TableColumnType.Name:
|
|
667
|
+
return isSecondValue ? "Jane Doe" : "John Doe";
|
|
668
|
+
case TableColumnType.Description:
|
|
669
|
+
return isSecondValue
|
|
670
|
+
? "Second example description"
|
|
671
|
+
: "Example description";
|
|
672
|
+
case TableColumnType.Domain:
|
|
673
|
+
return isSecondValue ? "example.org" : "example.com";
|
|
674
|
+
default:
|
|
675
|
+
return isSecondValue ? "example_value_2" : "example_value_1";
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
static getQuerySchemaExample(modelType) {
|
|
304
679
|
const model = new modelType();
|
|
305
680
|
const columns = getTableColumns(model);
|
|
306
|
-
const
|
|
681
|
+
const example = {};
|
|
682
|
+
let exampleCount = 0;
|
|
683
|
+
const maxExamples = 3;
|
|
307
684
|
for (const key in columns) {
|
|
685
|
+
if (exampleCount >= maxExamples) {
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
308
688
|
const column = columns[key];
|
|
309
689
|
if (!column) {
|
|
310
690
|
continue;
|
|
311
691
|
}
|
|
312
|
-
|
|
692
|
+
const validOperators = this.getValidOperatorsForColumnType(column.type);
|
|
693
|
+
if (validOperators.length === 0) {
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
// Add example based on column type and available operators
|
|
697
|
+
if (column.type === TableColumnType.ShortText ||
|
|
698
|
+
column.type === TableColumnType.Name) {
|
|
699
|
+
if (validOperators.includes("EqualTo")) {
|
|
700
|
+
example[key] = { _type: "EqualTo", value: "Example Text" };
|
|
701
|
+
exampleCount++;
|
|
702
|
+
}
|
|
703
|
+
else if (validOperators.includes("Search")) {
|
|
704
|
+
example[key] = { _type: "Search", value: "example" };
|
|
705
|
+
exampleCount++;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
else if (column.type === TableColumnType.Email &&
|
|
709
|
+
validOperators.includes("EqualTo")) {
|
|
710
|
+
example[key] = { _type: "EqualTo", value: "user@example.com" };
|
|
711
|
+
exampleCount++;
|
|
712
|
+
}
|
|
713
|
+
else if (column.type === TableColumnType.Date &&
|
|
714
|
+
validOperators.includes("GreaterThan")) {
|
|
715
|
+
example[key] = {
|
|
716
|
+
_type: "GreaterThan",
|
|
717
|
+
value: "2023-01-01T00:00:00.000Z",
|
|
718
|
+
};
|
|
719
|
+
exampleCount++;
|
|
720
|
+
}
|
|
721
|
+
else if (column.type === TableColumnType.Boolean &&
|
|
722
|
+
validOperators.includes("EqualTo")) {
|
|
723
|
+
example[key] = { _type: "EqualTo", value: true };
|
|
724
|
+
exampleCount++;
|
|
725
|
+
}
|
|
726
|
+
else if ((column.type === TableColumnType.Number ||
|
|
727
|
+
column.type === TableColumnType.PositiveNumber) &&
|
|
728
|
+
validOperators.includes("GreaterThan")) {
|
|
729
|
+
example[key] = { _type: "GreaterThan", value: 10 };
|
|
730
|
+
exampleCount++;
|
|
731
|
+
}
|
|
732
|
+
else if (validOperators.includes("EqualTo")) {
|
|
733
|
+
example[key] = {
|
|
734
|
+
_type: "EqualTo",
|
|
735
|
+
value: this.getExampleValueForColumn(column.type),
|
|
736
|
+
};
|
|
737
|
+
exampleCount++;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
// If no examples were added, add a generic one
|
|
741
|
+
if (exampleCount === 0) {
|
|
742
|
+
example["_id"] = {
|
|
743
|
+
_type: "EqualTo",
|
|
744
|
+
value: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
return example;
|
|
748
|
+
}
|
|
749
|
+
static getSelectSchemaExample(modelType) {
|
|
750
|
+
if (!modelType) {
|
|
751
|
+
throw new BadDataException("Model type is required to generate select schema example.");
|
|
752
|
+
}
|
|
753
|
+
const model = new modelType();
|
|
754
|
+
const columns = getTableColumns(model);
|
|
755
|
+
const example = {};
|
|
756
|
+
// Add common fields that most models have
|
|
757
|
+
const commonFields = ["_id", "createdAt", "updatedAt"];
|
|
758
|
+
for (const field of commonFields) {
|
|
759
|
+
if (columns[field]) {
|
|
760
|
+
example[field] = true;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
// Add first few non-common fields as examples
|
|
764
|
+
let fieldCount = 0;
|
|
765
|
+
const maxFields = 5;
|
|
766
|
+
for (const key in columns) {
|
|
767
|
+
if (fieldCount >= maxFields) {
|
|
768
|
+
break;
|
|
769
|
+
}
|
|
770
|
+
const column = columns[key];
|
|
771
|
+
if (!column || commonFields.includes(key)) {
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
// Prioritize fields that are likely to be commonly selected
|
|
775
|
+
if (column.type === TableColumnType.ShortText ||
|
|
776
|
+
column.type === TableColumnType.Name ||
|
|
777
|
+
column.type === TableColumnType.Email ||
|
|
778
|
+
column.type === TableColumnType.Description) {
|
|
779
|
+
example[key] = true;
|
|
780
|
+
fieldCount++;
|
|
781
|
+
}
|
|
313
782
|
}
|
|
314
|
-
return
|
|
783
|
+
return example;
|
|
315
784
|
}
|
|
316
|
-
static
|
|
317
|
-
|
|
785
|
+
static getGroupBySchemaExample(modelType) {
|
|
786
|
+
if (!modelType) {
|
|
787
|
+
throw new BadDataException("Model type is required to generate group by schema example.");
|
|
788
|
+
}
|
|
318
789
|
const model = new modelType();
|
|
319
790
|
const columns = getTableColumns(model);
|
|
320
|
-
|
|
791
|
+
// Find the first suitable field for grouping
|
|
321
792
|
for (const key in columns) {
|
|
322
793
|
const column = columns[key];
|
|
323
794
|
if (!column) {
|
|
324
795
|
continue;
|
|
325
796
|
}
|
|
326
|
-
|
|
327
|
-
if (
|
|
797
|
+
// Prioritize common groupable fields
|
|
798
|
+
if (column.type === TableColumnType.ShortText ||
|
|
799
|
+
column.type === TableColumnType.Name ||
|
|
800
|
+
column.type === TableColumnType.Boolean ||
|
|
801
|
+
column.type === TableColumnType.Date) {
|
|
802
|
+
return { [key]: true };
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
// Fallback to any available field
|
|
806
|
+
for (const key in columns) {
|
|
807
|
+
const column = columns[key];
|
|
808
|
+
if (!column) {
|
|
328
809
|
continue;
|
|
329
810
|
}
|
|
330
|
-
|
|
331
|
-
.
|
|
332
|
-
.
|
|
811
|
+
const isGroupable = [
|
|
812
|
+
TableColumnType.ShortText,
|
|
813
|
+
TableColumnType.LongText,
|
|
814
|
+
TableColumnType.Name,
|
|
815
|
+
TableColumnType.Email,
|
|
816
|
+
TableColumnType.Slug,
|
|
817
|
+
TableColumnType.ObjectID,
|
|
818
|
+
TableColumnType.Boolean,
|
|
819
|
+
TableColumnType.Date,
|
|
820
|
+
TableColumnType.Number,
|
|
821
|
+
TableColumnType.PositiveNumber,
|
|
822
|
+
TableColumnType.SmallNumber,
|
|
823
|
+
TableColumnType.SmallPositiveNumber,
|
|
824
|
+
TableColumnType.BigNumber,
|
|
825
|
+
TableColumnType.BigPositiveNumber,
|
|
826
|
+
].includes(column.type);
|
|
827
|
+
if (isGroupable) {
|
|
828
|
+
return { [key]: true };
|
|
829
|
+
}
|
|
333
830
|
}
|
|
334
|
-
|
|
831
|
+
// Final fallback
|
|
832
|
+
return { status: true };
|
|
335
833
|
}
|
|
336
|
-
|
|
834
|
+
// Shared method to build model schemas with different field exclusions
|
|
835
|
+
static buildModelSchema(data) {
|
|
337
836
|
const modelType = data.modelType;
|
|
338
837
|
const model = new modelType();
|
|
339
838
|
const columns = getTableColumns(model);
|
|
340
839
|
const shape = {};
|
|
840
|
+
// Get column access control for permission filtering
|
|
841
|
+
const columnAccessControl = model.getColumnAccessControlForAllColumns();
|
|
341
842
|
for (const key in columns) {
|
|
342
843
|
const column = columns[key];
|
|
343
844
|
if (!column) {
|
|
344
845
|
continue;
|
|
345
846
|
}
|
|
346
|
-
//
|
|
347
|
-
if (
|
|
348
|
-
column.modelType &&
|
|
349
|
-
(column.type === TableColumnType.EntityArray ||
|
|
350
|
-
column.type === TableColumnType.Entity)) {
|
|
351
|
-
// can only do one level of nesting
|
|
352
|
-
shape[key] = this.getSelectModelSchema({
|
|
353
|
-
modelType: column.modelType,
|
|
354
|
-
isNested: true,
|
|
355
|
-
});
|
|
847
|
+
// Skip excluded fields
|
|
848
|
+
if (data.excludedFields && data.excludedFields.includes(key)) {
|
|
356
849
|
continue;
|
|
357
850
|
}
|
|
358
|
-
|
|
851
|
+
// Only include specified fields if includedFields is provided
|
|
852
|
+
if (data.includedFields && !data.includedFields.includes(key)) {
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
// Filter out columns with no permissions (root-only access)
|
|
856
|
+
const accessControl = columnAccessControl[key];
|
|
857
|
+
if (accessControl) {
|
|
858
|
+
let hasPermissions = false;
|
|
859
|
+
// Check if column has any permissions defined for the current operation
|
|
860
|
+
if (data.schemaType === "create" &&
|
|
861
|
+
accessControl.create &&
|
|
862
|
+
accessControl.create.length > 0) {
|
|
863
|
+
hasPermissions = true;
|
|
864
|
+
}
|
|
865
|
+
else if (data.schemaType === "read" &&
|
|
866
|
+
accessControl.read &&
|
|
867
|
+
accessControl.read.length > 0) {
|
|
868
|
+
hasPermissions = true;
|
|
869
|
+
}
|
|
870
|
+
else if (data.schemaType === "update" &&
|
|
871
|
+
accessControl.update &&
|
|
872
|
+
accessControl.update.length > 0) {
|
|
873
|
+
hasPermissions = true;
|
|
874
|
+
}
|
|
875
|
+
else if (data.schemaType === "delete") {
|
|
876
|
+
// For delete operations, we don't filter by column permissions
|
|
877
|
+
hasPermissions = true;
|
|
878
|
+
}
|
|
879
|
+
// If no permissions are defined for this operation, exclude the column
|
|
880
|
+
if (!hasPermissions) {
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
let zodType = this.getZodTypeForColumn(column, key, data.schemaType);
|
|
885
|
+
// Make fields optional if specified
|
|
886
|
+
if (data.makeOptional) {
|
|
887
|
+
zodType = zodType.optional();
|
|
888
|
+
}
|
|
889
|
+
// Add title and description to the schema
|
|
890
|
+
if (column.title) {
|
|
891
|
+
zodType = zodType.describe(column.title);
|
|
892
|
+
}
|
|
893
|
+
shape[key] = zodType;
|
|
894
|
+
}
|
|
895
|
+
const schema = z.object(shape).openapi({
|
|
896
|
+
type: "object",
|
|
897
|
+
description: `${data.description} schema for ${model.tableName || "model"} model. ${data.description}`,
|
|
898
|
+
example: data.example,
|
|
899
|
+
additionalProperties: false,
|
|
900
|
+
});
|
|
901
|
+
logger.debug(`${data.schemaType} model schema for ${model.tableName} created with shape: ${JSON.stringify(shape, null, 2)}`);
|
|
902
|
+
return schema;
|
|
903
|
+
}
|
|
904
|
+
// Helper method to get Zod type for a column
|
|
905
|
+
static getZodTypeForColumn(column, key, schemaType) {
|
|
906
|
+
let zodType;
|
|
907
|
+
if (column.type === TableColumnType.ObjectID) {
|
|
908
|
+
zodType = z.string().openapi({
|
|
909
|
+
type: "string",
|
|
910
|
+
example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
else if (column.type === TableColumnType.Color) {
|
|
914
|
+
zodType = Color.getSchema();
|
|
915
|
+
}
|
|
916
|
+
else if (column.type === TableColumnType.Date) {
|
|
917
|
+
zodType = z.date().openapi({
|
|
918
|
+
type: "string",
|
|
919
|
+
format: "date-time",
|
|
920
|
+
example: "2023-01-15T12:30:00.000Z",
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
else if (column.type === TableColumnType.VeryLongText) {
|
|
924
|
+
zodType = z.string().openapi({
|
|
925
|
+
type: "string",
|
|
926
|
+
example: "This is an example of very long text content that might be stored in this field. It can contain a lot of information, such as detailed descriptions, comments, or any other lengthy text data that needs to be stored in the database.",
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
else if (column.type === TableColumnType.Number ||
|
|
930
|
+
column.type === TableColumnType.PositiveNumber) {
|
|
931
|
+
zodType = z.number().openapi({ type: "number", example: 42 });
|
|
932
|
+
}
|
|
933
|
+
else if (column.type === TableColumnType.Email) {
|
|
934
|
+
zodType = z.string().email().openapi({
|
|
935
|
+
type: "string",
|
|
936
|
+
format: "email",
|
|
937
|
+
example: "user@example.com",
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
else if (column.type === TableColumnType.HashedString) {
|
|
941
|
+
zodType = z
|
|
942
|
+
.string()
|
|
943
|
+
.openapi({ type: "string", example: "hashed_string_value" });
|
|
944
|
+
}
|
|
945
|
+
else if (column.type === TableColumnType.Slug) {
|
|
946
|
+
zodType = z
|
|
947
|
+
.string()
|
|
948
|
+
.openapi({ type: "string", example: "example-slug-value" });
|
|
949
|
+
}
|
|
950
|
+
else if (column.type === TableColumnType.ShortText) {
|
|
951
|
+
zodType = z
|
|
952
|
+
.string()
|
|
953
|
+
.openapi({ type: "string", example: "Example short text" });
|
|
954
|
+
}
|
|
955
|
+
else if (column.type === TableColumnType.LongText) {
|
|
956
|
+
zodType = z.string().openapi({
|
|
957
|
+
type: "string",
|
|
958
|
+
example: "This is an example of longer text content that might be stored in this field.",
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
else if (column.type === TableColumnType.Phone) {
|
|
962
|
+
zodType = z
|
|
963
|
+
.string()
|
|
964
|
+
.openapi({ type: "string", example: "+1-555-123-4567" });
|
|
965
|
+
}
|
|
966
|
+
else if (column.type === TableColumnType.Version) {
|
|
967
|
+
zodType = z.string().openapi({
|
|
968
|
+
type: "string",
|
|
969
|
+
example: "1.0.0",
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
else if (column.type === TableColumnType.Password) {
|
|
973
|
+
zodType = z.string().openapi({
|
|
974
|
+
type: "string",
|
|
975
|
+
format: "password",
|
|
976
|
+
example: "••••••••",
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
else if (column.type === TableColumnType.Name) {
|
|
980
|
+
zodType = z.string().openapi({
|
|
981
|
+
type: "string",
|
|
982
|
+
example: "John Doe",
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
else if (column.type === TableColumnType.Description) {
|
|
986
|
+
zodType = z.string().openapi({
|
|
987
|
+
type: "string",
|
|
988
|
+
example: "This is a description of the item",
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
else if (column.type === TableColumnType.File) {
|
|
992
|
+
zodType = z.any().openapi({
|
|
993
|
+
type: "string",
|
|
994
|
+
format: "binary",
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
else if (column.type === TableColumnType.Buffer) {
|
|
998
|
+
zodType = z.any().openapi({
|
|
999
|
+
type: "string",
|
|
1000
|
+
format: "binary",
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
else if (column.type === TableColumnType.ShortURL) {
|
|
1004
|
+
zodType = z.string().url().openapi({
|
|
1005
|
+
type: "string",
|
|
1006
|
+
example: "https://short.url/abc123",
|
|
1007
|
+
});
|
|
359
1008
|
}
|
|
360
|
-
|
|
1009
|
+
else if (column.type === TableColumnType.Markdown) {
|
|
1010
|
+
zodType = z.string().openapi({
|
|
1011
|
+
type: "string",
|
|
1012
|
+
example: "# Heading\n\nThis is **markdown** content",
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
else if (column.type === TableColumnType.Domain) {
|
|
1016
|
+
zodType = z.string().openapi({
|
|
1017
|
+
type: "string",
|
|
1018
|
+
example: "example.com",
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
else if (column.type === TableColumnType.LongURL) {
|
|
1022
|
+
zodType = z.string().url().openapi({
|
|
1023
|
+
type: "string",
|
|
1024
|
+
example: "https://www.example.com/path/to/resource?param=value",
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
else if (column.type === TableColumnType.OTP) {
|
|
1028
|
+
zodType = z.string().openapi({
|
|
1029
|
+
type: "string",
|
|
1030
|
+
example: "123456",
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
else if (column.type === TableColumnType.HTML) {
|
|
1034
|
+
zodType = z.string().openapi({
|
|
1035
|
+
type: "string",
|
|
1036
|
+
example: "<div><h1>Title</h1><p>Content</p></div>",
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
else if (column.type === TableColumnType.JavaScript) {
|
|
1040
|
+
zodType = z.string().openapi({
|
|
1041
|
+
type: "string",
|
|
1042
|
+
example: "function example() { return true; }",
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
else if (column.type === TableColumnType.CSS) {
|
|
1046
|
+
zodType = z.string().openapi({
|
|
1047
|
+
type: "string",
|
|
1048
|
+
example: "body { color: #333; margin: 0; }",
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
else if (column.type === TableColumnType.Array) {
|
|
1052
|
+
zodType = z.array(z.any()).openapi({
|
|
1053
|
+
type: "array",
|
|
1054
|
+
items: {
|
|
1055
|
+
type: "string",
|
|
1056
|
+
},
|
|
1057
|
+
example: ["item1", "item2", "item3"],
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
else if (column.type === TableColumnType.SmallPositiveNumber) {
|
|
1061
|
+
zodType = z.number().int().nonnegative().openapi({
|
|
1062
|
+
type: "integer",
|
|
1063
|
+
example: 5,
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
else if (column.type === TableColumnType.BigPositiveNumber) {
|
|
1067
|
+
zodType = z.number().nonnegative().openapi({
|
|
1068
|
+
type: "number",
|
|
1069
|
+
example: 1000000,
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
else if (column.type === TableColumnType.SmallNumber) {
|
|
1073
|
+
zodType = z.number().int().openapi({
|
|
1074
|
+
type: "integer",
|
|
1075
|
+
example: 10,
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
else if (column.type === TableColumnType.BigNumber) {
|
|
1079
|
+
zodType = z.number().openapi({
|
|
1080
|
+
type: "number",
|
|
1081
|
+
example: 1000000,
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
else if (column.type === TableColumnType.Permission) {
|
|
1085
|
+
zodType = z.any().openapi({
|
|
1086
|
+
type: "object",
|
|
1087
|
+
example: { read: true, write: false, delete: false },
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
else if (column.type === TableColumnType.CustomFieldType) {
|
|
1091
|
+
zodType = z.any().openapi({
|
|
1092
|
+
type: "object",
|
|
1093
|
+
example: { type: "text", required: true },
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
else if (column.type === TableColumnType.MonitorType) {
|
|
1097
|
+
zodType = z.string().openapi({
|
|
1098
|
+
type: "string",
|
|
1099
|
+
example: "HTTP",
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
else if (column.type === TableColumnType.WorkflowStatus) {
|
|
1103
|
+
zodType = z.string().openapi({
|
|
1104
|
+
type: "string",
|
|
1105
|
+
example: "In Progress",
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
else if (column.type === TableColumnType.Boolean) {
|
|
1109
|
+
zodType = z.boolean().openapi({ type: "boolean", example: true });
|
|
1110
|
+
}
|
|
1111
|
+
else if (column.type === TableColumnType.JSON) {
|
|
1112
|
+
zodType = z.any().openapi({
|
|
1113
|
+
type: "object",
|
|
1114
|
+
example: { key: "value", nested: { data: 123 } },
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
else if (column.type === TableColumnType.EntityArray) {
|
|
1118
|
+
const entityArrayType = column.modelType;
|
|
1119
|
+
if (!entityArrayType) {
|
|
1120
|
+
logger.debug(`Entity type is not defined for column ${key}`);
|
|
1121
|
+
return z.any().openapi({ type: "null", example: null });
|
|
1122
|
+
}
|
|
1123
|
+
// Use the appropriate schema method based on the operation type
|
|
1124
|
+
let schemaMethod;
|
|
1125
|
+
switch (schemaType) {
|
|
1126
|
+
case "create":
|
|
1127
|
+
schemaMethod = ModelSchema.getCreateModelSchema;
|
|
1128
|
+
break;
|
|
1129
|
+
case "read":
|
|
1130
|
+
schemaMethod = ModelSchema.getReadModelSchema;
|
|
1131
|
+
break;
|
|
1132
|
+
case "update":
|
|
1133
|
+
schemaMethod = ModelSchema.getUpdateModelSchema;
|
|
1134
|
+
break;
|
|
1135
|
+
case "delete":
|
|
1136
|
+
schemaMethod = ModelSchema.getDeleteModelSchema;
|
|
1137
|
+
break;
|
|
1138
|
+
}
|
|
1139
|
+
zodType = z
|
|
1140
|
+
.array(z.lazy(() => {
|
|
1141
|
+
return schemaMethod({
|
|
1142
|
+
modelType: entityArrayType,
|
|
1143
|
+
});
|
|
1144
|
+
}))
|
|
1145
|
+
.openapi({
|
|
1146
|
+
type: "array",
|
|
1147
|
+
items: {
|
|
1148
|
+
type: "object",
|
|
1149
|
+
properties: {
|
|
1150
|
+
id: { type: "string" },
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
example: [{ id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }],
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
else if (column.type === TableColumnType.Entity) {
|
|
1157
|
+
const entityType = column.modelType;
|
|
1158
|
+
if (!entityType) {
|
|
1159
|
+
logger.debug(`Entity type is not defined for column ${key}`);
|
|
1160
|
+
return z.any().openapi({ type: "null", example: null });
|
|
1161
|
+
}
|
|
1162
|
+
// Use the appropriate schema method based on the operation type
|
|
1163
|
+
let schema;
|
|
1164
|
+
switch (schemaType) {
|
|
1165
|
+
case "create":
|
|
1166
|
+
schema = ModelSchema.getCreateModelSchema({ modelType: entityType });
|
|
1167
|
+
break;
|
|
1168
|
+
case "read":
|
|
1169
|
+
schema = ModelSchema.getReadModelSchema({ modelType: entityType });
|
|
1170
|
+
break;
|
|
1171
|
+
case "update":
|
|
1172
|
+
schema = ModelSchema.getUpdateModelSchema({ modelType: entityType });
|
|
1173
|
+
break;
|
|
1174
|
+
case "delete":
|
|
1175
|
+
schema = ModelSchema.getDeleteModelSchema({ modelType: entityType });
|
|
1176
|
+
break;
|
|
1177
|
+
}
|
|
1178
|
+
zodType = z
|
|
1179
|
+
.lazy(() => {
|
|
1180
|
+
return schema;
|
|
1181
|
+
})
|
|
1182
|
+
.openapi({
|
|
1183
|
+
type: "object",
|
|
1184
|
+
example: { id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" },
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
else {
|
|
1188
|
+
zodType = z.any().openapi({ type: "null", example: null });
|
|
1189
|
+
}
|
|
1190
|
+
return zodType;
|
|
1191
|
+
}
|
|
1192
|
+
static getCreateModelSchema(data) {
|
|
1193
|
+
// Auto-generated fields to exclude from create schema
|
|
1194
|
+
const excludedFields = [
|
|
1195
|
+
"_id",
|
|
1196
|
+
"createdAt",
|
|
1197
|
+
"updatedAt",
|
|
1198
|
+
"deletedAt",
|
|
1199
|
+
"version",
|
|
1200
|
+
];
|
|
1201
|
+
return this.buildModelSchema({
|
|
1202
|
+
modelType: data.modelType,
|
|
1203
|
+
excludedFields,
|
|
1204
|
+
schemaType: "create",
|
|
1205
|
+
description: "Create",
|
|
1206
|
+
example: this.getCreateSchemaExample(),
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
static getReadModelSchema(data) {
|
|
1210
|
+
// For read operations, include all fields
|
|
1211
|
+
return this.buildModelSchema({
|
|
1212
|
+
modelType: data.modelType,
|
|
1213
|
+
schemaType: "read",
|
|
1214
|
+
description: "Read",
|
|
1215
|
+
example: this.getReadSchemaExample(),
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
static getUpdateModelSchema(data) {
|
|
1219
|
+
// Auto-generated fields to exclude from update schema (but allow _id for identification)
|
|
1220
|
+
const excludedFields = [
|
|
1221
|
+
"createdAt",
|
|
1222
|
+
"updatedAt",
|
|
1223
|
+
"deletedAt",
|
|
1224
|
+
"version",
|
|
1225
|
+
];
|
|
1226
|
+
return this.buildModelSchema({
|
|
1227
|
+
modelType: data.modelType,
|
|
1228
|
+
excludedFields,
|
|
1229
|
+
schemaType: "update",
|
|
1230
|
+
description: "Update",
|
|
1231
|
+
example: this.getUpdateSchemaExample(),
|
|
1232
|
+
makeOptional: true, // All fields are optional for updates
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
static getDeleteModelSchema(data) {
|
|
1236
|
+
// For delete, we typically only need the ID
|
|
1237
|
+
const includedFields = ["_id"];
|
|
1238
|
+
return this.buildModelSchema({
|
|
1239
|
+
modelType: data.modelType,
|
|
1240
|
+
includedFields,
|
|
1241
|
+
schemaType: "delete",
|
|
1242
|
+
description: "Delete",
|
|
1243
|
+
example: this.getDeleteSchemaExample(),
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
static getCreateSchemaExample() {
|
|
1247
|
+
return {
|
|
1248
|
+
name: "John Doe",
|
|
1249
|
+
email: "john@example.com",
|
|
1250
|
+
description: "Example user description",
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
static getReadSchemaExample() {
|
|
1254
|
+
return {
|
|
1255
|
+
_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
1256
|
+
name: "John Doe",
|
|
1257
|
+
email: "john@example.com",
|
|
1258
|
+
description: "Example user description",
|
|
1259
|
+
createdAt: "2023-01-15T12:30:00.000Z",
|
|
1260
|
+
updatedAt: "2023-01-15T12:30:00.000Z",
|
|
1261
|
+
version: 1,
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
static getUpdateSchemaExample() {
|
|
1265
|
+
return {
|
|
1266
|
+
name: "Jane Doe",
|
|
1267
|
+
email: "jane@example.com",
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
static getDeleteSchemaExample() {
|
|
1271
|
+
return {
|
|
1272
|
+
_id: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
1273
|
+
};
|
|
361
1274
|
}
|
|
362
1275
|
}
|
|
363
1276
|
//# sourceMappingURL=ModelSchema.js.map
|