@oneuptime/common 7.0.4358 → 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/Server/Utils/OpenAPI.ts +564 -2
- package/Utils/Schema/AnalyticsModelSchema.ts +764 -0
- package/Utils/Schema/BaseSchema.ts +450 -0
- package/Utils/Schema/ModelSchema.ts +176 -407
- 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/Server/Utils/OpenAPI.js +445 -2
- package/build/dist/Server/Utils/OpenAPI.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 +155 -337
- package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
import z, { ZodSchema } from "./Zod";
|
|
2
|
+
import TableColumnType from "../../Types/AnalyticsDatabase/TableColumnType";
|
|
3
|
+
import AnalyticsTableColumn from "../../Types/AnalyticsDatabase/TableColumn";
|
|
4
|
+
import AnalyticsBaseModel from "../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
|
5
|
+
import logger from "../../Server/Utils/Logger";
|
|
6
|
+
import { z as ZodTypes } from "zod";
|
|
7
|
+
import { BaseSchema, SchemaExample, ShapeRecord } from "./BaseSchema";
|
|
8
|
+
|
|
9
|
+
export type AnalyticsModelSchemaType = ZodSchema;
|
|
10
|
+
|
|
11
|
+
export class AnalyticsModelSchema extends BaseSchema {
|
|
12
|
+
public static getModelSchema(data: {
|
|
13
|
+
modelType: new () => AnalyticsBaseModel;
|
|
14
|
+
}): AnalyticsModelSchemaType {
|
|
15
|
+
const modelType: new () => AnalyticsBaseModel = data.modelType;
|
|
16
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
17
|
+
|
|
18
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
19
|
+
|
|
20
|
+
// Filter out columns with no read permissions
|
|
21
|
+
const filteredColumns: Array<AnalyticsTableColumn> = columns.filter(
|
|
22
|
+
(column: AnalyticsTableColumn) => {
|
|
23
|
+
const accessControl: any = model.getColumnAccessControlFor(column.key);
|
|
24
|
+
if (!accessControl) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
const readPermissions: Array<string> = accessControl.read;
|
|
28
|
+
return readPermissions && readPermissions.length > 0;
|
|
29
|
+
},
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const shape: ShapeRecord = {};
|
|
33
|
+
|
|
34
|
+
for (const column of filteredColumns) {
|
|
35
|
+
const key: string = column.key;
|
|
36
|
+
let zodType: ZodTypes.ZodTypeAny;
|
|
37
|
+
|
|
38
|
+
if (column.type === TableColumnType.ObjectID) {
|
|
39
|
+
zodType = z.string().openapi({
|
|
40
|
+
type: "string",
|
|
41
|
+
example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
42
|
+
});
|
|
43
|
+
} else if (column.type === TableColumnType.Date) {
|
|
44
|
+
zodType = z.date().openapi({
|
|
45
|
+
type: "string",
|
|
46
|
+
format: "date-time",
|
|
47
|
+
example: "2023-01-15T12:30:00.000Z",
|
|
48
|
+
});
|
|
49
|
+
} else if (column.type === TableColumnType.Text) {
|
|
50
|
+
zodType = z.string().openapi({
|
|
51
|
+
type: "string",
|
|
52
|
+
example: "Example text value",
|
|
53
|
+
});
|
|
54
|
+
} else if (column.type === TableColumnType.Number) {
|
|
55
|
+
zodType = z.number().openapi({ type: "number", example: 42 });
|
|
56
|
+
} else if (column.type === TableColumnType.LongNumber) {
|
|
57
|
+
zodType = z.number().openapi({
|
|
58
|
+
type: "number",
|
|
59
|
+
example: 1000000,
|
|
60
|
+
});
|
|
61
|
+
} else if (column.type === TableColumnType.Boolean) {
|
|
62
|
+
zodType = z.boolean().openapi({ type: "boolean", example: true });
|
|
63
|
+
} else if (column.type === TableColumnType.JSON) {
|
|
64
|
+
zodType = z.any().openapi({
|
|
65
|
+
type: "object",
|
|
66
|
+
example: { key: "value", nested: { data: 123 } },
|
|
67
|
+
});
|
|
68
|
+
} else if (column.type === TableColumnType.JSONArray) {
|
|
69
|
+
zodType = z.array(z.any()).openapi({
|
|
70
|
+
type: "array",
|
|
71
|
+
items: {
|
|
72
|
+
type: "object",
|
|
73
|
+
},
|
|
74
|
+
example: [{ key: "value" }, { key2: "value2" }],
|
|
75
|
+
});
|
|
76
|
+
} else if (column.type === TableColumnType.Decimal) {
|
|
77
|
+
zodType = z.number().openapi({
|
|
78
|
+
type: "number",
|
|
79
|
+
example: 123.45,
|
|
80
|
+
});
|
|
81
|
+
} else if (column.type === TableColumnType.ArrayNumber) {
|
|
82
|
+
zodType = z.array(z.number()).openapi({
|
|
83
|
+
type: "array",
|
|
84
|
+
items: {
|
|
85
|
+
type: "number",
|
|
86
|
+
},
|
|
87
|
+
example: [1, 2, 3, 4, 5],
|
|
88
|
+
});
|
|
89
|
+
} else if (column.type === TableColumnType.ArrayText) {
|
|
90
|
+
zodType = z.array(z.string()).openapi({
|
|
91
|
+
type: "array",
|
|
92
|
+
items: {
|
|
93
|
+
type: "string",
|
|
94
|
+
},
|
|
95
|
+
example: ["item1", "item2", "item3"],
|
|
96
|
+
});
|
|
97
|
+
} else if (
|
|
98
|
+
column.type === TableColumnType.NestedModel &&
|
|
99
|
+
column.nestedModel
|
|
100
|
+
) {
|
|
101
|
+
// Handle nested models recursively
|
|
102
|
+
const nestedShape: ShapeRecord = {};
|
|
103
|
+
for (const nestedColumn of column.nestedModel.tableColumns) {
|
|
104
|
+
const nestedZodType: ZodTypes.ZodTypeAny =
|
|
105
|
+
this.getZodTypeForColumn(nestedColumn);
|
|
106
|
+
if (nestedColumn.required) {
|
|
107
|
+
nestedShape[nestedColumn.key] = nestedZodType;
|
|
108
|
+
} else {
|
|
109
|
+
nestedShape[nestedColumn.key] = nestedZodType.optional();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
zodType = z.object(nestedShape).openapi({
|
|
113
|
+
type: "object",
|
|
114
|
+
example: this.generateNestedModelExample(
|
|
115
|
+
column.nestedModel.tableColumns,
|
|
116
|
+
),
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
// Default fallback
|
|
120
|
+
zodType = z.any().openapi({
|
|
121
|
+
type: "string",
|
|
122
|
+
example: "example_value",
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (column.required) {
|
|
127
|
+
// leave as is
|
|
128
|
+
} else {
|
|
129
|
+
zodType = zodType.optional();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add title and description to the schema
|
|
133
|
+
if (column.title) {
|
|
134
|
+
zodType = zodType.describe(column.title);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
shape[key] = zodType;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const schema: AnalyticsModelSchemaType = z.object(shape);
|
|
141
|
+
|
|
142
|
+
logger.debug(
|
|
143
|
+
`Analytics model schema for ${model.tableName} created with shape keys: ${Object.keys(shape).join(", ")} (filtered ${columns.length - filteredColumns.length} columns without read permissions)`,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return schema;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private static getZodTypeForColumn(
|
|
150
|
+
column: AnalyticsTableColumn,
|
|
151
|
+
): ZodTypes.ZodTypeAny {
|
|
152
|
+
switch (column.type) {
|
|
153
|
+
case TableColumnType.ObjectID:
|
|
154
|
+
return z.string().openapi({
|
|
155
|
+
type: "string",
|
|
156
|
+
example: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
157
|
+
});
|
|
158
|
+
case TableColumnType.Date:
|
|
159
|
+
return z.date().openapi({
|
|
160
|
+
type: "string",
|
|
161
|
+
format: "date-time",
|
|
162
|
+
example: "2023-01-15T12:30:00.000Z",
|
|
163
|
+
});
|
|
164
|
+
case TableColumnType.Text:
|
|
165
|
+
return z.string().openapi({
|
|
166
|
+
type: "string",
|
|
167
|
+
example: "Example text",
|
|
168
|
+
});
|
|
169
|
+
case TableColumnType.Number:
|
|
170
|
+
return z.number().openapi({ type: "number", example: 42 });
|
|
171
|
+
case TableColumnType.LongNumber:
|
|
172
|
+
return z.number().openapi({
|
|
173
|
+
type: "number",
|
|
174
|
+
example: 1000000,
|
|
175
|
+
});
|
|
176
|
+
case TableColumnType.Boolean:
|
|
177
|
+
return z.boolean().openapi({ type: "boolean", example: true });
|
|
178
|
+
case TableColumnType.JSON:
|
|
179
|
+
return z.any().openapi({
|
|
180
|
+
type: "object",
|
|
181
|
+
example: { key: "value" },
|
|
182
|
+
});
|
|
183
|
+
case TableColumnType.JSONArray:
|
|
184
|
+
return z.array(z.any()).openapi({
|
|
185
|
+
type: "array",
|
|
186
|
+
items: { type: "object" },
|
|
187
|
+
example: [{ key: "value" }],
|
|
188
|
+
});
|
|
189
|
+
case TableColumnType.Decimal:
|
|
190
|
+
return z.number().openapi({
|
|
191
|
+
type: "number",
|
|
192
|
+
example: 123.45,
|
|
193
|
+
});
|
|
194
|
+
case TableColumnType.ArrayNumber:
|
|
195
|
+
return z.array(z.number()).openapi({
|
|
196
|
+
type: "array",
|
|
197
|
+
items: { type: "number" },
|
|
198
|
+
example: [1, 2, 3],
|
|
199
|
+
});
|
|
200
|
+
case TableColumnType.ArrayText:
|
|
201
|
+
return z.array(z.string()).openapi({
|
|
202
|
+
type: "array",
|
|
203
|
+
items: { type: "string" },
|
|
204
|
+
example: ["item1", "item2"],
|
|
205
|
+
});
|
|
206
|
+
default:
|
|
207
|
+
return z.any().openapi({
|
|
208
|
+
type: "string",
|
|
209
|
+
example: "example_value",
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private static generateNestedModelExample(
|
|
215
|
+
columns: Array<AnalyticsTableColumn>,
|
|
216
|
+
): Record<string, unknown> {
|
|
217
|
+
const example: Record<string, unknown> = {};
|
|
218
|
+
for (const column of columns) {
|
|
219
|
+
switch (column.type) {
|
|
220
|
+
case TableColumnType.ObjectID:
|
|
221
|
+
example[column.key] = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
|
222
|
+
break;
|
|
223
|
+
case TableColumnType.Date:
|
|
224
|
+
example[column.key] = "2023-01-15T12:30:00.000Z";
|
|
225
|
+
break;
|
|
226
|
+
case TableColumnType.Text:
|
|
227
|
+
example[column.key] = "Example text";
|
|
228
|
+
break;
|
|
229
|
+
case TableColumnType.Number:
|
|
230
|
+
example[column.key] = 42;
|
|
231
|
+
break;
|
|
232
|
+
case TableColumnType.Boolean:
|
|
233
|
+
example[column.key] = true;
|
|
234
|
+
break;
|
|
235
|
+
case TableColumnType.JSON:
|
|
236
|
+
example[column.key] = { key: "value" };
|
|
237
|
+
break;
|
|
238
|
+
case TableColumnType.ArrayText:
|
|
239
|
+
example[column.key] = ["item1", "item2"];
|
|
240
|
+
break;
|
|
241
|
+
case TableColumnType.ArrayNumber:
|
|
242
|
+
example[column.key] = [1, 2, 3];
|
|
243
|
+
break;
|
|
244
|
+
default:
|
|
245
|
+
example[column.key] = "example_value";
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return example;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public static getCreateModelSchema(data: {
|
|
252
|
+
modelType: new () => AnalyticsBaseModel;
|
|
253
|
+
}): AnalyticsModelSchemaType {
|
|
254
|
+
const modelType: new () => AnalyticsBaseModel = data.modelType;
|
|
255
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
256
|
+
|
|
257
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
258
|
+
const shape: ShapeRecord = {};
|
|
259
|
+
|
|
260
|
+
// Exclude system fields from create schema
|
|
261
|
+
const excludedFields: Array<string> = ["_id", "createdAt", "updatedAt"];
|
|
262
|
+
|
|
263
|
+
for (const column of columns) {
|
|
264
|
+
const key: string = column.key;
|
|
265
|
+
|
|
266
|
+
if (excludedFields.includes(key)) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Skip default value columns in create schema
|
|
271
|
+
if (column.isDefaultValueColumn) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Filter out columns with no create permissions
|
|
276
|
+
const accessControl: any = model.getColumnAccessControlFor(column.key);
|
|
277
|
+
if (!accessControl) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const createPermissions: Array<string> = accessControl.create;
|
|
281
|
+
if (!createPermissions || createPermissions.length === 0) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const zodType: ZodTypes.ZodTypeAny = this.getZodTypeForColumn(column);
|
|
286
|
+
|
|
287
|
+
if (column.required) {
|
|
288
|
+
shape[key] = zodType;
|
|
289
|
+
} else {
|
|
290
|
+
shape[key] = zodType.optional();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return z.object(shape).openapi({
|
|
295
|
+
type: "object",
|
|
296
|
+
description: `Create schema for ${model.tableName || "analytics model"}`,
|
|
297
|
+
example: this.getCreateSchemaExample(modelType),
|
|
298
|
+
additionalProperties: false,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
public static getQueryModelSchema(data: {
|
|
303
|
+
modelType: new () => AnalyticsBaseModel;
|
|
304
|
+
}): AnalyticsModelSchemaType {
|
|
305
|
+
const modelType: new () => AnalyticsBaseModel = data.modelType;
|
|
306
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
307
|
+
|
|
308
|
+
return this.generateQuerySchema({
|
|
309
|
+
model,
|
|
310
|
+
tableName: model.tableName || "analytics_model",
|
|
311
|
+
getColumns: (model: AnalyticsBaseModel) => {
|
|
312
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
313
|
+
return columns.map((column: AnalyticsTableColumn) => {
|
|
314
|
+
return { key: column.key, type: column.type };
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
getValidOperatorsForColumnType: (columnType: TableColumnType) => {
|
|
318
|
+
return this.getValidOperatorsForColumnType(columnType);
|
|
319
|
+
},
|
|
320
|
+
getOperatorSchema: (
|
|
321
|
+
operatorType: string,
|
|
322
|
+
columnType: TableColumnType,
|
|
323
|
+
) => {
|
|
324
|
+
return this.getOperatorSchema(operatorType, columnType);
|
|
325
|
+
},
|
|
326
|
+
getQuerySchemaExample: () => {
|
|
327
|
+
return this.getQuerySchemaExample(modelType);
|
|
328
|
+
},
|
|
329
|
+
getExampleValueForColumn: (columnType: TableColumnType) => {
|
|
330
|
+
return this.getExampleValueForColumn(columnType);
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
public static getSelectModelSchema(data: {
|
|
336
|
+
modelType: new () => AnalyticsBaseModel;
|
|
337
|
+
}): AnalyticsModelSchemaType {
|
|
338
|
+
const modelType: new () => AnalyticsBaseModel = data.modelType;
|
|
339
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
340
|
+
|
|
341
|
+
return this.generateSelectSchema({
|
|
342
|
+
model,
|
|
343
|
+
tableName: model.tableName || "analytics_model",
|
|
344
|
+
getColumns: (model: AnalyticsBaseModel) => {
|
|
345
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
346
|
+
return columns.map((column: AnalyticsTableColumn) => {
|
|
347
|
+
return { key: column.key, type: column.type };
|
|
348
|
+
});
|
|
349
|
+
},
|
|
350
|
+
getSelectSchemaExample: () => {
|
|
351
|
+
return this.getSelectSchemaExample(modelType);
|
|
352
|
+
},
|
|
353
|
+
allowNested: false, // Analytics models typically don't have nested schemas
|
|
354
|
+
getNestedSchema: () => {
|
|
355
|
+
return null;
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
public static getSortModelSchema(data: {
|
|
361
|
+
modelType: new () => AnalyticsBaseModel;
|
|
362
|
+
}): AnalyticsModelSchemaType {
|
|
363
|
+
const modelType: new () => AnalyticsBaseModel = data.modelType;
|
|
364
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
365
|
+
|
|
366
|
+
return this.generateSortSchema({
|
|
367
|
+
model,
|
|
368
|
+
tableName: model.tableName || "analytics_model",
|
|
369
|
+
getSortableTypes: () => {
|
|
370
|
+
return this.getSortableTypes();
|
|
371
|
+
},
|
|
372
|
+
getColumnsForSorting: (model: AnalyticsBaseModel) => {
|
|
373
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
374
|
+
return columns.map((column: AnalyticsTableColumn) => {
|
|
375
|
+
return { key: column.key, type: column.type };
|
|
376
|
+
});
|
|
377
|
+
},
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
public static getGroupByModelSchema(data: {
|
|
382
|
+
modelType: new () => AnalyticsBaseModel;
|
|
383
|
+
}): AnalyticsModelSchemaType {
|
|
384
|
+
const modelType: new () => AnalyticsBaseModel = data.modelType;
|
|
385
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
386
|
+
|
|
387
|
+
return this.generateGroupBySchema({
|
|
388
|
+
model,
|
|
389
|
+
tableName: model.tableName || "analytics_model",
|
|
390
|
+
getColumns: (model: AnalyticsBaseModel) => {
|
|
391
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
392
|
+
return columns.map((column: AnalyticsTableColumn) => {
|
|
393
|
+
return { key: column.key, type: column.type };
|
|
394
|
+
});
|
|
395
|
+
},
|
|
396
|
+
getGroupableTypes: () => {
|
|
397
|
+
return [
|
|
398
|
+
TableColumnType.Text,
|
|
399
|
+
TableColumnType.ObjectID,
|
|
400
|
+
TableColumnType.Boolean,
|
|
401
|
+
TableColumnType.Date,
|
|
402
|
+
TableColumnType.Number,
|
|
403
|
+
];
|
|
404
|
+
},
|
|
405
|
+
getGroupBySchemaExample: () => {
|
|
406
|
+
return this.getGroupBySchemaExample(modelType);
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private static getSortableTypes(): Array<TableColumnType> {
|
|
412
|
+
return [
|
|
413
|
+
TableColumnType.Text,
|
|
414
|
+
TableColumnType.Number,
|
|
415
|
+
TableColumnType.LongNumber,
|
|
416
|
+
TableColumnType.Date,
|
|
417
|
+
TableColumnType.Boolean,
|
|
418
|
+
TableColumnType.ObjectID,
|
|
419
|
+
TableColumnType.Decimal,
|
|
420
|
+
];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
private static getValidOperatorsForColumnType(
|
|
424
|
+
columnType: TableColumnType,
|
|
425
|
+
): Array<string> {
|
|
426
|
+
switch (columnType) {
|
|
427
|
+
case TableColumnType.Text:
|
|
428
|
+
return ["EqualTo", "NotEqual", "Search", "IsNull", "NotNull"];
|
|
429
|
+
case TableColumnType.Number:
|
|
430
|
+
case TableColumnType.LongNumber:
|
|
431
|
+
case TableColumnType.Decimal:
|
|
432
|
+
return [
|
|
433
|
+
"EqualTo",
|
|
434
|
+
"NotEqual",
|
|
435
|
+
"GreaterThan",
|
|
436
|
+
"LessThan",
|
|
437
|
+
"GreaterThanOrEqual",
|
|
438
|
+
"LessThanOrEqual",
|
|
439
|
+
"IsNull",
|
|
440
|
+
"NotNull",
|
|
441
|
+
];
|
|
442
|
+
case TableColumnType.Date:
|
|
443
|
+
return [
|
|
444
|
+
"EqualTo",
|
|
445
|
+
"NotEqual",
|
|
446
|
+
"GreaterThan",
|
|
447
|
+
"LessThan",
|
|
448
|
+
"GreaterThanOrEqual",
|
|
449
|
+
"LessThanOrEqual",
|
|
450
|
+
"IsNull",
|
|
451
|
+
"NotNull",
|
|
452
|
+
];
|
|
453
|
+
case TableColumnType.Boolean:
|
|
454
|
+
return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
|
|
455
|
+
case TableColumnType.ObjectID:
|
|
456
|
+
return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
|
|
457
|
+
case TableColumnType.JSON:
|
|
458
|
+
case TableColumnType.JSONArray:
|
|
459
|
+
return ["IsNull", "NotNull"];
|
|
460
|
+
case TableColumnType.ArrayText:
|
|
461
|
+
case TableColumnType.ArrayNumber:
|
|
462
|
+
return ["IsNull", "NotNull"];
|
|
463
|
+
default:
|
|
464
|
+
return ["EqualTo", "NotEqual", "IsNull", "NotNull"];
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private static getExampleValueForColumn(
|
|
469
|
+
columnType: TableColumnType,
|
|
470
|
+
): unknown {
|
|
471
|
+
switch (columnType) {
|
|
472
|
+
case TableColumnType.ObjectID:
|
|
473
|
+
return "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
|
|
474
|
+
case TableColumnType.Text:
|
|
475
|
+
return "example text";
|
|
476
|
+
case TableColumnType.Number:
|
|
477
|
+
case TableColumnType.LongNumber:
|
|
478
|
+
return 42;
|
|
479
|
+
case TableColumnType.Decimal:
|
|
480
|
+
return 123.45;
|
|
481
|
+
case TableColumnType.Date:
|
|
482
|
+
return "2023-01-15T12:30:00.000Z";
|
|
483
|
+
case TableColumnType.Boolean:
|
|
484
|
+
return true;
|
|
485
|
+
case TableColumnType.JSON:
|
|
486
|
+
return { key: "value" };
|
|
487
|
+
case TableColumnType.JSONArray:
|
|
488
|
+
return [{ key: "value" }];
|
|
489
|
+
case TableColumnType.ArrayText:
|
|
490
|
+
return ["item1", "item2"];
|
|
491
|
+
case TableColumnType.ArrayNumber:
|
|
492
|
+
return [1, 2, 3];
|
|
493
|
+
default:
|
|
494
|
+
return "example_value";
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private static getCreateSchemaExample(
|
|
499
|
+
modelType: new () => AnalyticsBaseModel,
|
|
500
|
+
): SchemaExample {
|
|
501
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
502
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
503
|
+
const example: SchemaExample = {};
|
|
504
|
+
|
|
505
|
+
let exampleCount: number = 0;
|
|
506
|
+
const maxExamples: number = 3;
|
|
507
|
+
|
|
508
|
+
for (const column of columns) {
|
|
509
|
+
if (exampleCount >= maxExamples) {
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Skip system fields and default value columns
|
|
514
|
+
if (
|
|
515
|
+
["_id", "createdAt", "updatedAt"].includes(column.key) ||
|
|
516
|
+
column.isDefaultValueColumn
|
|
517
|
+
) {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Skip columns with no create permissions
|
|
522
|
+
const accessControl: any = model.getColumnAccessControlFor(column.key);
|
|
523
|
+
if (!accessControl) {
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
const createPermissions: Array<string> = accessControl.create;
|
|
527
|
+
if (!createPermissions || createPermissions.length === 0) {
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
example[column.key] = this.getExampleValueForColumn(column.type);
|
|
532
|
+
exampleCount++;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return example;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
private static getQuerySchemaExample(
|
|
539
|
+
modelType: new () => AnalyticsBaseModel,
|
|
540
|
+
): SchemaExample {
|
|
541
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
542
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
543
|
+
const example: SchemaExample = {};
|
|
544
|
+
|
|
545
|
+
let exampleCount: number = 0;
|
|
546
|
+
const maxExamples: number = 2;
|
|
547
|
+
|
|
548
|
+
for (const column of columns) {
|
|
549
|
+
if (exampleCount >= maxExamples) {
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Check read permissions for query operations
|
|
554
|
+
const accessControl: any = model.getColumnAccessControlFor(column.key);
|
|
555
|
+
if (
|
|
556
|
+
!accessControl ||
|
|
557
|
+
!accessControl.read ||
|
|
558
|
+
accessControl.read.length === 0
|
|
559
|
+
) {
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
const validOperators: Array<string> = this.getValidOperatorsForColumnType(
|
|
564
|
+
column.type,
|
|
565
|
+
);
|
|
566
|
+
if (validOperators.length === 0) {
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (column.type === TableColumnType.Text) {
|
|
571
|
+
example[column.key] = {
|
|
572
|
+
_type: "EqualTo",
|
|
573
|
+
value: "example text",
|
|
574
|
+
};
|
|
575
|
+
exampleCount++;
|
|
576
|
+
} else if (column.type === TableColumnType.Date) {
|
|
577
|
+
example[column.key] = {
|
|
578
|
+
_type: "GreaterThan",
|
|
579
|
+
value: "2023-01-01T00:00:00.000Z",
|
|
580
|
+
};
|
|
581
|
+
exampleCount++;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return example;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
private static getSelectSchemaExample(
|
|
589
|
+
modelType: new () => AnalyticsBaseModel,
|
|
590
|
+
): SchemaExample {
|
|
591
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
592
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
593
|
+
|
|
594
|
+
// Add common fields (only if they have read permissions)
|
|
595
|
+
const example: SchemaExample = {};
|
|
596
|
+
|
|
597
|
+
// Check if _id has read permissions
|
|
598
|
+
const idAccessControl: any = model.getColumnAccessControlFor("_id");
|
|
599
|
+
if (
|
|
600
|
+
idAccessControl &&
|
|
601
|
+
idAccessControl.read &&
|
|
602
|
+
idAccessControl.read.length > 0
|
|
603
|
+
) {
|
|
604
|
+
example["_id"] = true;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Check if createdAt has read permissions
|
|
608
|
+
const createdAtAccessControl: any =
|
|
609
|
+
model.getColumnAccessControlFor("createdAt");
|
|
610
|
+
if (
|
|
611
|
+
createdAtAccessControl &&
|
|
612
|
+
createdAtAccessControl.read &&
|
|
613
|
+
createdAtAccessControl.read.length > 0
|
|
614
|
+
) {
|
|
615
|
+
example["createdAt"] = true;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Add first few non-system fields with read permissions
|
|
619
|
+
let fieldCount: number = 0;
|
|
620
|
+
const maxFields: number = 3;
|
|
621
|
+
|
|
622
|
+
for (const column of columns) {
|
|
623
|
+
if (fieldCount >= maxFields) {
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (!["_id", "createdAt", "updatedAt"].includes(column.key)) {
|
|
628
|
+
// Check read permissions
|
|
629
|
+
const accessControl: any = model.getColumnAccessControlFor(column.key);
|
|
630
|
+
if (
|
|
631
|
+
accessControl &&
|
|
632
|
+
accessControl.read &&
|
|
633
|
+
accessControl.read.length > 0
|
|
634
|
+
) {
|
|
635
|
+
example[column.key] = true;
|
|
636
|
+
fieldCount++;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return example;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
private static getGroupBySchemaExample(
|
|
645
|
+
modelType: new () => AnalyticsBaseModel,
|
|
646
|
+
): SchemaExample {
|
|
647
|
+
const model: AnalyticsBaseModel = new modelType();
|
|
648
|
+
const columns: Array<AnalyticsTableColumn> = model.getTableColumns();
|
|
649
|
+
|
|
650
|
+
// Find first suitable field for grouping with read permissions
|
|
651
|
+
for (const column of columns) {
|
|
652
|
+
const isGroupable: boolean = [
|
|
653
|
+
TableColumnType.Text,
|
|
654
|
+
TableColumnType.ObjectID,
|
|
655
|
+
TableColumnType.Boolean,
|
|
656
|
+
TableColumnType.Date,
|
|
657
|
+
TableColumnType.Number,
|
|
658
|
+
].includes(column.type);
|
|
659
|
+
|
|
660
|
+
if (
|
|
661
|
+
isGroupable &&
|
|
662
|
+
!["_id", "createdAt", "updatedAt"].includes(column.key)
|
|
663
|
+
) {
|
|
664
|
+
// Check read permissions
|
|
665
|
+
const accessControl: any = model.getColumnAccessControlFor(column.key);
|
|
666
|
+
if (
|
|
667
|
+
accessControl &&
|
|
668
|
+
accessControl.read &&
|
|
669
|
+
accessControl.read.length > 0
|
|
670
|
+
) {
|
|
671
|
+
return { [column.key]: true };
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Fallback to createdAt if it has read permissions
|
|
677
|
+
const createdAtAccessControl: any =
|
|
678
|
+
model.getColumnAccessControlFor("createdAt");
|
|
679
|
+
if (
|
|
680
|
+
createdAtAccessControl &&
|
|
681
|
+
createdAtAccessControl.read &&
|
|
682
|
+
createdAtAccessControl.read.length > 0
|
|
683
|
+
) {
|
|
684
|
+
return { ["createdAt"]: true };
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Final fallback - return empty object if no columns have read permissions
|
|
688
|
+
return {};
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
private static getOperatorSchema(
|
|
692
|
+
operatorType: string,
|
|
693
|
+
columnType: TableColumnType,
|
|
694
|
+
): ZodTypes.ZodTypeAny {
|
|
695
|
+
const baseValue: ZodTypes.ZodTypeAny =
|
|
696
|
+
this.getBaseValueSchemaForColumnType(columnType);
|
|
697
|
+
|
|
698
|
+
switch (operatorType) {
|
|
699
|
+
case "EqualTo":
|
|
700
|
+
case "NotEqual":
|
|
701
|
+
return z.object({
|
|
702
|
+
_type: z.literal(operatorType),
|
|
703
|
+
value: baseValue,
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
case "GreaterThan":
|
|
707
|
+
case "LessThan":
|
|
708
|
+
case "GreaterThanOrEqual":
|
|
709
|
+
case "LessThanOrEqual":
|
|
710
|
+
return z.object({
|
|
711
|
+
_type: z.literal(operatorType),
|
|
712
|
+
value: baseValue,
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
case "Search":
|
|
716
|
+
return z.object({
|
|
717
|
+
_type: z.literal("Search"),
|
|
718
|
+
value: z.string(),
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
case "IsNull":
|
|
722
|
+
case "NotNull":
|
|
723
|
+
return z.object({
|
|
724
|
+
_type: z.literal(operatorType),
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
default:
|
|
728
|
+
return z.object({
|
|
729
|
+
_type: z.literal(operatorType),
|
|
730
|
+
value: baseValue.optional(),
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
private static getBaseValueSchemaForColumnType(
|
|
736
|
+
columnType: TableColumnType,
|
|
737
|
+
): ZodTypes.ZodTypeAny {
|
|
738
|
+
switch (columnType) {
|
|
739
|
+
case TableColumnType.ObjectID:
|
|
740
|
+
case TableColumnType.Text:
|
|
741
|
+
return z.string();
|
|
742
|
+
|
|
743
|
+
case TableColumnType.Number:
|
|
744
|
+
case TableColumnType.LongNumber:
|
|
745
|
+
case TableColumnType.Decimal:
|
|
746
|
+
return z.number();
|
|
747
|
+
|
|
748
|
+
case TableColumnType.Date:
|
|
749
|
+
return z.date();
|
|
750
|
+
|
|
751
|
+
case TableColumnType.Boolean:
|
|
752
|
+
return z.boolean();
|
|
753
|
+
|
|
754
|
+
case TableColumnType.JSON:
|
|
755
|
+
case TableColumnType.JSONArray:
|
|
756
|
+
case TableColumnType.ArrayText:
|
|
757
|
+
case TableColumnType.ArrayNumber:
|
|
758
|
+
return z.any();
|
|
759
|
+
|
|
760
|
+
default:
|
|
761
|
+
return z.string();
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|