@forestadmin-experimental/datasource-cosmos 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +512 -0
- package/dist/collection.d.ts +14 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +90 -0
- package/dist/datasource.d.ts +26 -0
- package/dist/datasource.d.ts.map +1 -0
- package/dist/datasource.js +87 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/introspection/builder.d.ts +68 -0
- package/dist/introspection/builder.d.ts.map +1 -0
- package/dist/introspection/builder.js +31 -0
- package/dist/introspection/container-introspector.d.ts +27 -0
- package/dist/introspection/container-introspector.d.ts.map +1 -0
- package/dist/introspection/container-introspector.js +168 -0
- package/dist/introspection/introspector.d.ts +18 -0
- package/dist/introspection/introspector.d.ts.map +1 -0
- package/dist/introspection/introspector.js +55 -0
- package/dist/model-builder/model.d.ts +79 -0
- package/dist/model-builder/model.d.ts.map +1 -0
- package/dist/model-builder/model.js +176 -0
- package/dist/utils/aggregation-converter.d.ts +30 -0
- package/dist/utils/aggregation-converter.d.ts.map +1 -0
- package/dist/utils/aggregation-converter.js +181 -0
- package/dist/utils/model-to-collection-schema-converter.d.ts +9 -0
- package/dist/utils/model-to-collection-schema-converter.d.ts.map +1 -0
- package/dist/utils/model-to-collection-schema-converter.js +72 -0
- package/dist/utils/query-converter.d.ts +38 -0
- package/dist/utils/query-converter.d.ts.map +1 -0
- package/dist/utils/query-converter.js +199 -0
- package/dist/utils/serializer.d.ts +6 -0
- package/dist/utils/serializer.d.ts.map +1 -0
- package/dist/utils/serializer.js +29 -0
- package/dist/utils/type-converter.d.ts +43 -0
- package/dist/utils/type-converter.d.ts.map +1 -0
- package/dist/utils/type-converter.js +157 -0
- package/package.json +36 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const container_introspector_1 = __importDefault(require("./container-introspector"));
|
|
7
|
+
class Introspector {
|
|
8
|
+
/**
|
|
9
|
+
* Introspect all containers in a Cosmos DB database
|
|
10
|
+
*/
|
|
11
|
+
static async introspect(cosmosClient, databaseName, logger) {
|
|
12
|
+
logger?.('Info', 'Introspector - Introspect Cosmos DB');
|
|
13
|
+
return Introspector.introspectAll(cosmosClient, databaseName, logger);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Introspect all containers in the specified database
|
|
17
|
+
*/
|
|
18
|
+
static async introspectAll(cosmosClient, databaseName, logger) {
|
|
19
|
+
const results = [];
|
|
20
|
+
const database = cosmosClient.database(databaseName);
|
|
21
|
+
// Get all containers in the database
|
|
22
|
+
const { resources: containers } = await database.containers.readAll().fetchAll();
|
|
23
|
+
// Filter out system containers if needed
|
|
24
|
+
const userContainers = containers.filter(container => !container.id.startsWith('_'));
|
|
25
|
+
logger?.('Info', `Introspector - Found ${userContainers.length} containers in database '${databaseName}'`);
|
|
26
|
+
// Introspect each container
|
|
27
|
+
for (const containerInfo of userContainers) {
|
|
28
|
+
try {
|
|
29
|
+
const model = await (0, container_introspector_1.default)(cosmosClient, containerInfo.id, // Use container name as collection name
|
|
30
|
+
databaseName, containerInfo.id, undefined, // Let introspector determine partition key
|
|
31
|
+
100);
|
|
32
|
+
results.push(model);
|
|
33
|
+
logger?.('Info', `Introspector - Successfully introspected container '${containerInfo.id}'`);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
logger?.('Warn', `Introspector - Failed to introspect container '${containerInfo.id}': ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
logger?.('Info', `Introspector - The following containers have been loaded: ${userContainers
|
|
40
|
+
.map(c => c.id)
|
|
41
|
+
.join(', ')}`);
|
|
42
|
+
return results;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Introspect a specific container
|
|
46
|
+
*/
|
|
47
|
+
static async introspectContainer(cosmosClient, databaseName, containerName, sampleSize, logger) {
|
|
48
|
+
logger?.('Info', `Introspector - Introspecting container '${containerName}'`);
|
|
49
|
+
const model = await (0, container_introspector_1.default)(cosmosClient, containerName, databaseName, containerName, undefined, sampleSize || 100);
|
|
50
|
+
logger?.('Info', `Introspector - Successfully introspected container '${containerName}'`);
|
|
51
|
+
return model;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.default = Introspector;
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50cm9zcGVjdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2ludHJvc3BlY3Rpb24vaW50cm9zcGVjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBSUEsc0ZBQTJEO0FBRzNELE1BQXFCLFlBQVk7SUFDL0I7O09BRUc7SUFDSCxNQUFNLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FDckIsWUFBMEIsRUFDMUIsWUFBb0IsRUFDcEIsTUFBZTtRQUVmLE1BQU0sRUFBRSxDQUFDLE1BQU0sRUFBRSxxQ0FBcUMsQ0FBQyxDQUFDO1FBRXhELE9BQU8sWUFBWSxDQUFDLGFBQWEsQ0FBQyxZQUFZLEVBQUUsWUFBWSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ3hFLENBQUM7SUFFRDs7T0FFRztJQUNLLE1BQU0sQ0FBQyxLQUFLLENBQUMsYUFBYSxDQUNoQyxZQUEwQixFQUMxQixZQUFvQixFQUNwQixNQUFlO1FBRWYsTUFBTSxPQUFPLEdBQWtCLEVBQUUsQ0FBQztRQUNsQyxNQUFNLFFBQVEsR0FBRyxZQUFZLENBQUMsUUFBUSxDQUFDLFlBQVksQ0FBQyxDQUFDO1FBRXJELHFDQUFxQztRQUNyQyxNQUFNLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxHQUFHLE1BQU0sUUFBUSxDQUFDLFVBQVUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUVqRix5Q0FBeUM7UUFDekMsTUFBTSxjQUFjLEdBQUcsVUFBVSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUVyRixNQUFNLEVBQUUsQ0FDTixNQUFNLEVBQ04sd0JBQXdCLGNBQWMsQ0FBQyxNQUFNLDRCQUE0QixZQUFZLEdBQUcsQ0FDekYsQ0FBQztRQUVGLDRCQUE0QjtRQUM1QixLQUFLLE1BQU0sYUFBYSxJQUFJLGNBQWMsRUFBRSxDQUFDO1lBQzNDLElBQUksQ0FBQztnQkFDSCxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUEsZ0NBQW1CLEVBQ3JDLFlBQVksRUFDWixhQUFhLENBQUMsRUFBRSxFQUFFLHdDQUF3QztnQkFDMUQsWUFBWSxFQUNaLGFBQWEsQ0FBQyxFQUFFLEVBQ2hCLFNBQVMsRUFBRSwyQ0FBMkM7Z0JBQ3RELEdBQUcsQ0FDSixDQUFDO2dCQUVGLE9BQU8sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBRXBCLE1BQU0sRUFBRSxDQUNOLE1BQU0sRUFDTix1REFBdUQsYUFBYSxDQUFDLEVBQUUsR0FBRyxDQUMzRSxDQUFDO1lBQ0osQ0FBQztZQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7Z0JBQ2YsTUFBTSxFQUFFLENBQ04sTUFBTSxFQUNOLGtEQUFrRCxhQUFhLENBQUMsRUFBRSxNQUFNLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FDeEYsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO1FBRUQsTUFBTSxFQUFFLENBQ04sTUFBTSxFQUNOLDZEQUE2RCxjQUFjO2FBQ3hFLEdBQUcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7YUFDZCxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDaEIsQ0FBQztRQUVGLE9BQU8sT0FBTyxDQUFDO0lBQ2pCLENBQUM7SUFFRDs7T0FFRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsbUJBQW1CLENBQzlCLFlBQTBCLEVBQzFCLFlBQW9CLEVBQ3BCLGFBQXFCLEVBQ3JCLFVBQW1CLEVBQ25CLE1BQWU7UUFFZixNQUFNLEVBQUUsQ0FBQyxNQUFNLEVBQUUsMkNBQTJDLGFBQWEsR0FBRyxDQUFDLENBQUM7UUFFOUUsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFBLGdDQUFtQixFQUNyQyxZQUFZLEVBQ1osYUFBYSxFQUNiLFlBQVksRUFDWixhQUFhLEVBQ2IsU0FBUyxFQUNULFVBQVUsSUFBSSxHQUFHLENBQ2xCLENBQUM7UUFFRixNQUFNLEVBQUUsQ0FBQyxNQUFNLEVBQUUsdURBQXVELGFBQWEsR0FBRyxDQUFDLENBQUM7UUFFMUYsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0Y7QUFqR0QsK0JBaUdDIn0=
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Container, CosmosClient, SqlQuerySpec } from '@azure/cosmos';
|
|
2
|
+
import { RecordData } from '@forestadmin/datasource-toolkit';
|
|
3
|
+
import { OverrideTypeConverter } from '../introspection/builder';
|
|
4
|
+
export interface CosmosSchema {
|
|
5
|
+
[key: string]: {
|
|
6
|
+
type: string;
|
|
7
|
+
nullable?: boolean;
|
|
8
|
+
indexed?: boolean;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export default class ModelCosmos {
|
|
12
|
+
name: string;
|
|
13
|
+
/**
|
|
14
|
+
* The Cosmos DB database name
|
|
15
|
+
*/
|
|
16
|
+
private databaseName;
|
|
17
|
+
/**
|
|
18
|
+
* The Cosmos DB container name
|
|
19
|
+
*/
|
|
20
|
+
private containerName;
|
|
21
|
+
/**
|
|
22
|
+
* The partition key path for this container
|
|
23
|
+
*/
|
|
24
|
+
private partitionKeyPath;
|
|
25
|
+
/**
|
|
26
|
+
* Schema definition inferred from sample documents
|
|
27
|
+
*/
|
|
28
|
+
private schema;
|
|
29
|
+
/**
|
|
30
|
+
* Cosmos DB client instance
|
|
31
|
+
*/
|
|
32
|
+
private cosmosClient;
|
|
33
|
+
/**
|
|
34
|
+
* Container reference for direct operations
|
|
35
|
+
*/
|
|
36
|
+
private container;
|
|
37
|
+
overrideTypeConverter?: OverrideTypeConverter;
|
|
38
|
+
enableCount?: boolean;
|
|
39
|
+
constructor(cosmosClient: CosmosClient, name: string, databaseName: string, containerName: string, partitionKeyPath: string, schema: CosmosSchema, overrideTypeConverter?: OverrideTypeConverter, enableCount?: boolean);
|
|
40
|
+
create(data: RecordData[]): Promise<RecordData[]>;
|
|
41
|
+
update(ids: string[], patch: RecordData): Promise<void>;
|
|
42
|
+
delete(ids: string[]): Promise<void>;
|
|
43
|
+
query(querySpec: SqlQuerySpec, offset?: number, limit?: number): Promise<RecordData[]>;
|
|
44
|
+
aggregateQuery(querySpec: SqlQuerySpec): Promise<RecordData[]>;
|
|
45
|
+
count(querySpec?: SqlQuerySpec): Promise<number>;
|
|
46
|
+
/**
|
|
47
|
+
* Get items by their IDs (requires scanning as we don't have partition keys)
|
|
48
|
+
*/
|
|
49
|
+
private getItemsByIds;
|
|
50
|
+
/**
|
|
51
|
+
* Extract the partition key value from an item
|
|
52
|
+
*/
|
|
53
|
+
private getPartitionKeyValue;
|
|
54
|
+
/**
|
|
55
|
+
* Generate a unique ID for new items
|
|
56
|
+
*/
|
|
57
|
+
private generateId;
|
|
58
|
+
/**
|
|
59
|
+
* Return all fields from schema
|
|
60
|
+
*/
|
|
61
|
+
getAttributes(): CosmosSchema;
|
|
62
|
+
/**
|
|
63
|
+
* Get container reference for advanced operations
|
|
64
|
+
*/
|
|
65
|
+
getContainer(): Container;
|
|
66
|
+
/**
|
|
67
|
+
* Get partition key path
|
|
68
|
+
*/
|
|
69
|
+
getPartitionKeyPath(): string;
|
|
70
|
+
/**
|
|
71
|
+
* Get database name
|
|
72
|
+
*/
|
|
73
|
+
getDatabaseName(): string;
|
|
74
|
+
/**
|
|
75
|
+
* Get container name
|
|
76
|
+
*/
|
|
77
|
+
getContainerName(): string;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/model-builder/model.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAkB,YAAY,EAAE,MAAM,eAAe,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAE7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAGjE,MAAM,WAAW,YAAY;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG;QACb,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,OAAO,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;KACnB,CAAC;CACH;AAED,MAAM,CAAC,OAAO,OAAO,WAAW;IACvB,IAAI,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,OAAO,CAAC,YAAY,CAAS;IAE7B;;OAEG;IACH,OAAO,CAAC,aAAa,CAAS;IAE9B;;OAEG;IACH,OAAO,CAAC,gBAAgB,CAAS;IAEjC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAe;IAE7B;;OAEG;IACH,OAAO,CAAC,YAAY,CAAe;IAEnC;;OAEG;IACH,OAAO,CAAC,SAAS,CAAY;IAEtB,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAE9C,WAAW,CAAC,EAAE,OAAO,CAAC;gBAG3B,YAAY,EAAE,YAAY,EAC1B,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,YAAY,EACpB,qBAAqB,CAAC,EAAE,qBAAqB,EAC7C,WAAW,CAAC,EAAE,OAAO;IAcV,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IA4BjD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAapC,KAAK,CAChB,SAAS,EAAE,YAAY,EACvB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC;IAsBX,cAAc,CAAC,SAAS,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAM9D,KAAK,CAAC,SAAS,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAmB7D;;OAEG;YACW,aAAa;IAgB3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;OAEG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACI,aAAa,IAAI,YAAY;IAIpC;;OAEG;IACI,YAAY,IAAI,SAAS;IAIhC;;OAEG;IACI,mBAAmB,IAAI,MAAM;IAIpC;;OAEG;IACI,eAAe,IAAI,MAAM;IAIhC;;OAEG;IACI,gBAAgB,IAAI,MAAM;CAGlC"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const serializer_1 = __importDefault(require("../utils/serializer"));
|
|
7
|
+
class ModelCosmos {
|
|
8
|
+
constructor(cosmosClient, name, databaseName, containerName, partitionKeyPath, schema, overrideTypeConverter, enableCount) {
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.databaseName = databaseName;
|
|
11
|
+
this.containerName = containerName;
|
|
12
|
+
this.partitionKeyPath = partitionKeyPath;
|
|
13
|
+
this.schema = schema;
|
|
14
|
+
this.overrideTypeConverter = overrideTypeConverter;
|
|
15
|
+
this.enableCount = enableCount;
|
|
16
|
+
this.cosmosClient = cosmosClient;
|
|
17
|
+
this.container = this.cosmosClient.database(databaseName).container(containerName);
|
|
18
|
+
}
|
|
19
|
+
async create(data) {
|
|
20
|
+
const createdRecords = [];
|
|
21
|
+
// Add timestamps if schema includes them
|
|
22
|
+
if (this.schema.createdAt) {
|
|
23
|
+
data.forEach(newRecord => {
|
|
24
|
+
newRecord.createdAt = new Date().toISOString();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// Cosmos DB doesn't have a native bulk create, so we'll create items individually
|
|
28
|
+
// In production, you might want to use stored procedures or batch operations
|
|
29
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
30
|
+
for (const record of data) {
|
|
31
|
+
// Ensure the record has an id field (required by Cosmos DB)
|
|
32
|
+
const itemToCreate = {
|
|
33
|
+
id: record.id || this.generateId(),
|
|
34
|
+
...record,
|
|
35
|
+
};
|
|
36
|
+
// eslint-disable-next-line no-await-in-loop
|
|
37
|
+
const { resource } = await this.container.items.create(itemToCreate);
|
|
38
|
+
createdRecords.push(serializer_1.default.serialize(resource));
|
|
39
|
+
}
|
|
40
|
+
return createdRecords;
|
|
41
|
+
}
|
|
42
|
+
async update(ids, patch) {
|
|
43
|
+
// Cosmos DB requires both id and partition key for updates
|
|
44
|
+
// We need to fetch the items first to get their partition keys
|
|
45
|
+
const itemsToUpdate = await this.getItemsByIds(ids);
|
|
46
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
47
|
+
for (const item of itemsToUpdate) {
|
|
48
|
+
const partitionKeyValue = this.getPartitionKeyValue(item);
|
|
49
|
+
// Merge the patch with the existing item
|
|
50
|
+
const updatedItem = {
|
|
51
|
+
...item,
|
|
52
|
+
...patch,
|
|
53
|
+
};
|
|
54
|
+
// eslint-disable-next-line no-await-in-loop
|
|
55
|
+
await this.container.item(item.id, partitionKeyValue).replace(updatedItem);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async delete(ids) {
|
|
59
|
+
// Similar to update, we need partition keys for deletion
|
|
60
|
+
const itemsToDelete = await this.getItemsByIds(ids);
|
|
61
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
62
|
+
for (const item of itemsToDelete) {
|
|
63
|
+
const partitionKeyValue = this.getPartitionKeyValue(item);
|
|
64
|
+
// eslint-disable-next-line no-await-in-loop
|
|
65
|
+
await this.container.item(item.id, partitionKeyValue).delete();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async query(querySpec, offset, limit) {
|
|
69
|
+
const options = {
|
|
70
|
+
maxItemCount: limit,
|
|
71
|
+
};
|
|
72
|
+
const { resources } = await this.container.items.query(querySpec, options).fetchAll();
|
|
73
|
+
// Apply offset and limit (Cosmos DB doesn't have native OFFSET)
|
|
74
|
+
let results = resources;
|
|
75
|
+
if (offset) {
|
|
76
|
+
results = results.slice(offset);
|
|
77
|
+
}
|
|
78
|
+
if (limit && offset) {
|
|
79
|
+
// If we already sliced with offset, limit the remaining results
|
|
80
|
+
results = results.slice(0, limit);
|
|
81
|
+
}
|
|
82
|
+
return results.map(item => serializer_1.default.serialize(item));
|
|
83
|
+
}
|
|
84
|
+
async aggregateQuery(querySpec) {
|
|
85
|
+
const { resources } = await this.container.items.query(querySpec).fetchAll();
|
|
86
|
+
return resources.map(item => serializer_1.default.serialize(item));
|
|
87
|
+
}
|
|
88
|
+
async count(querySpec) {
|
|
89
|
+
if (!querySpec) {
|
|
90
|
+
// Simple count without filters
|
|
91
|
+
const countQuery = {
|
|
92
|
+
query: 'SELECT VALUE COUNT(1) FROM c',
|
|
93
|
+
};
|
|
94
|
+
const { resources } = await this.container.items.query(countQuery).fetchAll();
|
|
95
|
+
return resources[0] || 0;
|
|
96
|
+
}
|
|
97
|
+
// Count with filters - we need to modify the query to use COUNT
|
|
98
|
+
const { resources } = await this.container.items.query(querySpec).fetchAll();
|
|
99
|
+
return resources.length;
|
|
100
|
+
}
|
|
101
|
+
// INTERNAL HELPER METHODS
|
|
102
|
+
/**
|
|
103
|
+
* Get items by their IDs (requires scanning as we don't have partition keys)
|
|
104
|
+
*/
|
|
105
|
+
async getItemsByIds(ids) {
|
|
106
|
+
const querySpec = {
|
|
107
|
+
query: 'SELECT * FROM c WHERE ARRAY_CONTAINS(@ids, c.id)',
|
|
108
|
+
parameters: [
|
|
109
|
+
{
|
|
110
|
+
name: '@ids',
|
|
111
|
+
value: ids,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
const { resources } = await this.container.items.query(querySpec).fetchAll();
|
|
116
|
+
return resources;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Extract the partition key value from an item
|
|
120
|
+
*/
|
|
121
|
+
getPartitionKeyValue(item) {
|
|
122
|
+
// Remove leading slash from partition key path
|
|
123
|
+
const keyPath = this.partitionKeyPath.replace(/^\//, '');
|
|
124
|
+
// Handle nested paths (e.g., "/address/city")
|
|
125
|
+
const keys = keyPath.split('/');
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
127
|
+
let value = item;
|
|
128
|
+
for (const key of keys) {
|
|
129
|
+
if (value && typeof value === 'object') {
|
|
130
|
+
value = value[key];
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Generate a unique ID for new items
|
|
140
|
+
*/
|
|
141
|
+
generateId() {
|
|
142
|
+
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Return all fields from schema
|
|
146
|
+
*/
|
|
147
|
+
getAttributes() {
|
|
148
|
+
return this.schema;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get container reference for advanced operations
|
|
152
|
+
*/
|
|
153
|
+
getContainer() {
|
|
154
|
+
return this.container;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get partition key path
|
|
158
|
+
*/
|
|
159
|
+
getPartitionKeyPath() {
|
|
160
|
+
return this.partitionKeyPath;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get database name
|
|
164
|
+
*/
|
|
165
|
+
getDatabaseName() {
|
|
166
|
+
return this.databaseName;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get container name
|
|
170
|
+
*/
|
|
171
|
+
getContainerName() {
|
|
172
|
+
return this.containerName;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.default = ModelCosmos;
|
|
176
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbW9kZWwtYnVpbGRlci9tb2RlbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUtBLHFFQUE2QztBQVU3QyxNQUFxQixXQUFXO0lBcUM5QixZQUNFLFlBQTBCLEVBQzFCLElBQVksRUFDWixZQUFvQixFQUNwQixhQUFxQixFQUNyQixnQkFBd0IsRUFDeEIsTUFBb0IsRUFDcEIscUJBQTZDLEVBQzdDLFdBQXFCO1FBRXJCLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLElBQUksQ0FBQyxZQUFZLEdBQUcsWUFBWSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxhQUFhLEdBQUcsYUFBYSxDQUFDO1FBQ25DLElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxnQkFBZ0IsQ0FBQztRQUN6QyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUNyQixJQUFJLENBQUMscUJBQXFCLEdBQUcscUJBQXFCLENBQUM7UUFDbkQsSUFBSSxDQUFDLFdBQVcsR0FBRyxXQUFXLENBQUM7UUFFL0IsSUFBSSxDQUFDLFlBQVksR0FBRyxZQUFZLENBQUM7UUFDakMsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLFFBQVEsQ0FBQyxZQUFZLENBQUMsQ0FBQyxTQUFTLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDckYsQ0FBQztJQUVNLEtBQUssQ0FBQyxNQUFNLENBQUMsSUFBa0I7UUFDcEMsTUFBTSxjQUFjLEdBQWlCLEVBQUUsQ0FBQztRQUV4Qyx5Q0FBeUM7UUFDekMsSUFBSSxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO1lBQzFCLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUU7Z0JBQ3ZCLFNBQVMsQ0FBQyxTQUFTLEdBQUcsSUFBSSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQztZQUNqRCxDQUFDLENBQUMsQ0FB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
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { SqlQuerySpec } from '@azure/cosmos';
|
|
2
|
+
import { AggregateResult, Aggregation, AggregationOperation, ConditionTree } from '@forestadmin/datasource-toolkit';
|
|
3
|
+
export default class AggregationConverter {
|
|
4
|
+
private static AGGREGATION_OPERATION;
|
|
5
|
+
/**
|
|
6
|
+
* Build SQL aggregation query for Cosmos DB
|
|
7
|
+
*/
|
|
8
|
+
static buildAggregationQuery(aggregation: Aggregation, conditionTree?: ConditionTree, limit?: number): SqlQuerySpec;
|
|
9
|
+
/**
|
|
10
|
+
* Build SELECT clause for aggregation expression
|
|
11
|
+
*/
|
|
12
|
+
private static buildAggregateExpression;
|
|
13
|
+
/**
|
|
14
|
+
* Build aggregation query with grouping
|
|
15
|
+
*/
|
|
16
|
+
private static buildGroupedAggregationQuery;
|
|
17
|
+
/**
|
|
18
|
+
* Build date grouping expression
|
|
19
|
+
*/
|
|
20
|
+
private static buildDateGroupExpression;
|
|
21
|
+
/**
|
|
22
|
+
* Process raw aggregation results into Forest Admin format
|
|
23
|
+
*/
|
|
24
|
+
static processAggregationResults(rawResults: any[], aggregation: Aggregation): AggregateResult[];
|
|
25
|
+
/**
|
|
26
|
+
* Build a simpler aggregation query for single-group scenarios
|
|
27
|
+
*/
|
|
28
|
+
static buildSimpleAggregationQuery(operation: AggregationOperation, field: string | null, groupByField: string | null, conditionTree?: ConditionTree, limit?: number): SqlQuerySpec;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=aggregation-converter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregation-converter.d.ts","sourceRoot":"","sources":["../../src/utils/aggregation-converter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EACL,eAAe,EACf,WAAW,EACX,oBAAoB,EACpB,aAAa,EAEd,MAAM,iCAAiC,CAAC;AAKzC,MAAM,CAAC,OAAO,OAAO,oBAAoB;IACvC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAMlC;IAEF;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAC1B,WAAW,EAAE,WAAW,EACxB,aAAa,CAAC,EAAE,aAAa,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,YAAY;IAiBf;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAYvC;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;IA6D3C;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,wBAAwB;IAyBvC;;OAEG;IACH,MAAM,CAAC,yBAAyB,CAE9B,UAAU,EAAE,GAAG,EAAE,EACjB,WAAW,EAAE,WAAW,GACvB,eAAe,EAAE;IAkCpB;;OAEG;IACH,MAAM,CAAC,2BAA2B,CAChC,SAAS,EAAE,oBAAoB,EAC/B,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,aAAa,CAAC,EAAE,aAAa,EAC7B,KAAK,CAAC,EAAE,MAAM,GACb,YAAY;CAoChB"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const query_converter_1 = __importDefault(require("./query-converter"));
|
|
7
|
+
const serializer_1 = __importDefault(require("./serializer"));
|
|
8
|
+
class AggregationConverter {
|
|
9
|
+
/**
|
|
10
|
+
* Build SQL aggregation query for Cosmos DB
|
|
11
|
+
*/
|
|
12
|
+
static buildAggregationQuery(aggregation, conditionTree, limit) {
|
|
13
|
+
const queryConverter = new query_converter_1.default();
|
|
14
|
+
const { where, parameters } = queryConverter.getWhereClause(conditionTree);
|
|
15
|
+
const whereFragment = where ? `WHERE ${where}` : '';
|
|
16
|
+
// Handle simple aggregation without grouping
|
|
17
|
+
if (!aggregation.groups || aggregation.groups.length === 0) {
|
|
18
|
+
const selectClause = this.buildAggregateExpression(aggregation);
|
|
19
|
+
const query = `SELECT ${selectClause} as value FROM c ${whereFragment}`;
|
|
20
|
+
return { query, parameters };
|
|
21
|
+
}
|
|
22
|
+
// Handle aggregation with grouping
|
|
23
|
+
return this.buildGroupedAggregationQuery(aggregation, whereFragment, parameters, limit);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build SELECT clause for aggregation expression
|
|
27
|
+
*/
|
|
28
|
+
static buildAggregateExpression(aggregation) {
|
|
29
|
+
if (!aggregation.field) {
|
|
30
|
+
// COUNT(*) case
|
|
31
|
+
return 'COUNT(1)';
|
|
32
|
+
}
|
|
33
|
+
const operation = this.AGGREGATION_OPERATION[aggregation.operation];
|
|
34
|
+
const fieldPath = `c.${aggregation.field}`;
|
|
35
|
+
return `${operation}(${fieldPath})`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build aggregation query with grouping
|
|
39
|
+
*/
|
|
40
|
+
static buildGroupedAggregationQuery(aggregation, whereFragment, parameters, limit) {
|
|
41
|
+
const groups = aggregation.groups || [];
|
|
42
|
+
// Build the aggregate expression
|
|
43
|
+
const aggregateExpr = this.buildAggregateExpression(aggregation);
|
|
44
|
+
// Build the full query
|
|
45
|
+
// Note: Cosmos DB doesn't support GROUP BY directly in SQL API
|
|
46
|
+
// We need to use a different approach with subqueries or application-level grouping
|
|
47
|
+
// For now, we'll use VALUE syntax with DISTINCT for simple grouping
|
|
48
|
+
if (groups.length === 1 && !groups[0].operation) {
|
|
49
|
+
// Simple single field grouping
|
|
50
|
+
const groupField = `c.${groups[0].field}`;
|
|
51
|
+
// For Count operation without field, we need a different approach
|
|
52
|
+
if (aggregation.operation === 'Count' && !aggregation.field) {
|
|
53
|
+
const query = `
|
|
54
|
+
SELECT ${groupField} as groupKey, COUNT(1) as value
|
|
55
|
+
FROM c
|
|
56
|
+
${whereFragment}
|
|
57
|
+
GROUP BY ${groupField}
|
|
58
|
+
ORDER BY ${groupField}
|
|
59
|
+
${limit ? `OFFSET 0 LIMIT ${limit}` : ''}
|
|
60
|
+
`
|
|
61
|
+
.trim()
|
|
62
|
+
.replace(/\s+/g, ' ');
|
|
63
|
+
return { query, parameters };
|
|
64
|
+
}
|
|
65
|
+
// For other aggregations
|
|
66
|
+
const query = `
|
|
67
|
+
SELECT ${groupField} as groupKey, ${aggregateExpr} as value
|
|
68
|
+
FROM c
|
|
69
|
+
${whereFragment}
|
|
70
|
+
GROUP BY ${groupField}
|
|
71
|
+
ORDER BY ${groupField}
|
|
72
|
+
${limit ? `OFFSET 0 LIMIT ${limit}` : ''}
|
|
73
|
+
`
|
|
74
|
+
.trim()
|
|
75
|
+
.replace(/\s+/g, ' ');
|
|
76
|
+
return { query, parameters };
|
|
77
|
+
}
|
|
78
|
+
// Multiple groups or date operations - more complex, needs special handling
|
|
79
|
+
// For now, throw an error as this requires more complex implementation
|
|
80
|
+
const errorMessage = 'Complex grouping with multiple fields or date operations requires ' +
|
|
81
|
+
'application-level processing. Please implement this using the raw query ' +
|
|
82
|
+
'results and post-processing.';
|
|
83
|
+
throw new Error(errorMessage);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Build date grouping expression
|
|
87
|
+
*/
|
|
88
|
+
static buildDateGroupExpression(field, operation, alias) {
|
|
89
|
+
const fieldPath = `c.${field}`;
|
|
90
|
+
switch (operation) {
|
|
91
|
+
case 'Year':
|
|
92
|
+
return `DateTimeYear(${fieldPath}) as ${alias}`;
|
|
93
|
+
case 'Month':
|
|
94
|
+
return `CONCAT(DateTimeYear(${fieldPath}), '-', DateTimeMonth(${fieldPath})) as ${alias}`;
|
|
95
|
+
case 'Week':
|
|
96
|
+
// Cosmos DB doesn't have built-in week function, we'll approximate
|
|
97
|
+
return `DateTimeDay(${fieldPath}) as ${alias}`;
|
|
98
|
+
case 'Day':
|
|
99
|
+
return (`CONCAT(DateTimeYear(${fieldPath}), '-', DateTimeMonth(${fieldPath}), '-', ` +
|
|
100
|
+
`DateTimeDay(${fieldPath})) as ${alias}`);
|
|
101
|
+
default:
|
|
102
|
+
throw new Error(`Unsupported date operation: ${operation}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Process raw aggregation results into Forest Admin format
|
|
107
|
+
*/
|
|
108
|
+
static processAggregationResults(
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
110
|
+
rawResults, aggregation) {
|
|
111
|
+
// Handle simple aggregation without grouping
|
|
112
|
+
if (!aggregation.groups || aggregation.groups.length === 0) {
|
|
113
|
+
if (rawResults.length === 0) {
|
|
114
|
+
return [{ value: 0, group: {} }];
|
|
115
|
+
}
|
|
116
|
+
return [
|
|
117
|
+
{
|
|
118
|
+
value: serializer_1.default.serializeValue(rawResults[0].value),
|
|
119
|
+
group: {},
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
// Handle grouped aggregation
|
|
124
|
+
return rawResults.map(result => {
|
|
125
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
126
|
+
const group = {};
|
|
127
|
+
if (aggregation.groups) {
|
|
128
|
+
aggregation.groups.forEach((groupDef, index) => {
|
|
129
|
+
const groupKey = result.groupKey || result[`group${index}`];
|
|
130
|
+
group[groupDef.field] = serializer_1.default.serializeValue(groupKey);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
value: serializer_1.default.serializeValue(result.value),
|
|
135
|
+
group,
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Build a simpler aggregation query for single-group scenarios
|
|
141
|
+
*/
|
|
142
|
+
static buildSimpleAggregationQuery(operation, field, groupByField, conditionTree, limit) {
|
|
143
|
+
const queryConverter = new query_converter_1.default();
|
|
144
|
+
const { where, parameters } = queryConverter.getWhereClause(conditionTree);
|
|
145
|
+
const whereFragment = where ? `WHERE ${where}` : '';
|
|
146
|
+
let selectClause;
|
|
147
|
+
let groupByClause = '';
|
|
148
|
+
let orderByClause = '';
|
|
149
|
+
if (!field) {
|
|
150
|
+
// COUNT(*) case
|
|
151
|
+
selectClause = groupByField
|
|
152
|
+
? `c.${groupByField} as groupKey, COUNT(1) as value`
|
|
153
|
+
: 'COUNT(1) as value';
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const op = this.AGGREGATION_OPERATION[operation];
|
|
157
|
+
const fieldPath = `c.${field}`;
|
|
158
|
+
selectClause = groupByField
|
|
159
|
+
? `c.${groupByField} as groupKey, ${op}(${fieldPath}) as value`
|
|
160
|
+
: `${op}(${fieldPath}) as value`;
|
|
161
|
+
}
|
|
162
|
+
if (groupByField) {
|
|
163
|
+
groupByClause = `GROUP BY c.${groupByField}`;
|
|
164
|
+
orderByClause = `ORDER BY c.${groupByField}`;
|
|
165
|
+
}
|
|
166
|
+
const limitClause = limit && groupByField ? `OFFSET 0 LIMIT ${limit}` : '';
|
|
167
|
+
const queryString = `SELECT ${selectClause} FROM c ${whereFragment} ${groupByClause} ` +
|
|
168
|
+
`${orderByClause} ${limitClause}`;
|
|
169
|
+
const query = queryString.trim().replace(/\s+/g, ' ');
|
|
170
|
+
return { query, parameters };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
AggregationConverter.AGGREGATION_OPERATION = {
|
|
174
|
+
Sum: 'SUM',
|
|
175
|
+
Avg: 'AVG',
|
|
176
|
+
Count: 'COUNT',
|
|
177
|
+
Max: 'MAX',
|
|
178
|
+
Min: 'MIN',
|
|
179
|
+
};
|
|
180
|
+
exports.default = AggregationConverter;
|
|
181
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWdncmVnYXRpb24tY29udmVydGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL2FnZ3JlZ2F0aW9uLWNvbnZlcnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQVNBLHdFQUErQztBQUMvQyw4REFBc0M7QUFFdEMsTUFBcUIsb0JBQW9CO0lBU3ZDOztPQUVHO0lBQ0gsTUFBTSxDQUFDLHFCQUFxQixDQUMxQixXQUF3QixFQUN4QixhQUE2QixFQUM3QixLQUFjO1FBRWQsTUFBTSxjQUFjLEdBQUcsSUFBSSx5QkFBYyxFQUFFLENBQUM7UUFDNUMsTUFBTSxFQUFFLEtBQUssRUFBRSxVQUFVLEVBQUUsR0FBRyxjQUFjLENBQUMsY0FBYyxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQzNFLE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxLQUFLLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBRXBELDZDQUE2QztRQUM3QyxJQUFJLENBQUMsV0FBVyxDQUFDLE1BQU0sSUFBSSxXQUFXLENBQUMsTUFBTSxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUMzRCxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDaEUsTUFBTSxLQUFLLEdBQUcsVUFBVSxZQUFZLG9CQUFvQixhQUFhLEVBQUUsQ0FBQztZQUV4RSxPQUFPLEVBQUUsS0FBSyxFQUFFLFVBQVUsRUFBRSxDQUFDO1FBQy9CLENBQUM7UUFFRCxtQ0FBbUM7UUFDbkMsT0FBTyxJQUFJLENBQUMsNEJBQTRCLENBQUMsV0FBVyxFQUFFLGFBQWEsRUFBRSxVQUFVLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDMUYsQ0FBQztJQUVEOztPQUVHO0lBQ0ssTUFBTSxDQUFDLHdCQUF3QixDQUFDLFdBQXdCO1FBQzlELElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxFQUFFLENBQUM7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
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CollectionSchema, Logger } from '@forestadmin/datasource-toolkit';
|
|
2
|
+
import ModelCosmos from '../model-builder/model';
|
|
3
|
+
export default class ModelToCollectionSchemaConverter {
|
|
4
|
+
private static convertAttribute;
|
|
5
|
+
private static convertAttributes;
|
|
6
|
+
private static getFieldSchemaOrOverride;
|
|
7
|
+
static convert(model: ModelCosmos, logger?: Logger): CollectionSchema;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=model-to-collection-schema-converter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-to-collection-schema-converter.d.ts","sourceRoot":"","sources":["../../src/utils/model-to-collection-schema-converter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAGhB,MAAM,EACP,MAAM,iCAAiC,CAAC;AAIzC,OAAO,WAA6B,MAAM,wBAAwB,CAAC;AAEnE,MAAM,CAAC,OAAO,OAAO,gCAAgC;IACnD,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAqB/B,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAoChC,OAAO,CAAC,MAAM,CAAC,wBAAwB;WAgBzB,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,gBAAgB;CAmB7E"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const type_converter_1 = __importDefault(require("./type-converter"));
|
|
7
|
+
class ModelToCollectionSchemaConverter {
|
|
8
|
+
static convertAttribute(fieldName, fieldSchema) {
|
|
9
|
+
const cosmosType = fieldSchema.type;
|
|
10
|
+
const columnType = type_converter_1.default.fromDataType(cosmosType);
|
|
11
|
+
const filterOperators = type_converter_1.default.operatorsForColumnType(columnType);
|
|
12
|
+
const isSortable = type_converter_1.default.isSortable(cosmosType);
|
|
13
|
+
const column = {
|
|
14
|
+
columnType,
|
|
15
|
+
filterOperators,
|
|
16
|
+
type: 'Column',
|
|
17
|
+
validation: [],
|
|
18
|
+
isReadOnly: false, // Cosmos DB fields are generally writable
|
|
19
|
+
isSortable,
|
|
20
|
+
};
|
|
21
|
+
return column;
|
|
22
|
+
}
|
|
23
|
+
static convertAttributes(modelName, schema, overrideTypeConverter, logger) {
|
|
24
|
+
const fields = {};
|
|
25
|
+
Object.entries(schema).forEach(([fieldName, fieldSchema]) => {
|
|
26
|
+
try {
|
|
27
|
+
fields[fieldName] = this.getFieldSchemaOrOverride(fieldName, fieldSchema, overrideTypeConverter);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
logger?.('Warn', `Skipping column '${modelName}.${fieldName}' (${error.message})`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
// Add the 'id' field which is required by Cosmos DB
|
|
34
|
+
const idColumn = {
|
|
35
|
+
columnType: 'String',
|
|
36
|
+
type: 'Column',
|
|
37
|
+
filterOperators: type_converter_1.default.operatorsForId(),
|
|
38
|
+
validation: [],
|
|
39
|
+
isReadOnly: false, // Can be set when creating documents
|
|
40
|
+
isSortable: true,
|
|
41
|
+
isPrimaryKey: true,
|
|
42
|
+
};
|
|
43
|
+
fields.id = idColumn;
|
|
44
|
+
return fields;
|
|
45
|
+
}
|
|
46
|
+
static getFieldSchemaOrOverride(fieldName, fieldSchema, overrideTypeConverter) {
|
|
47
|
+
const field = ModelToCollectionSchemaConverter.convertAttribute(fieldName, fieldSchema);
|
|
48
|
+
return overrideTypeConverter
|
|
49
|
+
? overrideTypeConverter({
|
|
50
|
+
fieldName,
|
|
51
|
+
attribute: fieldSchema,
|
|
52
|
+
generatedFieldSchema: field,
|
|
53
|
+
}) || field
|
|
54
|
+
: field;
|
|
55
|
+
}
|
|
56
|
+
static convert(model, logger) {
|
|
57
|
+
if (!model)
|
|
58
|
+
throw new Error('Invalid (null) model.');
|
|
59
|
+
return {
|
|
60
|
+
actions: {},
|
|
61
|
+
charts: [],
|
|
62
|
+
countable: model.enableCount ?? true,
|
|
63
|
+
fields: {
|
|
64
|
+
...this.convertAttributes(model.name, model.getAttributes(), model.overrideTypeConverter, logger),
|
|
65
|
+
},
|
|
66
|
+
searchable: false, // Cosmos DB doesn't have built-in full-text search like Elasticsearch
|
|
67
|
+
segments: [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
exports.default = ModelToCollectionSchemaConverter;
|
|
72
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kZWwtdG8tY29sbGVjdGlvbi1zY2hlbWEtY29udmVydGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL21vZGVsLXRvLWNvbGxlY3Rpb24tc2NoZW1hLWNvbnZlcnRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQU9BLHNFQUFpRTtBQUlqRSxNQUFxQixnQ0FBZ0M7SUFDM0MsTUFBTSxDQUFDLGdCQUFnQixDQUM3QixTQUFpQixFQUNqQixXQUFvRTtRQUVwRSxNQUFNLFVBQVUsR0FBRyxXQUFXLENBQUMsSUFBc0IsQ0FBQztRQUN0RCxNQUFNLFVBQVUsR0FBRyx3QkFBYSxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUMxRCxNQUFNLGVBQWUsR0FBRyx3QkFBYSxDQUFDLHNCQUFzQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ3pFLE1BQU0sVUFBVSxHQUFHLHdCQUFhLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBRXhELE1BQU0sTUFBTSxHQUFpQjtZQUMzQixVQUFVO1lBQ1YsZUFBZTtZQUNmLElBQUksRUFBRSxRQUFRO1lBQ2QsVUFBVSxFQUFFLEVBQUU7WUFDZCxVQUFVLEVBQUUsS0FBSyxFQUFFLDBDQUEwQztZQUM3RCxVQUFVO1NBQ1gsQ0FBQztRQUVGLE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUM7SUFFTyxNQUFNLENBQUMsaUJBQWlCLENBQzlCLFNBQWlCLEVBQ2pCLE1BQW9CLEVBQ3BCLHFCQUE2QyxFQUM3QyxNQUFlO1FBRWYsTUFBTSxNQUFNLEdBQStCLEVBQUUsQ0FBQztRQUU5QyxNQUFNLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxFQUFFLEVBQUU7WUFDMUQsSUFBSSxDQUFDO2dCQUNILE1BQU0sQ0FBQyxTQUFTLENBQUMsR0FBRyxJQUFJLENBQUMsd0JBQXdCLENBQy9DLFNBQVMsRUFDVCxXQUFXLEVBQ1gscUJBQXFCLENBQ3RCLENBQUM7WUFDSixDQUFDO1lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztnQkFDZixNQUFNLEVBQUUsQ0FBQyxNQUFNLEVBQUUsb0JBQW9CLFNBQVMsSUFBSSxTQUFTLE1BQU0sS0FBSyxDQUFDLE9BQU8sR0FBRyxDQUFDLENBQUM7WUFDckYsQ0FBQztRQUNILENBQUMsQ0FBQyxDQUFDO1FBRUgsb0RBQW9EO1FBQ3BELE1BQU0sUUFBUSxHQUFpQjtZQUM3QixVQUFVLEVBQUUsUUFBUTtZQUNwQixJQUFJLEVBQUUsUUFBUTtZQUNkLGVBQWUsRUFBRSx3QkFBYSxDQUFDLGNBQWMsRUFBRTtZQUMvQyxVQUFVLEVBQUUsRUFBRTtZQUNkLFVBQVUsRUFBRSxLQUFLLEVBQUUscUNBQXFDO1lBQ3hELFVBQVUsRUFBRSxJQUFJO1lBQ2hCLFlBQVksRUFBRSxJQUFJO1NBQ25CLENBQUM7UUFFRixNQUFNLENBQUMsRUFBRSxHQUFHLFFBQVEsQ0FBQztRQUVyQixPQUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0lBRU8sTUFBTSxDQUFDLHdCQUF3QixDQUNyQyxTQUFpQixFQUNqQixXQUFvRSxFQUNwRSxxQkFBNkM7UUFFN0MsTUFBTSxLQUFLLEdBQUcsZ0NBQWdDLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBRXhGLE9BQU8scUJBQXFCO1lBQzFCLENBQUMsQ0FBQyxxQkFBcUIsQ0FBQztnQkFDcEIsU0FBUztnQkFDVCxTQUFTLEVBQUUsV0FBVztnQkFDdEIsb0JBQW9CLEVBQUUsS0FBSzthQUM1QixDQUFDLElBQUksS0FBSztZQUNiLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFDWixDQUFDO0lBRU0sTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFrQixFQUFFLE1BQWU7UUFDdkQsSUFBSSxDQUFDLEtBQUs7WUFBRSxNQUFNLElBQUksS0FBSyxDQUFDLHVCQUF1QixDQUFDLENBQUM7UUFFckQsT0FBTztZQUNMLE9BQU8sRUFBRSxFQUFFO1lBQ1gsTUFBTSxFQUFFLEVBQUU7WUFDVixTQUFTLEVBQUUsS0FBSyxDQUFDLFdBQVcsSUFBSSxJQUFJO1lBQ3BDLE1BQU0sRUFBRTtnQkFDTixHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FDdkIsS0FBSyxDQUFDLElBQUksRUFDVixLQUFLLENBQUMsYUFBYSxFQUFFLEVBQ3JCLEtBQUssQ0FBQyxxQkFBcUIsRUFDM0IsTUFBTSxDQUNQO2FBQ0Y7WUFDRCxVQUFVLEVBQUUsS0FBSyxFQUFFLHNFQUFzRTtZQUN6RixRQUFRLEVBQUUsRUFBRTtTQUNiLENBQUM7SUFDSixDQUFDO0NBQ0Y7QUE3RkQsbURBNkZDIn0=
|