@ductape/sdk 0.0.4-v5 → 0.0.4-v51
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 +1 -1
- package/dist/api/services/appApi.service.d.ts +48 -2
- package/dist/api/services/appApi.service.js +101 -2
- package/dist/api/services/appApi.service.js.map +1 -1
- package/dist/api/services/pricingApi.service.d.ts +10 -0
- package/dist/api/services/pricingApi.service.js +34 -0
- package/dist/api/services/pricingApi.service.js.map +1 -0
- package/dist/api/services/processorApi.service.d.ts +12 -2
- package/dist/api/services/processorApi.service.js +12 -2
- package/dist/api/services/processorApi.service.js.map +1 -1
- package/dist/api/services/productsApi.service.d.ts +40 -1
- package/dist/api/services/productsApi.service.js +105 -1
- package/dist/api/services/productsApi.service.js.map +1 -1
- package/dist/api/services/userApi.service.js +1 -0
- package/dist/api/services/userApi.service.js.map +1 -1
- package/dist/api/services/workspaceApi.service.js +1 -0
- package/dist/api/services/workspaceApi.service.js.map +1 -1
- package/dist/api/services/workspaceSecretsApi.service.d.ts +75 -0
- package/dist/api/services/workspaceSecretsApi.service.js +62 -0
- package/dist/api/services/workspaceSecretsApi.service.js.map +1 -0
- package/dist/api/urls.d.ts +6 -1
- package/dist/api/urls.js +12 -2
- package/dist/api/urls.js.map +1 -1
- package/dist/api/utils/cache.utils.d.ts +1 -1
- package/dist/api/utils/cache.utils.js +10 -4
- package/dist/api/utils/cache.utils.js.map +1 -1
- package/dist/api/utils/strings.utils.d.ts +2 -0
- package/dist/api/utils/strings.utils.js +14 -0
- package/dist/api/utils/strings.utils.js.map +1 -1
- package/dist/apps/services/app.service.d.ts +51 -33
- package/dist/apps/services/app.service.js +488 -244
- package/dist/apps/services/app.service.js.map +1 -1
- package/dist/apps/validators/joi-validators/create.appAction.validator.d.ts +1 -2
- package/dist/apps/validators/joi-validators/create.appAction.validator.js +21 -2
- package/dist/apps/validators/joi-validators/create.appAction.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/create.appWebhook.validator.d.ts +1 -2
- package/dist/apps/validators/joi-validators/create.appWebhook.validator.js +2 -15
- package/dist/apps/validators/joi-validators/create.appWebhook.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appAction.validator.js +11 -1
- package/dist/apps/validators/joi-validators/update.appAction.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appActionResponse.validator.d.ts +1 -1
- package/dist/apps/validators/joi-validators/update.appActionResponse.validator.js +34 -1
- package/dist/apps/validators/joi-validators/update.appActionResponse.validator.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appWebhook.validator.d.ts +1 -2
- package/dist/apps/validators/joi-validators/update.appWebhook.validator.js +2 -14
- package/dist/apps/validators/joi-validators/update.appWebhook.validator.js.map +1 -1
- package/dist/clients/pricing.client.d.ts +3 -0
- package/dist/clients/pricing.client.js +33 -0
- package/dist/clients/pricing.client.js.map +1 -0
- package/dist/database/adapters/base.adapter.d.ts +176 -0
- package/dist/database/adapters/base.adapter.js +31 -0
- package/dist/database/adapters/base.adapter.js.map +1 -0
- package/dist/database/adapters/dynamodb.adapter.d.ts +91 -0
- package/dist/database/adapters/dynamodb.adapter.js +1608 -0
- package/dist/database/adapters/dynamodb.adapter.js.map +1 -0
- package/dist/database/adapters/mongodb.adapter.d.ts +71 -0
- package/dist/database/adapters/mongodb.adapter.js +1072 -0
- package/dist/database/adapters/mongodb.adapter.js.map +1 -0
- package/dist/database/adapters/mysql.adapter.d.ts +146 -0
- package/dist/database/adapters/mysql.adapter.js +1492 -0
- package/dist/database/adapters/mysql.adapter.js.map +1 -0
- package/dist/database/adapters/postgresql.adapter.d.ts +147 -0
- package/dist/database/adapters/postgresql.adapter.js +1603 -0
- package/dist/database/adapters/postgresql.adapter.js.map +1 -0
- package/dist/database/database.service.d.ts +232 -0
- package/dist/database/database.service.js +802 -0
- package/dist/database/database.service.js.map +1 -0
- package/dist/database/index.d.ts +18 -0
- package/dist/database/index.js +98 -0
- package/dist/database/index.js.map +1 -0
- package/dist/database/types/aggregation.types.d.ts +261 -0
- package/dist/database/types/aggregation.types.js +21 -0
- package/dist/database/types/aggregation.types.js.map +1 -0
- package/dist/database/types/connection.types.d.ts +132 -0
- package/dist/database/types/connection.types.js +6 -0
- package/dist/database/types/connection.types.js.map +1 -0
- package/dist/database/types/database.types.d.ts +175 -0
- package/dist/database/types/database.types.js +75 -0
- package/dist/database/types/database.types.js.map +1 -0
- package/dist/database/types/index.d.ts +12 -0
- package/dist/database/types/index.js +37 -0
- package/dist/database/types/index.js.map +1 -0
- package/dist/database/types/index.types.d.ts +220 -0
- package/dist/database/types/index.types.js +27 -0
- package/dist/database/types/index.types.js.map +1 -0
- package/dist/database/types/migration.types.d.ts +205 -0
- package/dist/database/types/migration.types.js +44 -0
- package/dist/database/types/migration.types.js.map +1 -0
- package/dist/database/types/query.types.d.ts +305 -0
- package/dist/database/types/query.types.js +57 -0
- package/dist/database/types/query.types.js.map +1 -0
- package/dist/database/types/result.types.d.ts +220 -0
- package/dist/database/types/result.types.js +6 -0
- package/dist/database/types/result.types.js.map +1 -0
- package/dist/database/types/schema.types.d.ts +190 -0
- package/dist/database/types/schema.types.js +69 -0
- package/dist/database/types/schema.types.js.map +1 -0
- package/dist/database/utils/helpers.d.ts +66 -0
- package/dist/database/utils/helpers.js +501 -0
- package/dist/database/utils/helpers.js.map +1 -0
- package/dist/database/utils/migration.utils.d.ts +151 -0
- package/dist/database/utils/migration.utils.js +476 -0
- package/dist/database/utils/migration.utils.js.map +1 -0
- package/dist/database/utils/transaction.d.ts +64 -0
- package/dist/database/utils/transaction.js +130 -0
- package/dist/database/utils/transaction.js.map +1 -0
- package/dist/database/validators/connection.validator.d.ts +20 -0
- package/dist/database/validators/connection.validator.js +267 -0
- package/dist/database/validators/connection.validator.js.map +1 -0
- package/dist/database/validators/query.validator.d.ts +31 -0
- package/dist/database/validators/query.validator.js +305 -0
- package/dist/database/validators/query.validator.js.map +1 -0
- package/dist/database/validators/schema.validator.d.ts +31 -0
- package/dist/database/validators/schema.validator.js +334 -0
- package/dist/database/validators/schema.validator.js.map +1 -0
- package/dist/graph/adapters/arangodb.adapter.d.ts +80 -0
- package/dist/graph/adapters/arangodb.adapter.js +1393 -0
- package/dist/graph/adapters/arangodb.adapter.js.map +1 -0
- package/dist/graph/adapters/base.adapter.d.ts +228 -0
- package/dist/graph/adapters/base.adapter.js +38 -0
- package/dist/graph/adapters/base.adapter.js.map +1 -0
- package/dist/graph/adapters/index.d.ts +10 -0
- package/dist/graph/adapters/index.js +23 -0
- package/dist/graph/adapters/index.js.map +1 -0
- package/dist/graph/adapters/memgraph.adapter.d.ts +85 -0
- package/dist/graph/adapters/memgraph.adapter.js +1491 -0
- package/dist/graph/adapters/memgraph.adapter.js.map +1 -0
- package/dist/graph/adapters/neo4j.adapter.d.ts +88 -0
- package/dist/graph/adapters/neo4j.adapter.js +1861 -0
- package/dist/graph/adapters/neo4j.adapter.js.map +1 -0
- package/dist/graph/adapters/neptune.adapter.d.ts +87 -0
- package/dist/graph/adapters/neptune.adapter.js +1430 -0
- package/dist/graph/adapters/neptune.adapter.js.map +1 -0
- package/dist/graph/graph.service.d.ts +278 -0
- package/dist/graph/graph.service.js +687 -0
- package/dist/graph/graph.service.js.map +1 -0
- package/dist/graph/index.d.ts +11 -0
- package/dist/graph/index.js +42 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/types/connection.types.d.ts +158 -0
- package/dist/graph/types/connection.types.js +43 -0
- package/dist/graph/types/connection.types.js.map +1 -0
- package/dist/graph/types/graph.types.d.ts +144 -0
- package/dist/graph/types/graph.types.js +84 -0
- package/dist/graph/types/graph.types.js.map +1 -0
- package/dist/graph/types/index.d.ts +11 -0
- package/dist/graph/types/index.js +35 -0
- package/dist/graph/types/index.js.map +1 -0
- package/dist/graph/types/node.types.d.ts +193 -0
- package/dist/graph/types/node.types.js +49 -0
- package/dist/graph/types/node.types.js.map +1 -0
- package/dist/graph/types/path.types.d.ts +224 -0
- package/dist/graph/types/path.types.js +38 -0
- package/dist/graph/types/path.types.js.map +1 -0
- package/dist/graph/types/query.types.d.ts +247 -0
- package/dist/graph/types/query.types.js +23 -0
- package/dist/graph/types/query.types.js.map +1 -0
- package/dist/graph/types/relationship.types.d.ts +224 -0
- package/dist/graph/types/relationship.types.js +35 -0
- package/dist/graph/types/relationship.types.js.map +1 -0
- package/dist/graph/types/result.types.d.ts +237 -0
- package/dist/graph/types/result.types.js +7 -0
- package/dist/graph/types/result.types.js.map +1 -0
- package/dist/graph/validators/index.d.ts +81 -0
- package/dist/graph/validators/index.js +243 -0
- package/dist/graph/validators/index.js.map +1 -0
- package/dist/imports/imports.service.d.ts +3 -3
- package/dist/imports/imports.service.js +7 -7
- package/dist/imports/imports.service.js.map +1 -1
- package/dist/imports/imports.types.d.ts +8 -0
- package/dist/imports/repos/postmanV21.repo.d.ts +1 -1
- package/dist/imports/repos/postmanV21.repo.js +29 -2
- package/dist/imports/repos/postmanV21.repo.js.map +1 -1
- package/dist/index.d.ts +1244 -150
- package/dist/index.js +1309 -209
- package/dist/index.js.map +1 -1
- package/dist/inputs/inputs.service.js +2 -2
- package/dist/inputs/inputs.service.js.map +1 -1
- package/dist/inputs/utils/inputs.utils.create.js +1 -1
- package/dist/inputs/utils/inputs.utils.create.js.map +1 -1
- package/dist/logs/logs.types.d.ts +5 -0
- package/dist/logs/logs.types.js +1 -0
- package/dist/logs/logs.types.js.map +1 -1
- package/dist/parsers/index.d.ts +3 -0
- package/dist/parsers/index.js +27 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/pipelines/postman.pipelines.d.ts +15 -0
- package/dist/parsers/pipelines/postman.pipelines.js +103 -0
- package/dist/parsers/pipelines/postman.pipelines.js.map +1 -0
- package/dist/parsers/types/postman.types.d.ts +200 -0
- package/dist/parsers/types/postman.types.js +3 -0
- package/dist/parsers/types/postman.types.js.map +1 -0
- package/dist/parsers/utils/postman.utils.d.ts +12 -0
- package/dist/parsers/utils/postman.utils.js +116 -0
- package/dist/parsers/utils/postman.utils.js.map +1 -0
- package/dist/parsers/validators/postman-auth.validators.d.ts +10 -0
- package/dist/parsers/validators/postman-auth.validators.js +127 -0
- package/dist/parsers/validators/postman-auth.validators.js.map +1 -0
- package/dist/parsers/validators/postman-request.validators.d.ts +13 -0
- package/dist/parsers/validators/postman-request.validators.js +139 -0
- package/dist/parsers/validators/postman-request.validators.js.map +1 -0
- package/dist/parsers/validators/postman-response.validators.d.ts +13 -0
- package/dist/parsers/validators/postman-response.validators.js +150 -0
- package/dist/parsers/validators/postman-response.validators.js.map +1 -0
- package/dist/parsers/validators/postman-variable.validators.d.ts +14 -0
- package/dist/parsers/validators/postman-variable.validators.js +163 -0
- package/dist/parsers/validators/postman-variable.validators.js.map +1 -0
- package/dist/pricing/pricing.repo.d.ts +0 -0
- package/dist/pricing/pricing.repo.js +1 -0
- package/dist/pricing/pricing.repo.js.map +1 -0
- package/dist/pricing/pricing.service.d.ts +24 -0
- package/dist/pricing/pricing.service.js +51 -0
- package/dist/pricing/pricing.service.js.map +1 -0
- package/dist/pricing/pricing.types.d.ts +76 -0
- package/dist/pricing/pricing.types.js +21 -0
- package/dist/pricing/pricing.types.js.map +1 -0
- package/dist/pricing/utils/string.utils.d.ts +1 -0
- package/dist/pricing/utils/string.utils.js +9 -0
- package/dist/pricing/utils/string.utils.js.map +1 -0
- package/dist/processor/repos/sms.repo.d.ts +4 -4
- package/dist/processor/repos/sms.repo.js +23 -10
- package/dist/processor/repos/sms.repo.js.map +1 -1
- package/dist/processor/services/messagebrokers/kafka.service.js +0 -2
- package/dist/processor/services/messagebrokers/kafka.service.js.map +1 -1
- package/dist/processor/services/messagebrokers/rabbitmq.service.d.ts +9 -1
- package/dist/processor/services/messagebrokers/rabbitmq.service.js +40 -11
- package/dist/processor/services/messagebrokers/rabbitmq.service.js.map +1 -1
- package/dist/processor/services/processor.service.d.ts +38 -10
- package/dist/processor/services/processor.service.js +533 -248
- package/dist/processor/services/processor.service.js.map +1 -1
- package/dist/processor/services/request.service.d.ts +36 -0
- package/dist/processor/services/request.service.js +304 -0
- package/dist/processor/services/request.service.js.map +1 -0
- package/dist/processor/types/request.types.d.ts +14 -0
- package/dist/processor/types/request.types.js +3 -0
- package/dist/processor/types/request.types.js.map +1 -0
- package/dist/processor/utils/processor.utils.d.ts +3 -0
- package/dist/processor/utils/processor.utils.js +84 -22
- package/dist/processor/utils/processor.utils.js.map +1 -1
- package/dist/processor/utils/request.utils.d.ts +20 -0
- package/dist/processor/utils/request.utils.js +113 -0
- package/dist/processor/utils/request.utils.js.map +1 -0
- package/dist/products/services/products.service.d.ts +114 -77
- package/dist/products/services/products.service.js +805 -362
- package/dist/products/services/products.service.js.map +1 -1
- package/dist/products/services/utils/crypt.utils.d.ts +1 -0
- package/dist/products/services/utils/crypt.utils.js +17 -0
- package/dist/products/services/utils/crypt.utils.js.map +1 -0
- package/dist/products/services/utils/functions.utils.d.ts +13 -0
- package/dist/products/services/utils/functions.utils.js +289 -0
- package/dist/products/services/utils/functions.utils.js.map +1 -0
- package/dist/products/services/utils/objects.utils.d.ts +13 -0
- package/dist/products/services/utils/objects.utils.js +89 -0
- package/dist/products/services/utils/objects.utils.js.map +1 -0
- package/dist/products/services/utils/string.utils.d.ts +12 -0
- package/dist/products/services/utils/string.utils.js +168 -0
- package/dist/products/services/utils/string.utils.js.map +1 -0
- package/dist/products/utils/string.utils.d.ts +1 -1
- package/dist/products/utils/string.utils.js +14 -2
- package/dist/products/utils/string.utils.js.map +1 -1
- package/dist/products/validators/index.d.ts +4 -1
- package/dist/products/validators/index.js +7 -1
- package/dist/products/validators/index.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productDatabaseAction.validator.d.ts +15 -4
- package/dist/products/validators/joi-validators/create.productDatabaseAction.validator.js +501 -109
- package/dist/products/validators/joi-validators/create.productDatabaseAction.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productEnv.validator.js +1 -0
- package/dist/products/validators/joi-validators/create.productEnv.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productGraph.validator.d.ts +3 -0
- package/dist/products/validators/joi-validators/create.productGraph.validator.js +82 -0
- package/dist/products/validators/joi-validators/create.productGraph.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/create.productGraphAction.validator.d.ts +14 -0
- package/dist/products/validators/joi-validators/create.productGraphAction.validator.js +696 -0
- package/dist/products/validators/joi-validators/create.productGraphAction.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/create.productHealthcheck.validator.d.ts +4 -0
- package/dist/products/validators/joi-validators/create.productHealthcheck.validator.js +58 -0
- package/dist/products/validators/joi-validators/create.productHealthcheck.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/create.productMessageBrokerTopic.validator.js +1 -0
- package/dist/products/validators/joi-validators/create.productMessageBrokerTopic.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productMessageBrokers.validator.js +9 -4
- package/dist/products/validators/joi-validators/create.productMessageBrokers.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productNotification.validator.js +5 -2
- package/dist/products/validators/joi-validators/create.productNotification.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.userAuth.validator.js +1 -0
- package/dist/products/validators/joi-validators/create.userAuth.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.dataValue.validator.js +1 -0
- package/dist/products/validators/joi-validators/update.dataValue.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productDatabaseAction.validator.d.ts +6 -0
- package/dist/products/validators/joi-validators/update.productDatabaseAction.validator.js +28 -26
- package/dist/products/validators/joi-validators/update.productDatabaseAction.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productEnv.validator.js +3 -0
- package/dist/products/validators/joi-validators/update.productEnv.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.productGraph.validator.d.ts +3 -0
- package/dist/products/validators/joi-validators/update.productGraph.validator.js +87 -0
- package/dist/products/validators/joi-validators/update.productGraph.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/update.productGraphAction.validator.d.ts +7 -0
- package/dist/products/validators/joi-validators/update.productGraphAction.validator.js +72 -0
- package/dist/products/validators/joi-validators/update.productGraphAction.validator.js.map +1 -0
- package/dist/products/validators/joi-validators/update.productMessageBrokerTopic.validator.js +1 -0
- package/dist/products/validators/joi-validators/update.productMessageBrokerTopic.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/update.userAuth.validator.js +1 -0
- package/dist/products/validators/joi-validators/update.userAuth.validator.js.map +1 -1
- package/dist/test/test.health.d.ts +1 -0
- package/dist/test/test.health.js +49 -0
- package/dist/test/test.health.js.map +1 -0
- package/dist/test/test.import.js +51 -4
- package/dist/test/test.import.js.map +1 -1
- package/dist/test/test.imports.js +22 -7
- package/dist/test/test.imports.js.map +1 -1
- package/dist/test/test.notifiers.d.ts +1 -0
- package/dist/test/test.notifiers.js +85 -0
- package/dist/test/test.notifiers.js.map +1 -0
- package/dist/test/test.processor.js +30 -115
- package/dist/test/test.processor.js.map +1 -1
- package/dist/test/test.products.d.ts +1 -0
- package/dist/test/test.products.js +49 -0
- package/dist/test/test.products.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/appBuilder.types.d.ts +5 -12
- package/dist/types/enums.d.ts +4 -1
- package/dist/types/enums.js +3 -0
- package/dist/types/enums.js.map +1 -1
- package/dist/types/index.types.d.ts +4 -0
- package/dist/types/pricing.types.d.ts +4 -0
- package/dist/types/pricing.types.js +3 -0
- package/dist/types/pricing.types.js.map +1 -0
- package/dist/types/processor.types.d.ts +67 -11
- package/dist/types/processor.types.js.map +1 -1
- package/dist/types/productsBuilder.types.d.ts +132 -14
- package/dist/types/productsBuilder.types.js +69 -4
- package/dist/types/productsBuilder.types.js.map +1 -1
- package/dist/types/request-tracker.interface.d.ts +0 -0
- package/dist/types/request-tracker.interface.js +1 -0
- package/dist/types/request-tracker.interface.js.map +1 -0
- package/dist/utils/constants.d.ts +1 -0
- package/dist/utils/constants.js +5 -0
- package/dist/utils/constants.js.map +1 -0
- package/package.json +17 -3
|
@@ -0,0 +1,1608 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DynamoDB Database Adapter
|
|
4
|
+
* Implements database operations for Amazon DynamoDB
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.DynamoDBAdapter = void 0;
|
|
41
|
+
const base_adapter_1 = require("./base.adapter");
|
|
42
|
+
const database_types_1 = require("../types/database.types");
|
|
43
|
+
const uuid_1 = require("uuid");
|
|
44
|
+
const schema_types_1 = require("../types/schema.types");
|
|
45
|
+
const migration_types_1 = require("../types/migration.types");
|
|
46
|
+
/**
|
|
47
|
+
* DynamoDB Connection wrapper
|
|
48
|
+
*/
|
|
49
|
+
class DynamoDBConnection {
|
|
50
|
+
constructor(id, client, docClient) {
|
|
51
|
+
this.id = id;
|
|
52
|
+
this.type = database_types_1.DatabaseType.DYNAMODB;
|
|
53
|
+
this.status = database_types_1.ConnectionStatus.CONNECTED;
|
|
54
|
+
this.client = client;
|
|
55
|
+
this.docClient = docClient;
|
|
56
|
+
}
|
|
57
|
+
async connect() {
|
|
58
|
+
this.status = database_types_1.ConnectionStatus.CONNECTED;
|
|
59
|
+
}
|
|
60
|
+
async disconnect() {
|
|
61
|
+
this.client.destroy();
|
|
62
|
+
this.status = database_types_1.ConnectionStatus.DISCONNECTED;
|
|
63
|
+
}
|
|
64
|
+
isConnected() {
|
|
65
|
+
return this.status === database_types_1.ConnectionStatus.CONNECTED;
|
|
66
|
+
}
|
|
67
|
+
getClient() {
|
|
68
|
+
return this.docClient;
|
|
69
|
+
}
|
|
70
|
+
getRawClient() {
|
|
71
|
+
return this.client;
|
|
72
|
+
}
|
|
73
|
+
getType() {
|
|
74
|
+
return this.type;
|
|
75
|
+
}
|
|
76
|
+
getConfig() {
|
|
77
|
+
throw new Error('Config not stored in connection');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* DynamoDB Transaction implementation
|
|
82
|
+
* Note: DynamoDB transactions work differently from SQL - they collect items
|
|
83
|
+
* and execute them atomically on commit using TransactWriteItems.
|
|
84
|
+
* Maximum 100 items per transaction.
|
|
85
|
+
* Does NOT support savepoints.
|
|
86
|
+
*/
|
|
87
|
+
class DynamoDBTransaction {
|
|
88
|
+
constructor(connection, _options) {
|
|
89
|
+
this.transactItems = [];
|
|
90
|
+
this.id = (0, uuid_1.v4)();
|
|
91
|
+
this.connection = connection;
|
|
92
|
+
this.status = database_types_1.TransactionStatus.ACTIVE;
|
|
93
|
+
this.createdAt = new Date();
|
|
94
|
+
this.native = { items: this.transactItems };
|
|
95
|
+
}
|
|
96
|
+
async commit() {
|
|
97
|
+
if (this.status !== database_types_1.TransactionStatus.ACTIVE) {
|
|
98
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.TRANSACTION_ERROR, `Cannot commit transaction: status is ${this.status}`);
|
|
99
|
+
}
|
|
100
|
+
if (this.transactItems.length === 0) {
|
|
101
|
+
this.status = database_types_1.TransactionStatus.COMMITTED;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (this.transactItems.length > 100) {
|
|
105
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.TRANSACTION_ERROR, `DynamoDB transactions are limited to 100 items. Current: ${this.transactItems.length}`);
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
const { TransactWriteCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
109
|
+
const docClient = this.connection.getClient();
|
|
110
|
+
await docClient.send(new TransactWriteCommand({
|
|
111
|
+
TransactItems: this.transactItems,
|
|
112
|
+
}));
|
|
113
|
+
this.status = database_types_1.TransactionStatus.COMMITTED;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
this.status = database_types_1.TransactionStatus.ROLLED_BACK;
|
|
117
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.TRANSACTION_ERROR, `DynamoDB transaction commit failed: ${error.message}`, error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async rollback() {
|
|
121
|
+
if (this.status !== database_types_1.TransactionStatus.ACTIVE) {
|
|
122
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.TRANSACTION_ERROR, `Cannot rollback transaction: status is ${this.status}`);
|
|
123
|
+
}
|
|
124
|
+
// DynamoDB transactions are atomic - just clear items and mark as rolled back
|
|
125
|
+
this.transactItems = [];
|
|
126
|
+
this.status = database_types_1.TransactionStatus.ROLLED_BACK;
|
|
127
|
+
}
|
|
128
|
+
async savepoint(_name) {
|
|
129
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.NOT_SUPPORTED, 'DynamoDB does not support savepoints. Transactions are atomic batch operations.');
|
|
130
|
+
}
|
|
131
|
+
isActive() {
|
|
132
|
+
return this.status === database_types_1.TransactionStatus.ACTIVE;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Add an item to the transaction batch
|
|
136
|
+
* Used internally by the adapter for transactional operations
|
|
137
|
+
*/
|
|
138
|
+
addItem(item) {
|
|
139
|
+
if (this.transactItems.length >= 100) {
|
|
140
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.TRANSACTION_ERROR, 'DynamoDB transactions are limited to 100 items');
|
|
141
|
+
}
|
|
142
|
+
this.transactItems.push(item);
|
|
143
|
+
}
|
|
144
|
+
getItems() {
|
|
145
|
+
return this.transactItems;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* DynamoDB Database Adapter
|
|
150
|
+
*/
|
|
151
|
+
class DynamoDBAdapter extends base_adapter_1.BaseDatabaseAdapter {
|
|
152
|
+
constructor() {
|
|
153
|
+
super(...arguments);
|
|
154
|
+
this.type = database_types_1.DatabaseType.DYNAMODB;
|
|
155
|
+
}
|
|
156
|
+
// ==================== Connection Methods ====================
|
|
157
|
+
async connect(config) {
|
|
158
|
+
var _a, _b, _c, _d;
|
|
159
|
+
try {
|
|
160
|
+
// Dynamic import of AWS SDK v3
|
|
161
|
+
const { DynamoDBClient } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
162
|
+
const { DynamoDBDocumentClient } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
163
|
+
const clientConfig = {
|
|
164
|
+
region: ((_a = config.options) === null || _a === void 0 ? void 0 : _a.region) || 'us-east-1',
|
|
165
|
+
};
|
|
166
|
+
// Add credentials if provided
|
|
167
|
+
if (((_b = config.options) === null || _b === void 0 ? void 0 : _b.accessKeyId) && ((_c = config.options) === null || _c === void 0 ? void 0 : _c.secretAccessKey)) {
|
|
168
|
+
clientConfig.credentials = {
|
|
169
|
+
accessKeyId: config.options.accessKeyId,
|
|
170
|
+
secretAccessKey: config.options.secretAccessKey,
|
|
171
|
+
sessionToken: config.options.sessionToken,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// Add endpoint for local development
|
|
175
|
+
if ((_d = config.options) === null || _d === void 0 ? void 0 : _d.endpoint) {
|
|
176
|
+
clientConfig.endpoint = config.options.endpoint;
|
|
177
|
+
}
|
|
178
|
+
const client = new DynamoDBClient(clientConfig);
|
|
179
|
+
const docClient = DynamoDBDocumentClient.from(client);
|
|
180
|
+
return new DynamoDBConnection(`dynamodb-${Date.now()}`, client, docClient);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.CONNECTION_ERROR, `Failed to connect to DynamoDB: ${error.message}`, error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async disconnect(connection) {
|
|
187
|
+
await connection.disconnect();
|
|
188
|
+
}
|
|
189
|
+
async testConnection(connection) {
|
|
190
|
+
const startTime = Date.now();
|
|
191
|
+
try {
|
|
192
|
+
const { ListTablesCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
193
|
+
const client = connection.getRawClient();
|
|
194
|
+
await client.send(new ListTablesCommand({ Limit: 1 }));
|
|
195
|
+
return {
|
|
196
|
+
connected: true,
|
|
197
|
+
message: 'DynamoDB connection successful',
|
|
198
|
+
databaseType: 'DynamoDB',
|
|
199
|
+
responseTime: Date.now() - startTime,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
return {
|
|
204
|
+
connected: false,
|
|
205
|
+
message: `DynamoDB connection failed: ${error.message}`,
|
|
206
|
+
databaseType: 'DynamoDB',
|
|
207
|
+
responseTime: Date.now() - startTime,
|
|
208
|
+
error: error.message,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// ==================== Transaction Methods ====================
|
|
213
|
+
async beginTransaction(connection, options) {
|
|
214
|
+
// DynamoDB transactions collect items and execute them atomically on commit
|
|
215
|
+
return new DynamoDBTransaction(connection, options);
|
|
216
|
+
}
|
|
217
|
+
async commitTransaction(_connection, transaction) {
|
|
218
|
+
await transaction.commit();
|
|
219
|
+
}
|
|
220
|
+
async rollbackTransaction(_connection, transaction) {
|
|
221
|
+
await transaction.rollback();
|
|
222
|
+
}
|
|
223
|
+
async createSavepoint(_connection, _transaction, _savepointName) {
|
|
224
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.NOT_SUPPORTED, 'DynamoDB does not support savepoints. Transactions are atomic batch operations.');
|
|
225
|
+
}
|
|
226
|
+
async rollbackToSavepoint(_connection, _transaction, _savepoint) {
|
|
227
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.NOT_SUPPORTED, 'DynamoDB does not support savepoints.');
|
|
228
|
+
}
|
|
229
|
+
async releaseSavepoint(_connection, _transaction, _savepoint) {
|
|
230
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.NOT_SUPPORTED, 'DynamoDB does not support savepoints.');
|
|
231
|
+
}
|
|
232
|
+
// ==================== Query Methods ====================
|
|
233
|
+
async query(connection, options) {
|
|
234
|
+
const startTime = Date.now();
|
|
235
|
+
try {
|
|
236
|
+
const { ScanCommand, QueryCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
237
|
+
const docClient = connection.getClient();
|
|
238
|
+
let result;
|
|
239
|
+
// If there's a partition key in where clause, use Query, otherwise use Scan
|
|
240
|
+
const hasPartitionKey = options.where && this.hasPartitionKeyInWhere(options.where);
|
|
241
|
+
if (hasPartitionKey) {
|
|
242
|
+
const params = this.buildQueryParams(options);
|
|
243
|
+
result = await docClient.send(new QueryCommand(params));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
const params = this.buildScanParams(options);
|
|
247
|
+
result = await docClient.send(new ScanCommand(params));
|
|
248
|
+
}
|
|
249
|
+
const executionTime = Date.now() - startTime;
|
|
250
|
+
return {
|
|
251
|
+
data: (result.Items || []),
|
|
252
|
+
count: result.Count || 0,
|
|
253
|
+
executionTime,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB query failed: ${error.message}`, error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async insert(connection, options) {
|
|
261
|
+
const startTime = Date.now();
|
|
262
|
+
try {
|
|
263
|
+
const { PutCommand, BatchWriteCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
264
|
+
const docClient = connection.getClient();
|
|
265
|
+
const dataArray = Array.isArray(options.data) ? options.data : [options.data];
|
|
266
|
+
if (dataArray.length === 1) {
|
|
267
|
+
// Single item insert
|
|
268
|
+
await docClient.send(new PutCommand({
|
|
269
|
+
TableName: options.table,
|
|
270
|
+
Item: dataArray[0],
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
// Batch insert (max 25 items per batch)
|
|
275
|
+
const batches = this.chunkArray(dataArray, 25);
|
|
276
|
+
for (const batch of batches) {
|
|
277
|
+
await docClient.send(new BatchWriteCommand({
|
|
278
|
+
RequestItems: {
|
|
279
|
+
[options.table]: batch.map((item) => ({
|
|
280
|
+
PutRequest: { Item: item },
|
|
281
|
+
})),
|
|
282
|
+
},
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const executionTime = Date.now() - startTime;
|
|
287
|
+
return {
|
|
288
|
+
insertedCount: dataArray.length,
|
|
289
|
+
insertedIds: [], // DynamoDB doesn't auto-generate IDs
|
|
290
|
+
data: undefined,
|
|
291
|
+
executionTime,
|
|
292
|
+
success: true,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB insert failed: ${error.message}`, error);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async update(connection, options) {
|
|
300
|
+
const startTime = Date.now();
|
|
301
|
+
try {
|
|
302
|
+
const { UpdateCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
303
|
+
const docClient = connection.getClient();
|
|
304
|
+
// DynamoDB requires a key to update - extract from where clause
|
|
305
|
+
const key = this.extractKeyFromWhere(options.where);
|
|
306
|
+
// Build update expression
|
|
307
|
+
const { updateExpression, expressionAttributeNames, expressionAttributeValues } = this.buildUpdateExpression(options.data);
|
|
308
|
+
await docClient.send(new UpdateCommand({
|
|
309
|
+
TableName: options.table,
|
|
310
|
+
Key: key,
|
|
311
|
+
UpdateExpression: updateExpression,
|
|
312
|
+
ExpressionAttributeNames: expressionAttributeNames,
|
|
313
|
+
ExpressionAttributeValues: expressionAttributeValues,
|
|
314
|
+
}));
|
|
315
|
+
const executionTime = Date.now() - startTime;
|
|
316
|
+
return {
|
|
317
|
+
updatedCount: 1, // DynamoDB updates one item at a time
|
|
318
|
+
matchedCount: 1,
|
|
319
|
+
data: undefined,
|
|
320
|
+
executionTime,
|
|
321
|
+
success: true,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB update failed: ${error.message}`, error);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async delete(connection, options) {
|
|
329
|
+
const startTime = Date.now();
|
|
330
|
+
try {
|
|
331
|
+
const { DeleteCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
332
|
+
const docClient = connection.getClient();
|
|
333
|
+
// DynamoDB requires a key to delete - extract from where clause
|
|
334
|
+
const key = this.extractKeyFromWhere(options.where);
|
|
335
|
+
await docClient.send(new DeleteCommand({
|
|
336
|
+
TableName: options.table,
|
|
337
|
+
Key: key,
|
|
338
|
+
}));
|
|
339
|
+
const executionTime = Date.now() - startTime;
|
|
340
|
+
return {
|
|
341
|
+
deletedCount: 1,
|
|
342
|
+
data: undefined,
|
|
343
|
+
executionTime,
|
|
344
|
+
success: true,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB delete failed: ${error.message}`, error);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
async upsert(connection, options) {
|
|
352
|
+
const startTime = Date.now();
|
|
353
|
+
try {
|
|
354
|
+
const { PutCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
355
|
+
const docClient = connection.getClient();
|
|
356
|
+
const data = Array.isArray(options.data) ? options.data[0] : options.data;
|
|
357
|
+
// PutItem automatically does upsert in DynamoDB
|
|
358
|
+
await docClient.send(new PutCommand({
|
|
359
|
+
TableName: options.table,
|
|
360
|
+
Item: data,
|
|
361
|
+
}));
|
|
362
|
+
const executionTime = Date.now() - startTime;
|
|
363
|
+
return {
|
|
364
|
+
insertedCount: 0, // Can't distinguish in DynamoDB
|
|
365
|
+
updatedCount: 0,
|
|
366
|
+
affectedCount: 1,
|
|
367
|
+
data: undefined,
|
|
368
|
+
executionTime,
|
|
369
|
+
success: true,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB upsert failed: ${error.message}`, error);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async executeRaw(connection, options) {
|
|
377
|
+
var _a;
|
|
378
|
+
const startTime = Date.now();
|
|
379
|
+
try {
|
|
380
|
+
const { ExecuteStatementCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
381
|
+
const client = connection.getRawClient();
|
|
382
|
+
// DynamoDB PartiQL support
|
|
383
|
+
const result = await client.send(new ExecuteStatementCommand({
|
|
384
|
+
Statement: options.query,
|
|
385
|
+
Parameters: options.params,
|
|
386
|
+
}));
|
|
387
|
+
const executionTime = Date.now() - startTime;
|
|
388
|
+
return {
|
|
389
|
+
rows: (result.Items || []),
|
|
390
|
+
rowCount: ((_a = result.Items) === null || _a === void 0 ? void 0 : _a.length) || 0,
|
|
391
|
+
executionTime,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB raw query failed: ${error.message}`, error);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// ==================== Aggregation Methods ====================
|
|
399
|
+
async count(connection, options) {
|
|
400
|
+
try {
|
|
401
|
+
const { ScanCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
402
|
+
const docClient = connection.getClient();
|
|
403
|
+
const params = {
|
|
404
|
+
TableName: options.table,
|
|
405
|
+
Select: 'COUNT',
|
|
406
|
+
};
|
|
407
|
+
if (options.where) {
|
|
408
|
+
const { filterExpression, expressionAttributeNames, expressionAttributeValues } = this.buildFilterExpression(options.where);
|
|
409
|
+
params.FilterExpression = filterExpression;
|
|
410
|
+
params.ExpressionAttributeNames = expressionAttributeNames;
|
|
411
|
+
params.ExpressionAttributeValues = expressionAttributeValues;
|
|
412
|
+
}
|
|
413
|
+
const result = await docClient.send(new ScanCommand(params));
|
|
414
|
+
return result.Count || 0;
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB count failed: ${error.message}`, error);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async sum(connection, options) {
|
|
421
|
+
// DynamoDB doesn't support aggregations natively - need to scan and calculate
|
|
422
|
+
try {
|
|
423
|
+
const result = await this.query(connection, {
|
|
424
|
+
table: options.table,
|
|
425
|
+
where: options.where,
|
|
426
|
+
env: options.env,
|
|
427
|
+
product: options.product,
|
|
428
|
+
database: options.database,
|
|
429
|
+
});
|
|
430
|
+
return result.data.reduce((sum, item) => sum + (item[options.column] || 0), 0);
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB sum failed: ${error.message}`, error);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
async avg(connection, options) {
|
|
437
|
+
try {
|
|
438
|
+
const result = await this.query(connection, {
|
|
439
|
+
table: options.table,
|
|
440
|
+
where: options.where,
|
|
441
|
+
env: options.env,
|
|
442
|
+
product: options.product,
|
|
443
|
+
database: options.database,
|
|
444
|
+
});
|
|
445
|
+
const sum = result.data.reduce((total, item) => total + (item[options.column] || 0), 0);
|
|
446
|
+
return result.data.length > 0 ? sum / result.data.length : 0;
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB avg failed: ${error.message}`, error);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
async min(connection, options) {
|
|
453
|
+
try {
|
|
454
|
+
const result = await this.query(connection, {
|
|
455
|
+
table: options.table,
|
|
456
|
+
where: options.where,
|
|
457
|
+
env: options.env,
|
|
458
|
+
product: options.product,
|
|
459
|
+
database: options.database,
|
|
460
|
+
});
|
|
461
|
+
if (result.data.length === 0)
|
|
462
|
+
return 0;
|
|
463
|
+
return result.data.reduce((min, item) => {
|
|
464
|
+
const value = item[options.column];
|
|
465
|
+
return value < min ? value : min;
|
|
466
|
+
}, result.data[0][options.column]);
|
|
467
|
+
}
|
|
468
|
+
catch (error) {
|
|
469
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB min failed: ${error.message}`, error);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async max(connection, options) {
|
|
473
|
+
try {
|
|
474
|
+
const result = await this.query(connection, {
|
|
475
|
+
table: options.table,
|
|
476
|
+
where: options.where,
|
|
477
|
+
env: options.env,
|
|
478
|
+
product: options.product,
|
|
479
|
+
database: options.database,
|
|
480
|
+
});
|
|
481
|
+
if (result.data.length === 0)
|
|
482
|
+
return 0;
|
|
483
|
+
return result.data.reduce((max, item) => {
|
|
484
|
+
const value = item[options.column];
|
|
485
|
+
return value > max ? value : max;
|
|
486
|
+
}, result.data[0][options.column]);
|
|
487
|
+
}
|
|
488
|
+
catch (error) {
|
|
489
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB max failed: ${error.message}`, error);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async groupBy(connection, options) {
|
|
493
|
+
try {
|
|
494
|
+
// DynamoDB doesn't support GROUP BY - need to scan and group manually
|
|
495
|
+
// WARNING: This scans the entire table - use with caution on large tables
|
|
496
|
+
const result = await this.query(connection, {
|
|
497
|
+
table: options.table,
|
|
498
|
+
where: options.where,
|
|
499
|
+
env: options.env,
|
|
500
|
+
product: options.product,
|
|
501
|
+
database: options.database,
|
|
502
|
+
});
|
|
503
|
+
const groups = new Map();
|
|
504
|
+
result.data.forEach((item) => {
|
|
505
|
+
// Create group key
|
|
506
|
+
const groupKey = options.groupBy.map((col) => item[col]).join('|');
|
|
507
|
+
if (!groups.has(groupKey)) {
|
|
508
|
+
const groupData = {};
|
|
509
|
+
options.groupBy.forEach((col) => {
|
|
510
|
+
groupData[col] = item[col];
|
|
511
|
+
});
|
|
512
|
+
groups.set(groupKey, groupData);
|
|
513
|
+
}
|
|
514
|
+
// Accumulate aggregations
|
|
515
|
+
if (options.aggregate) {
|
|
516
|
+
const groupData = groups.get(groupKey);
|
|
517
|
+
Object.entries(options.aggregate).forEach(([alias, agg]) => {
|
|
518
|
+
const aggObj = agg;
|
|
519
|
+
// Parse operation and column
|
|
520
|
+
let operation;
|
|
521
|
+
let column;
|
|
522
|
+
if (aggObj.$COUNT !== undefined) {
|
|
523
|
+
operation = 'count';
|
|
524
|
+
column = aggObj.$COUNT;
|
|
525
|
+
}
|
|
526
|
+
else if (aggObj.$SUM !== undefined) {
|
|
527
|
+
operation = 'sum';
|
|
528
|
+
column = aggObj.$SUM;
|
|
529
|
+
}
|
|
530
|
+
else if (aggObj.$AVG !== undefined) {
|
|
531
|
+
operation = 'avg';
|
|
532
|
+
column = aggObj.$AVG;
|
|
533
|
+
}
|
|
534
|
+
else if (aggObj.$MIN !== undefined) {
|
|
535
|
+
operation = 'min';
|
|
536
|
+
column = aggObj.$MIN;
|
|
537
|
+
}
|
|
538
|
+
else if (aggObj.$MAX !== undefined) {
|
|
539
|
+
operation = 'max';
|
|
540
|
+
column = aggObj.$MAX;
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
return; // Skip unknown format
|
|
544
|
+
}
|
|
545
|
+
const value = column === '*' ? 1 : item[column];
|
|
546
|
+
if (groupData[alias] === undefined) {
|
|
547
|
+
groupData[alias] = 0;
|
|
548
|
+
if (operation === 'min')
|
|
549
|
+
groupData[alias] = Infinity;
|
|
550
|
+
if (operation === 'max')
|
|
551
|
+
groupData[alias] = -Infinity;
|
|
552
|
+
}
|
|
553
|
+
switch (operation) {
|
|
554
|
+
case 'count':
|
|
555
|
+
groupData[alias]++;
|
|
556
|
+
break;
|
|
557
|
+
case 'sum':
|
|
558
|
+
groupData[alias] += value || 0;
|
|
559
|
+
break;
|
|
560
|
+
case 'avg':
|
|
561
|
+
if (!groupData[`${alias}_sum`])
|
|
562
|
+
groupData[`${alias}_sum`] = 0;
|
|
563
|
+
if (!groupData[`${alias}_count`])
|
|
564
|
+
groupData[`${alias}_count`] = 0;
|
|
565
|
+
groupData[`${alias}_sum`] += value || 0;
|
|
566
|
+
groupData[`${alias}_count`]++;
|
|
567
|
+
groupData[alias] = groupData[`${alias}_sum`] / groupData[`${alias}_count`];
|
|
568
|
+
break;
|
|
569
|
+
case 'min':
|
|
570
|
+
if (value !== undefined && value < groupData[alias]) {
|
|
571
|
+
groupData[alias] = value;
|
|
572
|
+
}
|
|
573
|
+
break;
|
|
574
|
+
case 'max':
|
|
575
|
+
if (value !== undefined && value > groupData[alias]) {
|
|
576
|
+
groupData[alias] = value;
|
|
577
|
+
}
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
// Clean up helper fields used for avg calculation
|
|
584
|
+
const results = Array.from(groups.values());
|
|
585
|
+
results.forEach((group) => {
|
|
586
|
+
Object.keys(group).forEach((key) => {
|
|
587
|
+
if (key.endsWith('_sum') || key.endsWith('_count')) {
|
|
588
|
+
delete group[key];
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
return results;
|
|
593
|
+
}
|
|
594
|
+
catch (error) {
|
|
595
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB groupBy failed: ${error.message}`, error);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
async aggregate(connection, options) {
|
|
599
|
+
try {
|
|
600
|
+
// WARNING: DynamoDB does NOT support native aggregations
|
|
601
|
+
// This implementation scans the entire table - use with caution on large tables
|
|
602
|
+
const result = await this.query(connection, {
|
|
603
|
+
table: options.table,
|
|
604
|
+
where: options.where,
|
|
605
|
+
env: options.env,
|
|
606
|
+
product: options.product,
|
|
607
|
+
database: options.database,
|
|
608
|
+
});
|
|
609
|
+
const aggregateResult = {};
|
|
610
|
+
Object.entries(options.operations).forEach(([alias, agg]) => {
|
|
611
|
+
const aggObj = agg;
|
|
612
|
+
// Parse operation and column
|
|
613
|
+
let operation;
|
|
614
|
+
let column;
|
|
615
|
+
if (aggObj.$COUNT !== undefined) {
|
|
616
|
+
operation = 'count';
|
|
617
|
+
column = aggObj.$COUNT;
|
|
618
|
+
}
|
|
619
|
+
else if (aggObj.$SUM !== undefined) {
|
|
620
|
+
operation = 'sum';
|
|
621
|
+
column = aggObj.$SUM;
|
|
622
|
+
}
|
|
623
|
+
else if (aggObj.$AVG !== undefined) {
|
|
624
|
+
operation = 'avg';
|
|
625
|
+
column = aggObj.$AVG;
|
|
626
|
+
}
|
|
627
|
+
else if (aggObj.$MIN !== undefined) {
|
|
628
|
+
operation = 'min';
|
|
629
|
+
column = aggObj.$MIN;
|
|
630
|
+
}
|
|
631
|
+
else if (aggObj.$MAX !== undefined) {
|
|
632
|
+
operation = 'max';
|
|
633
|
+
column = aggObj.$MAX;
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
return; // Skip unknown format
|
|
637
|
+
}
|
|
638
|
+
const values = column === '*'
|
|
639
|
+
? result.data.map(() => 1)
|
|
640
|
+
: result.data.map((item) => item[column]);
|
|
641
|
+
switch (operation) {
|
|
642
|
+
case 'count':
|
|
643
|
+
aggregateResult[alias] = result.data.length;
|
|
644
|
+
break;
|
|
645
|
+
case 'sum':
|
|
646
|
+
aggregateResult[alias] = values.reduce((sum, val) => sum + (val || 0), 0);
|
|
647
|
+
break;
|
|
648
|
+
case 'avg':
|
|
649
|
+
const sum = values.reduce((s, val) => s + (val || 0), 0);
|
|
650
|
+
aggregateResult[alias] = values.length > 0 ? sum / values.length : 0;
|
|
651
|
+
break;
|
|
652
|
+
case 'min':
|
|
653
|
+
const minValues = values.filter((v) => v !== undefined && v !== null);
|
|
654
|
+
aggregateResult[alias] = minValues.length > 0 ? Math.min(...minValues) : null;
|
|
655
|
+
break;
|
|
656
|
+
case 'max':
|
|
657
|
+
const maxValues = values.filter((v) => v !== undefined && v !== null);
|
|
658
|
+
aggregateResult[alias] = maxValues.length > 0 ? Math.max(...maxValues) : null;
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
return aggregateResult;
|
|
663
|
+
}
|
|
664
|
+
catch (error) {
|
|
665
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.QUERY_ERROR, `DynamoDB aggregate failed: ${error.message}`, error);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
// ==================== Schema Methods ====================
|
|
669
|
+
async createTable(connection, schema, options) {
|
|
670
|
+
const startTime = Date.now();
|
|
671
|
+
try {
|
|
672
|
+
const { CreateTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
673
|
+
const client = connection.getRawClient();
|
|
674
|
+
// Extract key schema from columns
|
|
675
|
+
const keySchema = [];
|
|
676
|
+
const attributeDefinitions = [];
|
|
677
|
+
schema.columns.forEach((col) => {
|
|
678
|
+
if (col.primaryKey) {
|
|
679
|
+
keySchema.push({
|
|
680
|
+
AttributeName: col.name,
|
|
681
|
+
KeyType: 'HASH', // Partition key
|
|
682
|
+
});
|
|
683
|
+
attributeDefinitions.push({
|
|
684
|
+
AttributeName: col.name,
|
|
685
|
+
AttributeType: this.mapColumnTypeToDynamoDB(col.type),
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
// If no sort key specified, DynamoDB only needs partition key
|
|
690
|
+
await client.send(new CreateTableCommand({
|
|
691
|
+
TableName: schema.name,
|
|
692
|
+
KeySchema: keySchema,
|
|
693
|
+
AttributeDefinitions: attributeDefinitions,
|
|
694
|
+
BillingMode: 'PAY_PER_REQUEST', // On-demand billing
|
|
695
|
+
}));
|
|
696
|
+
const executionTime = Date.now() - startTime;
|
|
697
|
+
return {
|
|
698
|
+
success: true,
|
|
699
|
+
operation: 'create',
|
|
700
|
+
table: schema.name,
|
|
701
|
+
executionTime,
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
return {
|
|
706
|
+
success: false,
|
|
707
|
+
operation: 'create',
|
|
708
|
+
table: schema.name,
|
|
709
|
+
error: error.message,
|
|
710
|
+
executionTime: Date.now() - startTime,
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
async dropTable(connection, tableName) {
|
|
715
|
+
const startTime = Date.now();
|
|
716
|
+
try {
|
|
717
|
+
const { DeleteTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
718
|
+
const client = connection.getRawClient();
|
|
719
|
+
await client.send(new DeleteTableCommand({
|
|
720
|
+
TableName: tableName,
|
|
721
|
+
}));
|
|
722
|
+
return {
|
|
723
|
+
success: true,
|
|
724
|
+
operation: 'drop',
|
|
725
|
+
table: tableName,
|
|
726
|
+
executionTime: Date.now() - startTime,
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
return {
|
|
731
|
+
success: false,
|
|
732
|
+
operation: 'drop',
|
|
733
|
+
table: tableName,
|
|
734
|
+
error: error.message,
|
|
735
|
+
executionTime: Date.now() - startTime,
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
async alterTable(connection, tableName, alterations, options) {
|
|
740
|
+
// DynamoDB doesn't support schema alterations in the traditional sense
|
|
741
|
+
// Can only modify throughput, streams, TTL, etc.
|
|
742
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.SCHEMA_ERROR, 'DynamoDB does not support traditional table alterations. You can only modify table settings.');
|
|
743
|
+
}
|
|
744
|
+
async getTableSchema(connection, tableName) {
|
|
745
|
+
try {
|
|
746
|
+
const { DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
747
|
+
const client = connection.getRawClient();
|
|
748
|
+
const result = await client.send(new DescribeTableCommand({
|
|
749
|
+
TableName: tableName,
|
|
750
|
+
}));
|
|
751
|
+
const table = result.Table;
|
|
752
|
+
const columns = table.KeySchema.map((key) => {
|
|
753
|
+
const attr = table.AttributeDefinitions.find((a) => a.AttributeName === key.AttributeName);
|
|
754
|
+
return {
|
|
755
|
+
name: key.AttributeName,
|
|
756
|
+
type: this.mapDynamoDBTypeToColumnType((attr === null || attr === void 0 ? void 0 : attr.AttributeType) || 'S'),
|
|
757
|
+
primaryKey: key.KeyType === 'HASH',
|
|
758
|
+
nullable: false,
|
|
759
|
+
};
|
|
760
|
+
});
|
|
761
|
+
return {
|
|
762
|
+
name: tableName,
|
|
763
|
+
columns,
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.SCHEMA_ERROR, `Failed to get DynamoDB table schema: ${error.message}`, error);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async listTables(connection) {
|
|
771
|
+
try {
|
|
772
|
+
const { ListTablesCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
773
|
+
const client = connection.getRawClient();
|
|
774
|
+
const result = await client.send(new ListTablesCommand({}));
|
|
775
|
+
return result.TableNames || [];
|
|
776
|
+
}
|
|
777
|
+
catch (error) {
|
|
778
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.SCHEMA_ERROR, `Failed to list DynamoDB tables: ${error.message}`, error);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
async tableExists(connection, tableName) {
|
|
782
|
+
try {
|
|
783
|
+
const { DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
784
|
+
const client = connection.getRawClient();
|
|
785
|
+
await client.send(new DescribeTableCommand({
|
|
786
|
+
TableName: tableName,
|
|
787
|
+
}));
|
|
788
|
+
return true;
|
|
789
|
+
}
|
|
790
|
+
catch (error) {
|
|
791
|
+
if (error.name === 'ResourceNotFoundException') {
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.SCHEMA_ERROR, `Failed to check DynamoDB table existence: ${error.message}`, error);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
// ==================== Index Methods ====================
|
|
798
|
+
async createIndex(connection, options) {
|
|
799
|
+
const startTime = Date.now();
|
|
800
|
+
try {
|
|
801
|
+
const { UpdateTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
802
|
+
const client = connection.getRawClient();
|
|
803
|
+
// Build GSI definition
|
|
804
|
+
const keySchema = options.index.columns.map((col, idx) => ({
|
|
805
|
+
AttributeName: col.name,
|
|
806
|
+
KeyType: idx === 0 ? 'HASH' : 'RANGE',
|
|
807
|
+
}));
|
|
808
|
+
const attributeDefinitions = options.index.columns.map((col) => ({
|
|
809
|
+
AttributeName: col.name,
|
|
810
|
+
AttributeType: 'S', // Default to String
|
|
811
|
+
}));
|
|
812
|
+
await client.send(new UpdateTableCommand({
|
|
813
|
+
TableName: options.table,
|
|
814
|
+
AttributeDefinitions: attributeDefinitions,
|
|
815
|
+
GlobalSecondaryIndexUpdates: [
|
|
816
|
+
{
|
|
817
|
+
Create: {
|
|
818
|
+
IndexName: options.index.name,
|
|
819
|
+
KeySchema: keySchema,
|
|
820
|
+
Projection: {
|
|
821
|
+
ProjectionType: 'ALL',
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
},
|
|
825
|
+
],
|
|
826
|
+
}));
|
|
827
|
+
return {
|
|
828
|
+
success: true,
|
|
829
|
+
operation: 'create',
|
|
830
|
+
indexName: options.index.name,
|
|
831
|
+
table: options.table,
|
|
832
|
+
executionTime: Date.now() - startTime,
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
catch (error) {
|
|
836
|
+
return {
|
|
837
|
+
success: false,
|
|
838
|
+
operation: 'create',
|
|
839
|
+
indexName: options.index.name,
|
|
840
|
+
table: options.table,
|
|
841
|
+
error: error.message,
|
|
842
|
+
executionTime: Date.now() - startTime,
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
async dropIndex(connection, options) {
|
|
847
|
+
const startTime = Date.now();
|
|
848
|
+
try {
|
|
849
|
+
const { UpdateTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
850
|
+
const client = connection.getRawClient();
|
|
851
|
+
await client.send(new UpdateTableCommand({
|
|
852
|
+
TableName: options.table,
|
|
853
|
+
GlobalSecondaryIndexUpdates: [
|
|
854
|
+
{
|
|
855
|
+
Delete: {
|
|
856
|
+
IndexName: options.indexName,
|
|
857
|
+
},
|
|
858
|
+
},
|
|
859
|
+
],
|
|
860
|
+
}));
|
|
861
|
+
return {
|
|
862
|
+
success: true,
|
|
863
|
+
operation: 'drop',
|
|
864
|
+
indexName: options.indexName,
|
|
865
|
+
table: options.table,
|
|
866
|
+
executionTime: Date.now() - startTime,
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
catch (error) {
|
|
870
|
+
return {
|
|
871
|
+
success: false,
|
|
872
|
+
operation: 'drop',
|
|
873
|
+
indexName: options.indexName,
|
|
874
|
+
table: options.table,
|
|
875
|
+
error: error.message,
|
|
876
|
+
executionTime: Date.now() - startTime,
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
async listIndexes(connection, options) {
|
|
881
|
+
var _a, _b;
|
|
882
|
+
try {
|
|
883
|
+
const { DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
884
|
+
const client = connection.getRawClient();
|
|
885
|
+
const result = await client.send(new DescribeTableCommand({
|
|
886
|
+
TableName: options.table,
|
|
887
|
+
}));
|
|
888
|
+
const indexes = [];
|
|
889
|
+
// Add GSIs
|
|
890
|
+
if ((_a = result.Table) === null || _a === void 0 ? void 0 : _a.GlobalSecondaryIndexes) {
|
|
891
|
+
result.Table.GlobalSecondaryIndexes.forEach((gsi) => {
|
|
892
|
+
indexes.push({
|
|
893
|
+
name: gsi.IndexName,
|
|
894
|
+
table: options.table,
|
|
895
|
+
columns: gsi.KeySchema.map((key) => key.AttributeName),
|
|
896
|
+
columnDetails: gsi.KeySchema.map((key) => ({
|
|
897
|
+
name: key.AttributeName,
|
|
898
|
+
order: 'ASC',
|
|
899
|
+
})),
|
|
900
|
+
unique: false,
|
|
901
|
+
primaryKey: false,
|
|
902
|
+
type: 'GSI',
|
|
903
|
+
});
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
// Add LSIs
|
|
907
|
+
if ((_b = result.Table) === null || _b === void 0 ? void 0 : _b.LocalSecondaryIndexes) {
|
|
908
|
+
result.Table.LocalSecondaryIndexes.forEach((lsi) => {
|
|
909
|
+
indexes.push({
|
|
910
|
+
name: lsi.IndexName,
|
|
911
|
+
table: options.table,
|
|
912
|
+
columns: lsi.KeySchema.map((key) => key.AttributeName),
|
|
913
|
+
columnDetails: lsi.KeySchema.map((key) => ({
|
|
914
|
+
name: key.AttributeName,
|
|
915
|
+
order: 'ASC',
|
|
916
|
+
})),
|
|
917
|
+
unique: false,
|
|
918
|
+
primaryKey: false,
|
|
919
|
+
type: 'LSI',
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
return indexes;
|
|
924
|
+
}
|
|
925
|
+
catch (error) {
|
|
926
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.INDEX_ERROR, `Failed to list DynamoDB indexes: ${error.message}`, error);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
async getIndexStatistics(connection, tableName, indexName) {
|
|
930
|
+
var _a;
|
|
931
|
+
try {
|
|
932
|
+
const { DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
933
|
+
const client = connection.getRawClient();
|
|
934
|
+
const result = await client.send(new DescribeTableCommand({
|
|
935
|
+
TableName: tableName,
|
|
936
|
+
}));
|
|
937
|
+
const statistics = [];
|
|
938
|
+
if ((_a = result.Table) === null || _a === void 0 ? void 0 : _a.GlobalSecondaryIndexes) {
|
|
939
|
+
result.Table.GlobalSecondaryIndexes.forEach((gsi) => {
|
|
940
|
+
if (!indexName || gsi.IndexName === indexName) {
|
|
941
|
+
statistics.push({
|
|
942
|
+
indexName: gsi.IndexName,
|
|
943
|
+
table: tableName,
|
|
944
|
+
size: gsi.IndexSizeBytes || 0,
|
|
945
|
+
tuplesRead: gsi.ItemCount || 0,
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
return statistics;
|
|
951
|
+
}
|
|
952
|
+
catch (error) {
|
|
953
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.INDEX_ERROR, `Failed to get DynamoDB index statistics: ${error.message}`, error);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
// ==================== Migration Methods ====================
|
|
957
|
+
/**
|
|
958
|
+
* Execute a migration operation
|
|
959
|
+
* Note: DynamoDB is schema-less, so column operations are skipped
|
|
960
|
+
*/
|
|
961
|
+
async executeMigrationOperation(connection, operation) {
|
|
962
|
+
var _a, _b, _c, _d;
|
|
963
|
+
const { CreateTableCommand, DeleteTableCommand, UpdateTableCommand, ExecuteStatementCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
964
|
+
const client = connection.getRawClient();
|
|
965
|
+
switch (operation.type) {
|
|
966
|
+
case 'create_table':
|
|
967
|
+
if ((_a = operation.params) === null || _a === void 0 ? void 0 : _a.schema) {
|
|
968
|
+
const schema = operation.params.schema;
|
|
969
|
+
const keySchema = [];
|
|
970
|
+
const attributeDefinitions = [];
|
|
971
|
+
// Extract partition and sort keys from schema
|
|
972
|
+
schema.columns.forEach((col) => {
|
|
973
|
+
if (col.primaryKey) {
|
|
974
|
+
keySchema.push({
|
|
975
|
+
AttributeName: col.name,
|
|
976
|
+
KeyType: 'HASH',
|
|
977
|
+
});
|
|
978
|
+
attributeDefinitions.push({
|
|
979
|
+
AttributeName: col.name,
|
|
980
|
+
AttributeType: this.mapColumnTypeToDynamoDB(col.type),
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
await client.send(new CreateTableCommand({
|
|
985
|
+
TableName: schema.name,
|
|
986
|
+
KeySchema: keySchema,
|
|
987
|
+
AttributeDefinitions: attributeDefinitions,
|
|
988
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
989
|
+
}));
|
|
990
|
+
return { executed: true, command: `CreateTable: ${schema.name}` };
|
|
991
|
+
}
|
|
992
|
+
return { executed: false };
|
|
993
|
+
case 'drop_table':
|
|
994
|
+
if (operation.table) {
|
|
995
|
+
await client.send(new DeleteTableCommand({
|
|
996
|
+
TableName: operation.table,
|
|
997
|
+
}));
|
|
998
|
+
return { executed: true, command: `DropTable: ${operation.table}` };
|
|
999
|
+
}
|
|
1000
|
+
return { executed: false };
|
|
1001
|
+
case 'add_index':
|
|
1002
|
+
if (operation.table && ((_b = operation.params) === null || _b === void 0 ? void 0 : _b.indexName) && ((_c = operation.params) === null || _c === void 0 ? void 0 : _c.columns)) {
|
|
1003
|
+
const keySchema = operation.params.columns.map((col, idx) => ({
|
|
1004
|
+
AttributeName: col,
|
|
1005
|
+
KeyType: idx === 0 ? 'HASH' : 'RANGE',
|
|
1006
|
+
}));
|
|
1007
|
+
const attributeDefinitions = operation.params.columns.map((col) => ({
|
|
1008
|
+
AttributeName: col,
|
|
1009
|
+
AttributeType: 'S', // Default to String
|
|
1010
|
+
}));
|
|
1011
|
+
await client.send(new UpdateTableCommand({
|
|
1012
|
+
TableName: operation.table,
|
|
1013
|
+
AttributeDefinitions: attributeDefinitions,
|
|
1014
|
+
GlobalSecondaryIndexUpdates: [
|
|
1015
|
+
{
|
|
1016
|
+
Create: {
|
|
1017
|
+
IndexName: operation.params.indexName,
|
|
1018
|
+
KeySchema: keySchema,
|
|
1019
|
+
Projection: {
|
|
1020
|
+
ProjectionType: 'ALL',
|
|
1021
|
+
},
|
|
1022
|
+
},
|
|
1023
|
+
},
|
|
1024
|
+
],
|
|
1025
|
+
}));
|
|
1026
|
+
return { executed: true, command: `AddIndex: ${operation.params.indexName}` };
|
|
1027
|
+
}
|
|
1028
|
+
return { executed: false };
|
|
1029
|
+
case 'drop_index':
|
|
1030
|
+
if (operation.table && ((_d = operation.params) === null || _d === void 0 ? void 0 : _d.indexName)) {
|
|
1031
|
+
await client.send(new UpdateTableCommand({
|
|
1032
|
+
TableName: operation.table,
|
|
1033
|
+
GlobalSecondaryIndexUpdates: [
|
|
1034
|
+
{
|
|
1035
|
+
Delete: {
|
|
1036
|
+
IndexName: operation.params.indexName,
|
|
1037
|
+
},
|
|
1038
|
+
},
|
|
1039
|
+
],
|
|
1040
|
+
}));
|
|
1041
|
+
return { executed: true, command: `DropIndex: ${operation.params.indexName}` };
|
|
1042
|
+
}
|
|
1043
|
+
return { executed: false };
|
|
1044
|
+
case 'raw_sql':
|
|
1045
|
+
if (operation.sql) {
|
|
1046
|
+
// Use PartiQL for raw queries
|
|
1047
|
+
await client.send(new ExecuteStatementCommand({
|
|
1048
|
+
Statement: operation.sql,
|
|
1049
|
+
}));
|
|
1050
|
+
return { executed: true, command: `PartiQL: ${operation.sql.substring(0, 50)}...` };
|
|
1051
|
+
}
|
|
1052
|
+
return { executed: false };
|
|
1053
|
+
// Schema-less operations - skip silently for DynamoDB
|
|
1054
|
+
case 'add_column':
|
|
1055
|
+
case 'drop_column':
|
|
1056
|
+
case 'modify_column':
|
|
1057
|
+
case 'rename_column':
|
|
1058
|
+
case 'alter_table':
|
|
1059
|
+
return { executed: false, command: `Skipped (schema-less): ${operation.type}` };
|
|
1060
|
+
// Constraint operations - not applicable to DynamoDB
|
|
1061
|
+
case 'add_constraint':
|
|
1062
|
+
case 'drop_constraint':
|
|
1063
|
+
return { executed: false, command: `Skipped (no constraints): ${operation.type}` };
|
|
1064
|
+
default:
|
|
1065
|
+
return { executed: false };
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
async runMigration(connection, migration, options) {
|
|
1069
|
+
const startTime = Date.now();
|
|
1070
|
+
const statements = [];
|
|
1071
|
+
try {
|
|
1072
|
+
// DynamoDB doesn't support transactions for schema changes
|
|
1073
|
+
// Execute operations sequentially
|
|
1074
|
+
for (const operation of migration.up) {
|
|
1075
|
+
const result = await this.executeMigrationOperation(connection, operation);
|
|
1076
|
+
if (result.command) {
|
|
1077
|
+
statements.push(result.command);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
// Record migration in history table
|
|
1081
|
+
await this.recordMigration(connection, migration);
|
|
1082
|
+
return {
|
|
1083
|
+
tag: migration.tag,
|
|
1084
|
+
status: migration_types_1.MigrationStatus.COMPLETED,
|
|
1085
|
+
direction: migration_types_1.MigrationDirection.UP,
|
|
1086
|
+
executedAt: new Date(),
|
|
1087
|
+
duration: Date.now() - startTime,
|
|
1088
|
+
statements,
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
catch (error) {
|
|
1092
|
+
return {
|
|
1093
|
+
tag: migration.tag,
|
|
1094
|
+
status: migration_types_1.MigrationStatus.FAILED,
|
|
1095
|
+
direction: migration_types_1.MigrationDirection.UP,
|
|
1096
|
+
executedAt: new Date(),
|
|
1097
|
+
duration: Date.now() - startTime,
|
|
1098
|
+
error: error.message,
|
|
1099
|
+
statements,
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
async rollbackMigration(connection, migration, options) {
|
|
1104
|
+
const startTime = Date.now();
|
|
1105
|
+
const statements = [];
|
|
1106
|
+
try {
|
|
1107
|
+
// Execute rollback operations
|
|
1108
|
+
for (const operation of migration.down) {
|
|
1109
|
+
const result = await this.executeMigrationOperation(connection, operation);
|
|
1110
|
+
if (result.command) {
|
|
1111
|
+
statements.push(result.command);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
// Remove migration from history table
|
|
1115
|
+
await this.removeMigration(connection, migration.tag);
|
|
1116
|
+
return {
|
|
1117
|
+
tag: migration.tag,
|
|
1118
|
+
status: migration_types_1.MigrationStatus.ROLLED_BACK,
|
|
1119
|
+
direction: migration_types_1.MigrationDirection.DOWN,
|
|
1120
|
+
executedAt: new Date(),
|
|
1121
|
+
duration: Date.now() - startTime,
|
|
1122
|
+
statements,
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
catch (error) {
|
|
1126
|
+
return {
|
|
1127
|
+
tag: migration.tag,
|
|
1128
|
+
status: migration_types_1.MigrationStatus.FAILED,
|
|
1129
|
+
direction: migration_types_1.MigrationDirection.DOWN,
|
|
1130
|
+
executedAt: new Date(),
|
|
1131
|
+
duration: Date.now() - startTime,
|
|
1132
|
+
error: error.message,
|
|
1133
|
+
statements,
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
async getMigrationHistory(connection, options) {
|
|
1138
|
+
try {
|
|
1139
|
+
const { ScanCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
1140
|
+
const docClient = connection.getClient();
|
|
1141
|
+
// Ensure migration table exists
|
|
1142
|
+
await this.ensureMigrationTable(connection);
|
|
1143
|
+
const result = await docClient.send(new ScanCommand({
|
|
1144
|
+
TableName: '_ductape_migrations',
|
|
1145
|
+
}));
|
|
1146
|
+
return (result.Items || []).map((item) => ({
|
|
1147
|
+
tag: item.tag,
|
|
1148
|
+
name: item.name,
|
|
1149
|
+
env: options.env,
|
|
1150
|
+
product: options.product,
|
|
1151
|
+
database: options.database,
|
|
1152
|
+
status: migration_types_1.MigrationStatus.COMPLETED,
|
|
1153
|
+
direction: migration_types_1.MigrationDirection.UP,
|
|
1154
|
+
appliedAt: new Date(item.executed_at),
|
|
1155
|
+
}));
|
|
1156
|
+
}
|
|
1157
|
+
catch (error) {
|
|
1158
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.MIGRATION_ERROR, `Failed to get DynamoDB migration history: ${error.message}`, error);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Ensure migration tracking table exists
|
|
1163
|
+
*/
|
|
1164
|
+
async ensureMigrationTable(connection) {
|
|
1165
|
+
try {
|
|
1166
|
+
const { CreateTableCommand, DescribeTableCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-dynamodb')));
|
|
1167
|
+
const client = connection.getRawClient();
|
|
1168
|
+
// Check if table exists
|
|
1169
|
+
try {
|
|
1170
|
+
await client.send(new DescribeTableCommand({
|
|
1171
|
+
TableName: '_ductape_migrations',
|
|
1172
|
+
}));
|
|
1173
|
+
return; // Table exists
|
|
1174
|
+
}
|
|
1175
|
+
catch (error) {
|
|
1176
|
+
if (error.name !== 'ResourceNotFoundException') {
|
|
1177
|
+
throw error;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
// Create migration table
|
|
1181
|
+
await client.send(new CreateTableCommand({
|
|
1182
|
+
TableName: '_ductape_migrations',
|
|
1183
|
+
KeySchema: [
|
|
1184
|
+
{
|
|
1185
|
+
AttributeName: 'tag',
|
|
1186
|
+
KeyType: 'HASH',
|
|
1187
|
+
},
|
|
1188
|
+
],
|
|
1189
|
+
AttributeDefinitions: [
|
|
1190
|
+
{
|
|
1191
|
+
AttributeName: 'tag',
|
|
1192
|
+
AttributeType: 'S',
|
|
1193
|
+
},
|
|
1194
|
+
],
|
|
1195
|
+
BillingMode: 'PAY_PER_REQUEST',
|
|
1196
|
+
}));
|
|
1197
|
+
// Wait for table to be active
|
|
1198
|
+
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
1199
|
+
}
|
|
1200
|
+
catch (error) {
|
|
1201
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.MIGRATION_ERROR, `Failed to ensure migration table: ${error.message}`, error);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Record migration execution
|
|
1206
|
+
*/
|
|
1207
|
+
async recordMigration(connection, migration) {
|
|
1208
|
+
try {
|
|
1209
|
+
const { PutCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
1210
|
+
const docClient = connection.getClient();
|
|
1211
|
+
await this.ensureMigrationTable(connection);
|
|
1212
|
+
await docClient.send(new PutCommand({
|
|
1213
|
+
TableName: '_ductape_migrations',
|
|
1214
|
+
Item: {
|
|
1215
|
+
tag: migration.tag,
|
|
1216
|
+
name: migration.name,
|
|
1217
|
+
executed_at: new Date().toISOString(),
|
|
1218
|
+
},
|
|
1219
|
+
}));
|
|
1220
|
+
}
|
|
1221
|
+
catch (error) {
|
|
1222
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.MIGRATION_ERROR, `Failed to record migration: ${error.message}`, error);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* Remove migration from history
|
|
1227
|
+
*/
|
|
1228
|
+
async removeMigration(connection, tag) {
|
|
1229
|
+
try {
|
|
1230
|
+
const { DeleteCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/lib-dynamodb')));
|
|
1231
|
+
const docClient = connection.getClient();
|
|
1232
|
+
await docClient.send(new DeleteCommand({
|
|
1233
|
+
TableName: '_ductape_migrations',
|
|
1234
|
+
Key: { tag },
|
|
1235
|
+
}));
|
|
1236
|
+
}
|
|
1237
|
+
catch (error) {
|
|
1238
|
+
throw new database_types_1.DatabaseError(database_types_1.DatabaseErrorType.MIGRATION_ERROR, `Failed to remove migration: ${error.message}`, error);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
// ==================== Helper Methods ====================
|
|
1242
|
+
hasPartitionKeyInWhere(where) {
|
|
1243
|
+
// Simple check - in production would need table metadata
|
|
1244
|
+
return where && typeof where === 'object' && Object.keys(where).length > 0;
|
|
1245
|
+
}
|
|
1246
|
+
buildQueryParams(options) {
|
|
1247
|
+
const params = {
|
|
1248
|
+
TableName: options.table,
|
|
1249
|
+
};
|
|
1250
|
+
if (options.where) {
|
|
1251
|
+
const { keyConditionExpression, expressionAttributeNames, expressionAttributeValues } = this.buildKeyConditionExpression(options.where);
|
|
1252
|
+
params.KeyConditionExpression = keyConditionExpression;
|
|
1253
|
+
params.ExpressionAttributeNames = expressionAttributeNames;
|
|
1254
|
+
params.ExpressionAttributeValues = expressionAttributeValues;
|
|
1255
|
+
}
|
|
1256
|
+
if (options.limit) {
|
|
1257
|
+
params.Limit = options.limit;
|
|
1258
|
+
}
|
|
1259
|
+
return params;
|
|
1260
|
+
}
|
|
1261
|
+
buildScanParams(options) {
|
|
1262
|
+
const params = {
|
|
1263
|
+
TableName: options.table,
|
|
1264
|
+
};
|
|
1265
|
+
if (options.where) {
|
|
1266
|
+
const { filterExpression, expressionAttributeNames, expressionAttributeValues } = this.buildFilterExpression(options.where);
|
|
1267
|
+
params.FilterExpression = filterExpression;
|
|
1268
|
+
params.ExpressionAttributeNames = expressionAttributeNames;
|
|
1269
|
+
params.ExpressionAttributeValues = expressionAttributeValues;
|
|
1270
|
+
}
|
|
1271
|
+
if (options.limit) {
|
|
1272
|
+
params.Limit = options.limit;
|
|
1273
|
+
}
|
|
1274
|
+
return params;
|
|
1275
|
+
}
|
|
1276
|
+
/**
|
|
1277
|
+
* Build DynamoDB key condition expression (for Query operations)
|
|
1278
|
+
* Supports both simplified syntax (GT, LT, etc.) and legacy $ prefix operators
|
|
1279
|
+
*/
|
|
1280
|
+
buildKeyConditionExpression(where) {
|
|
1281
|
+
const names = {};
|
|
1282
|
+
const values = {};
|
|
1283
|
+
let valueIndex = 0;
|
|
1284
|
+
const buildCondition = (condition) => {
|
|
1285
|
+
if (typeof condition !== 'object' || condition === null) {
|
|
1286
|
+
const valKey = `:val${valueIndex++}`;
|
|
1287
|
+
values[valKey] = condition;
|
|
1288
|
+
return valKey;
|
|
1289
|
+
}
|
|
1290
|
+
const conditions = [];
|
|
1291
|
+
for (const [key, value] of Object.entries(condition)) {
|
|
1292
|
+
const attrName = `#${key}`;
|
|
1293
|
+
names[attrName] = key;
|
|
1294
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
|
|
1295
|
+
// Handle comparison operators
|
|
1296
|
+
for (const [op, opValue] of Object.entries(value)) {
|
|
1297
|
+
const valKey = `:val${valueIndex++}`;
|
|
1298
|
+
values[valKey] = opValue;
|
|
1299
|
+
switch (op) {
|
|
1300
|
+
case 'GT':
|
|
1301
|
+
case '$gt':
|
|
1302
|
+
conditions.push(`${attrName} > ${valKey}`);
|
|
1303
|
+
break;
|
|
1304
|
+
case 'GTE':
|
|
1305
|
+
case '$gte':
|
|
1306
|
+
conditions.push(`${attrName} >= ${valKey}`);
|
|
1307
|
+
break;
|
|
1308
|
+
case 'LT':
|
|
1309
|
+
case '$lt':
|
|
1310
|
+
conditions.push(`${attrName} < ${valKey}`);
|
|
1311
|
+
break;
|
|
1312
|
+
case 'LTE':
|
|
1313
|
+
case '$lte':
|
|
1314
|
+
conditions.push(`${attrName} <= ${valKey}`);
|
|
1315
|
+
break;
|
|
1316
|
+
case 'NE':
|
|
1317
|
+
case 'NOT':
|
|
1318
|
+
case '$ne':
|
|
1319
|
+
conditions.push(`${attrName} <> ${valKey}`);
|
|
1320
|
+
break;
|
|
1321
|
+
case 'BETWEEN':
|
|
1322
|
+
case '$between':
|
|
1323
|
+
if (Array.isArray(opValue) && opValue.length === 2) {
|
|
1324
|
+
const valKey2 = `:val${valueIndex++}`;
|
|
1325
|
+
values[valKey] = opValue[0];
|
|
1326
|
+
values[valKey2] = opValue[1];
|
|
1327
|
+
conditions.push(`${attrName} BETWEEN ${valKey} AND ${valKey2}`);
|
|
1328
|
+
}
|
|
1329
|
+
break;
|
|
1330
|
+
default:
|
|
1331
|
+
// Simple equality for unknown operators
|
|
1332
|
+
conditions.push(`${attrName} = ${valKey}`);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
else {
|
|
1337
|
+
// Simple equality
|
|
1338
|
+
const valKey = `:val${valueIndex++}`;
|
|
1339
|
+
values[valKey] = value;
|
|
1340
|
+
conditions.push(`${attrName} = ${valKey}`);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
return conditions.join(' AND ');
|
|
1344
|
+
};
|
|
1345
|
+
const expression = buildCondition(where);
|
|
1346
|
+
return {
|
|
1347
|
+
keyConditionExpression: expression,
|
|
1348
|
+
expressionAttributeNames: names,
|
|
1349
|
+
expressionAttributeValues: values,
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Build DynamoDB filter expression (for Scan and Query operations)
|
|
1354
|
+
* Supports both simplified syntax (GT, LT, AND, OR, etc.) and legacy $ prefix operators
|
|
1355
|
+
*/
|
|
1356
|
+
buildFilterExpression(where) {
|
|
1357
|
+
const names = {};
|
|
1358
|
+
const values = {};
|
|
1359
|
+
let valueIndex = 0;
|
|
1360
|
+
const buildCondition = (condition) => {
|
|
1361
|
+
if (typeof condition !== 'object' || condition === null) {
|
|
1362
|
+
const valKey = `:val${valueIndex++}`;
|
|
1363
|
+
values[valKey] = condition;
|
|
1364
|
+
return valKey;
|
|
1365
|
+
}
|
|
1366
|
+
// Handle logical operators (supports $AND, AND, and legacy $and)
|
|
1367
|
+
if (condition.$AND || condition.AND || condition.$and) {
|
|
1368
|
+
const andConditions = condition.$AND || condition.AND || condition.$and;
|
|
1369
|
+
if (typeof andConditions === 'object' && !Array.isArray(andConditions)) {
|
|
1370
|
+
// New syntax: { $AND: { col1: val1, col2: val2 } }
|
|
1371
|
+
return buildCondition(andConditions);
|
|
1372
|
+
}
|
|
1373
|
+
else {
|
|
1374
|
+
// Array syntax: { $AND: [cond1, cond2] }
|
|
1375
|
+
const subConditions = andConditions.map(buildCondition);
|
|
1376
|
+
return `(${subConditions.join(' AND ')})`;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
if (condition.$OR || condition.OR || condition.$or) {
|
|
1380
|
+
const orConditions = condition.$OR || condition.OR || condition.$or;
|
|
1381
|
+
if (typeof orConditions === 'object' && !Array.isArray(orConditions)) {
|
|
1382
|
+
// New syntax: { $OR: { col1: val1, col2: val2 } }
|
|
1383
|
+
const subConditions = [];
|
|
1384
|
+
for (const [key, value] of Object.entries(orConditions)) {
|
|
1385
|
+
if (key === '$AND' || key === 'AND' || key === '$and' || key === '$OR' || key === 'OR' || key === '$or') {
|
|
1386
|
+
subConditions.push(buildCondition({ [key]: value }));
|
|
1387
|
+
}
|
|
1388
|
+
else {
|
|
1389
|
+
subConditions.push(buildCondition({ [key]: value }));
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
return `(${subConditions.join(' OR ')})`;
|
|
1393
|
+
}
|
|
1394
|
+
else {
|
|
1395
|
+
// Array syntax: { $OR: [cond1, cond2] }
|
|
1396
|
+
const subConditions = orConditions.map(buildCondition);
|
|
1397
|
+
return `(${subConditions.join(' OR ')})`;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
// Handle column conditions
|
|
1401
|
+
const conditions = [];
|
|
1402
|
+
for (const [key, value] of Object.entries(condition)) {
|
|
1403
|
+
// Skip logical operators at this level
|
|
1404
|
+
if (key === '$AND' || key === 'AND' || key === '$and' || key === '$OR' || key === 'OR' || key === '$or') {
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
const attrName = `#${key}`;
|
|
1408
|
+
names[attrName] = key;
|
|
1409
|
+
if (typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)) {
|
|
1410
|
+
// Handle comparison operators
|
|
1411
|
+
for (const [op, opValue] of Object.entries(value)) {
|
|
1412
|
+
switch (op) {
|
|
1413
|
+
// Preferred $-prefixed syntax
|
|
1414
|
+
case '$GT':
|
|
1415
|
+
case 'GT':
|
|
1416
|
+
case '$gt': {
|
|
1417
|
+
const valKey = `:val${valueIndex++}`;
|
|
1418
|
+
values[valKey] = opValue;
|
|
1419
|
+
conditions.push(`${attrName} > ${valKey}`);
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
case '$GTE':
|
|
1423
|
+
case 'GTE':
|
|
1424
|
+
case '$gte': {
|
|
1425
|
+
const valKey = `:val${valueIndex++}`;
|
|
1426
|
+
values[valKey] = opValue;
|
|
1427
|
+
conditions.push(`${attrName} >= ${valKey}`);
|
|
1428
|
+
break;
|
|
1429
|
+
}
|
|
1430
|
+
case '$LT':
|
|
1431
|
+
case 'LT':
|
|
1432
|
+
case '$lt': {
|
|
1433
|
+
const valKey = `:val${valueIndex++}`;
|
|
1434
|
+
values[valKey] = opValue;
|
|
1435
|
+
conditions.push(`${attrName} < ${valKey}`);
|
|
1436
|
+
break;
|
|
1437
|
+
}
|
|
1438
|
+
case '$LTE':
|
|
1439
|
+
case 'LTE':
|
|
1440
|
+
case '$lte': {
|
|
1441
|
+
const valKey = `:val${valueIndex++}`;
|
|
1442
|
+
values[valKey] = opValue;
|
|
1443
|
+
conditions.push(`${attrName} <= ${valKey}`);
|
|
1444
|
+
break;
|
|
1445
|
+
}
|
|
1446
|
+
case '$NE':
|
|
1447
|
+
case '$NOT':
|
|
1448
|
+
case 'NE':
|
|
1449
|
+
case 'NOT':
|
|
1450
|
+
case '$ne': {
|
|
1451
|
+
const valKey = `:val${valueIndex++}`;
|
|
1452
|
+
values[valKey] = opValue;
|
|
1453
|
+
conditions.push(`${attrName} <> ${valKey}`);
|
|
1454
|
+
break;
|
|
1455
|
+
}
|
|
1456
|
+
case '$IN':
|
|
1457
|
+
case 'IN':
|
|
1458
|
+
case '$in':
|
|
1459
|
+
if (Array.isArray(opValue)) {
|
|
1460
|
+
const inConditions = opValue.map((val) => {
|
|
1461
|
+
const valKey = `:val${valueIndex++}`;
|
|
1462
|
+
values[valKey] = val;
|
|
1463
|
+
return `${attrName} = ${valKey}`;
|
|
1464
|
+
});
|
|
1465
|
+
conditions.push(`(${inConditions.join(' OR ')})`);
|
|
1466
|
+
}
|
|
1467
|
+
break;
|
|
1468
|
+
case '$NOT_IN':
|
|
1469
|
+
case 'NOT_IN':
|
|
1470
|
+
case '$nin':
|
|
1471
|
+
if (Array.isArray(opValue)) {
|
|
1472
|
+
const notInConditions = opValue.map((val) => {
|
|
1473
|
+
const valKey = `:val${valueIndex++}`;
|
|
1474
|
+
values[valKey] = val;
|
|
1475
|
+
return `${attrName} <> ${valKey}`;
|
|
1476
|
+
});
|
|
1477
|
+
conditions.push(`(${notInConditions.join(' AND ')})`);
|
|
1478
|
+
}
|
|
1479
|
+
break;
|
|
1480
|
+
case '$LIKE':
|
|
1481
|
+
case 'LIKE':
|
|
1482
|
+
case '$like': {
|
|
1483
|
+
// DynamoDB uses contains() function for substring matching
|
|
1484
|
+
const valKey = `:val${valueIndex++}`;
|
|
1485
|
+
// Convert SQL LIKE pattern to substring (remove % wildcards)
|
|
1486
|
+
const searchValue = String(opValue).replace(/%/g, '');
|
|
1487
|
+
values[valKey] = searchValue;
|
|
1488
|
+
conditions.push(`contains(${attrName}, ${valKey})`);
|
|
1489
|
+
break;
|
|
1490
|
+
}
|
|
1491
|
+
case '$IS_NULL':
|
|
1492
|
+
case 'IS_NULL':
|
|
1493
|
+
case '$null':
|
|
1494
|
+
conditions.push(`attribute_not_exists(${attrName})`);
|
|
1495
|
+
break;
|
|
1496
|
+
case '$IS_NOT_NULL':
|
|
1497
|
+
case 'IS_NOT_NULL':
|
|
1498
|
+
case '$notNull':
|
|
1499
|
+
conditions.push(`attribute_exists(${attrName})`);
|
|
1500
|
+
break;
|
|
1501
|
+
case '$BETWEEN':
|
|
1502
|
+
case 'BETWEEN':
|
|
1503
|
+
case '$between':
|
|
1504
|
+
if (Array.isArray(opValue) && opValue.length === 2) {
|
|
1505
|
+
const valKey1 = `:val${valueIndex++}`;
|
|
1506
|
+
const valKey2 = `:val${valueIndex++}`;
|
|
1507
|
+
values[valKey1] = opValue[0];
|
|
1508
|
+
values[valKey2] = opValue[1];
|
|
1509
|
+
conditions.push(`${attrName} BETWEEN ${valKey1} AND ${valKey2}`);
|
|
1510
|
+
}
|
|
1511
|
+
break;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
else {
|
|
1516
|
+
// Simple equality
|
|
1517
|
+
const valKey = `:val${valueIndex++}`;
|
|
1518
|
+
values[valKey] = value;
|
|
1519
|
+
conditions.push(`${attrName} = ${valKey}`);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return conditions.join(' AND ');
|
|
1523
|
+
};
|
|
1524
|
+
const expression = buildCondition(where);
|
|
1525
|
+
return {
|
|
1526
|
+
filterExpression: expression,
|
|
1527
|
+
expressionAttributeNames: names,
|
|
1528
|
+
expressionAttributeValues: values,
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
buildUpdateExpression(data) {
|
|
1532
|
+
const names = {};
|
|
1533
|
+
const values = {};
|
|
1534
|
+
const updates = [];
|
|
1535
|
+
Object.entries(data).forEach(([key, value], idx) => {
|
|
1536
|
+
names[`#${key}`] = key;
|
|
1537
|
+
values[`:val${idx}`] = value;
|
|
1538
|
+
updates.push(`#${key} = :val${idx}`);
|
|
1539
|
+
});
|
|
1540
|
+
return {
|
|
1541
|
+
updateExpression: `SET ${updates.join(', ')}`,
|
|
1542
|
+
expressionAttributeNames: names,
|
|
1543
|
+
expressionAttributeValues: values,
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
extractKeyFromWhere(where) {
|
|
1547
|
+
// Simple implementation - extract key fields from where clause
|
|
1548
|
+
return where || {};
|
|
1549
|
+
}
|
|
1550
|
+
chunkArray(array, size) {
|
|
1551
|
+
const chunks = [];
|
|
1552
|
+
for (let i = 0; i < array.length; i += size) {
|
|
1553
|
+
chunks.push(array.slice(i, i + size));
|
|
1554
|
+
}
|
|
1555
|
+
return chunks;
|
|
1556
|
+
}
|
|
1557
|
+
mapColumnTypeToDynamoDB(type) {
|
|
1558
|
+
// DynamoDB only supports S (String), N (Number), and B (Binary) for keys
|
|
1559
|
+
switch (type) {
|
|
1560
|
+
case schema_types_1.ColumnType.INTEGER:
|
|
1561
|
+
case schema_types_1.ColumnType.BIGINT:
|
|
1562
|
+
case schema_types_1.ColumnType.SMALLINT:
|
|
1563
|
+
case schema_types_1.ColumnType.DECIMAL:
|
|
1564
|
+
case schema_types_1.ColumnType.NUMERIC:
|
|
1565
|
+
case schema_types_1.ColumnType.FLOAT:
|
|
1566
|
+
case schema_types_1.ColumnType.DOUBLE:
|
|
1567
|
+
case schema_types_1.ColumnType.REAL:
|
|
1568
|
+
return 'N';
|
|
1569
|
+
case schema_types_1.ColumnType.BLOB:
|
|
1570
|
+
case schema_types_1.ColumnType.BINARY:
|
|
1571
|
+
return 'B';
|
|
1572
|
+
default:
|
|
1573
|
+
return 'S';
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
mapDynamoDBTypeToColumnType(dynamoType) {
|
|
1577
|
+
switch (dynamoType) {
|
|
1578
|
+
case 'N':
|
|
1579
|
+
return schema_types_1.ColumnType.DECIMAL;
|
|
1580
|
+
case 'B':
|
|
1581
|
+
return schema_types_1.ColumnType.BINARY;
|
|
1582
|
+
default:
|
|
1583
|
+
return schema_types_1.ColumnType.STRING;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
// ==================== Utility Methods ====================
|
|
1587
|
+
escapeIdentifier(identifier) {
|
|
1588
|
+
// DynamoDB doesn't require escaping identifiers
|
|
1589
|
+
return identifier;
|
|
1590
|
+
}
|
|
1591
|
+
escapeValue(value) {
|
|
1592
|
+
if (value === null || value === undefined) {
|
|
1593
|
+
return 'null';
|
|
1594
|
+
}
|
|
1595
|
+
if (typeof value === 'string') {
|
|
1596
|
+
return `"${value.replace(/"/g, '\\"')}"`;
|
|
1597
|
+
}
|
|
1598
|
+
if (typeof value === 'object') {
|
|
1599
|
+
return JSON.stringify(value);
|
|
1600
|
+
}
|
|
1601
|
+
return String(value);
|
|
1602
|
+
}
|
|
1603
|
+
async getDatabaseVersion(connection) {
|
|
1604
|
+
return 'DynamoDB (AWS Managed)';
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
exports.DynamoDBAdapter = DynamoDBAdapter;
|
|
1608
|
+
//# sourceMappingURL=dynamodb.adapter.js.map
|