@loomcore/api 0.0.58 → 0.1.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/LICENSE +201 -0
- package/README.md +50 -0
- package/dist/__tests__/common-test.utils.d.ts +29 -60
- package/dist/__tests__/common-test.utils.js +88 -166
- package/dist/__tests__/index.d.ts +6 -0
- package/dist/__tests__/index.js +6 -0
- package/dist/__tests__/models/category.model.d.ts +8 -0
- package/dist/__tests__/models/category.model.js +6 -0
- package/dist/__tests__/models/mongo-test-entity.model.d.ts +11 -0
- package/dist/__tests__/models/mongo-test-entity.model.js +13 -0
- package/dist/__tests__/models/product.model.d.ts +17 -0
- package/dist/__tests__/models/product.model.js +10 -0
- package/dist/__tests__/models/test-entity.model.d.ts +11 -0
- package/dist/__tests__/models/test-entity.model.js +10 -0
- package/dist/__tests__/models/test-item.model.d.ts +12 -0
- package/dist/__tests__/models/test-item.model.js +9 -0
- package/dist/__tests__/mongo-db.test-database.d.ts +15 -0
- package/dist/__tests__/mongo-db.test-database.js +74 -0
- package/dist/__tests__/postgres-test-migrations/001-create-test-entities-table.migration.d.ts +11 -0
- package/dist/__tests__/postgres-test-migrations/001-create-test-entities-table.migration.js +59 -0
- package/dist/__tests__/postgres-test-migrations/002-create-categories-table.migration.d.ts +11 -0
- package/dist/__tests__/postgres-test-migrations/002-create-categories-table.migration.js +52 -0
- package/dist/__tests__/postgres-test-migrations/003-create-products-table.migration.d.ts +11 -0
- package/dist/__tests__/postgres-test-migrations/003-create-products-table.migration.js +62 -0
- package/dist/__tests__/postgres-test-migrations/004-create-test-users-table.migration.d.ts +11 -0
- package/dist/__tests__/postgres-test-migrations/004-create-test-users-table.migration.js +66 -0
- package/dist/__tests__/postgres-test-migrations/005-create-test-items-table.migration.d.ts +11 -0
- package/dist/__tests__/postgres-test-migrations/005-create-test-items-table.migration.js +50 -0
- package/dist/__tests__/postgres-test-migrations/run-test-migrations.d.ts +2 -0
- package/dist/__tests__/postgres-test-migrations/run-test-migrations.js +22 -0
- package/dist/__tests__/postgres.test-database.d.ts +13 -0
- package/dist/__tests__/postgres.test-database.js +85 -0
- package/dist/__tests__/test-database.interface.d.ts +7 -0
- package/dist/__tests__/test-express-app.d.ts +9 -7
- package/dist/__tests__/test-express-app.js +38 -48
- package/dist/__tests__/test-mongo-db.d.ts +14 -0
- package/dist/__tests__/test-mongo-db.js +81 -0
- package/dist/__tests__/test-objects.d.ts +23 -0
- package/dist/__tests__/test-objects.js +45 -0
- package/dist/__tests__/test-user.d.ts +3 -0
- package/dist/__tests__/test-user.js +16 -0
- package/dist/config/base-api-config.d.ts +2 -2
- package/dist/config/base-api-config.js +2 -2
- package/dist/controllers/api.controller.d.ts +1 -5
- package/dist/controllers/api.controller.js +4 -11
- package/dist/controllers/auth.controller.d.ts +2 -2
- package/dist/controllers/auth.controller.js +4 -5
- package/dist/controllers/organizations.controller.d.ts +2 -2
- package/dist/controllers/organizations.controller.js +4 -4
- package/dist/controllers/users.controller.d.ts +2 -2
- package/dist/controllers/users.controller.js +2 -2
- package/dist/databases/index.d.ts +1 -0
- package/dist/databases/index.js +1 -0
- package/dist/databases/models/constants.d.ts +1 -0
- package/dist/databases/models/constants.js +1 -0
- package/dist/databases/models/database.d.ts +3 -0
- package/dist/databases/models/database.interface.d.ts +28 -0
- package/dist/databases/models/delete-result.d.ts +5 -0
- package/dist/databases/models/delete-result.js +8 -0
- package/dist/databases/models/index.d.ts +5 -0
- package/dist/databases/models/index.js +5 -0
- package/dist/databases/models/update-result.d.ts +5 -0
- package/dist/databases/models/update-result.js +8 -0
- package/dist/databases/mongo-db/commands/batch-update.command.d.ts +3 -0
- package/dist/databases/mongo-db/commands/batch-update.command.js +41 -0
- package/dist/databases/mongo-db/commands/create-many.command.d.ts +5 -0
- package/dist/databases/mongo-db/commands/create-many.command.js +17 -0
- package/dist/databases/mongo-db/commands/create.command.d.ts +5 -0
- package/dist/databases/mongo-db/commands/create.command.js +17 -0
- package/dist/databases/mongo-db/commands/delete-by-id.command.d.ts +3 -0
- package/dist/databases/mongo-db/commands/delete-by-id.command.js +9 -0
- package/dist/databases/mongo-db/commands/delete-many.command.d.ts +4 -0
- package/dist/databases/mongo-db/commands/delete-many.command.js +9 -0
- package/dist/databases/mongo-db/commands/full-updateby-id.command.d.ts +3 -0
- package/dist/databases/mongo-db/commands/full-updateby-id.command.js +21 -0
- package/dist/databases/mongo-db/commands/index.d.ts +8 -0
- package/dist/databases/mongo-db/commands/index.js +8 -0
- package/dist/databases/mongo-db/commands/mongo-batch-update.command.d.ts +4 -0
- package/dist/databases/mongo-db/commands/mongo-batch-update.command.js +41 -0
- package/dist/databases/mongo-db/commands/mongo-create-many.command.d.ts +5 -0
- package/dist/databases/mongo-db/commands/mongo-create-many.command.js +17 -0
- package/dist/databases/mongo-db/commands/mongo-create.command.d.ts +5 -0
- package/dist/databases/mongo-db/commands/mongo-create.command.js +17 -0
- package/dist/databases/mongo-db/commands/mongo-delete-by-id.command.d.ts +3 -0
- package/dist/databases/mongo-db/commands/mongo-delete-by-id.command.js +14 -0
- package/dist/databases/mongo-db/commands/mongo-delete-many.command.d.ts +4 -0
- package/dist/databases/mongo-db/commands/mongo-delete-many.command.js +9 -0
- package/dist/databases/mongo-db/commands/mongo-full-updateby-id.command.d.ts +3 -0
- package/dist/databases/mongo-db/commands/mongo-full-updateby-id.command.js +25 -0
- package/dist/databases/mongo-db/commands/mongo-partial-update-by-id.command.d.ts +3 -0
- package/dist/databases/mongo-db/commands/mongo-partial-update-by-id.command.js +25 -0
- package/dist/databases/mongo-db/commands/mongo-update.command.d.ts +4 -0
- package/dist/databases/mongo-db/commands/mongo-update.command.js +19 -0
- package/dist/databases/mongo-db/commands/partial-update-by-id.command.d.ts +3 -0
- package/dist/databases/mongo-db/commands/partial-update-by-id.command.js +21 -0
- package/dist/databases/mongo-db/commands/update.command.d.ts +4 -0
- package/dist/databases/mongo-db/commands/update.command.js +19 -0
- package/dist/databases/mongo-db/index.d.ts +4 -0
- package/dist/databases/mongo-db/index.js +4 -0
- package/dist/databases/mongo-db/models/no-sql-pipeline.d.ts +15 -0
- package/dist/databases/mongo-db/models/no-sql-pipeline.interface.d.ts +11 -0
- package/dist/databases/mongo-db/models/no-sql-pipeline.js +43 -0
- package/dist/databases/mongo-db/mongo-db.database.d.ts +32 -0
- package/dist/databases/mongo-db/mongo-db.database.js +65 -0
- package/dist/databases/mongo-db/queries/find-one.query.d.ts +3 -0
- package/dist/databases/mongo-db/queries/find-one.query.js +9 -0
- package/dist/databases/mongo-db/queries/find.query.d.ts +3 -0
- package/dist/databases/mongo-db/queries/find.query.js +9 -0
- package/dist/databases/mongo-db/queries/get-all.query.d.ts +3 -0
- package/dist/databases/mongo-db/queries/get-all.query.js +17 -0
- package/dist/databases/mongo-db/queries/get-by-id.query.d.ts +3 -0
- package/dist/databases/mongo-db/queries/get-by-id.query.js +20 -0
- package/dist/databases/mongo-db/queries/get-count.query.d.ts +2 -0
- package/dist/databases/mongo-db/queries/get-count.query.js +5 -0
- package/dist/databases/mongo-db/queries/get.query.d.ts +4 -0
- package/dist/databases/mongo-db/queries/get.query.js +14 -0
- package/dist/databases/mongo-db/queries/index.d.ts +6 -0
- package/dist/databases/mongo-db/queries/index.js +6 -0
- package/dist/databases/mongo-db/queries/mongo-find-one.query.d.ts +3 -0
- package/dist/databases/mongo-db/queries/mongo-find-one.query.js +9 -0
- package/dist/databases/mongo-db/queries/mongo-find.query.d.ts +3 -0
- package/dist/databases/mongo-db/queries/mongo-find.query.js +9 -0
- package/dist/databases/mongo-db/queries/mongo-get-all.query.d.ts +3 -0
- package/dist/databases/mongo-db/queries/mongo-get-all.query.js +17 -0
- package/dist/databases/mongo-db/queries/mongo-get-by-id.query.d.ts +4 -0
- package/dist/databases/mongo-db/queries/mongo-get-by-id.query.js +17 -0
- package/dist/databases/mongo-db/queries/mongo-get-count.query.d.ts +2 -0
- package/dist/databases/mongo-db/queries/mongo-get-count.query.js +5 -0
- package/dist/databases/mongo-db/queries/mongo-get.query.d.ts +4 -0
- package/dist/databases/mongo-db/queries/mongo-get.query.js +14 -0
- package/dist/databases/mongo-db/utils/build-find-options.util.d.ts +3 -0
- package/dist/databases/mongo-db/utils/build-find-options.util.js +15 -0
- package/dist/databases/mongo-db/utils/build-no-sql-match.util.d.ts +3 -0
- package/dist/databases/mongo-db/utils/build-no-sql-match.util.js +59 -0
- package/dist/databases/mongo-db/utils/convert-object-ids-to-strings.util.d.ts +1 -0
- package/dist/databases/mongo-db/utils/convert-object-ids-to-strings.util.js +32 -0
- package/dist/databases/mongo-db/utils/convert-operations-to-pipeline.util.d.ts +3 -0
- package/dist/databases/mongo-db/utils/convert-operations-to-pipeline.util.js +68 -0
- package/dist/databases/mongo-db/utils/convert-query-options-to-pipeline.util.d.ts +3 -0
- package/dist/databases/mongo-db/utils/convert-query-options-to-pipeline.util.js +31 -0
- package/dist/databases/mongo-db/utils/convert-strings-to-object-ids.util.d.ts +3 -0
- package/dist/databases/mongo-db/utils/convert-strings-to-object-ids.util.js +72 -0
- package/dist/databases/mongo-db/utils/index.d.ts +7 -0
- package/dist/databases/mongo-db/utils/index.js +7 -0
- package/dist/databases/operations/join.operation.d.ts +7 -0
- package/dist/databases/operations/join.operation.js +12 -0
- package/dist/databases/operations/operation.d.ts +2 -0
- package/dist/databases/postgres/commands/postgres-batch-update.command.d.ts +4 -0
- package/dist/databases/postgres/commands/postgres-batch-update.command.js +56 -0
- package/dist/databases/postgres/commands/postgres-create-many.command.d.ts +6 -0
- package/dist/databases/postgres/commands/postgres-create-many.command.js +63 -0
- package/dist/databases/postgres/commands/postgres-create.command.d.ts +6 -0
- package/dist/databases/postgres/commands/postgres-create.command.js +29 -0
- package/dist/databases/postgres/commands/postgres-delete-by-id.command.d.ts +3 -0
- package/dist/databases/postgres/commands/postgres-delete-by-id.command.js +6 -0
- package/dist/databases/postgres/commands/postgres-delete-many.command.d.ts +4 -0
- package/dist/databases/postgres/commands/postgres-delete-many.command.js +13 -0
- package/dist/databases/postgres/commands/postgres-full-update-by-id.command.d.ts +4 -0
- package/dist/databases/postgres/commands/postgres-full-update-by-id.command.js +72 -0
- package/dist/databases/postgres/commands/postgres-partial-update-by-id.command.d.ts +4 -0
- package/dist/databases/postgres/commands/postgres-partial-update-by-id.command.js +42 -0
- package/dist/databases/postgres/commands/postgres-update.command.d.ts +5 -0
- package/dist/databases/postgres/commands/postgres-update.command.js +48 -0
- package/dist/databases/postgres/migrations/001-create-migrations-table.migration.d.ts +11 -0
- package/dist/databases/postgres/migrations/001-create-migrations-table.migration.js +52 -0
- package/dist/databases/postgres/migrations/002-create-organizations-table.migration.d.ts +11 -0
- package/dist/databases/postgres/migrations/002-create-organizations-table.migration.js +55 -0
- package/dist/databases/postgres/migrations/003-create-users-table.migration.d.ts +11 -0
- package/dist/databases/postgres/migrations/003-create-users-table.migration.js +65 -0
- package/dist/databases/postgres/migrations/004-create-refresh-token-table.migration.d.ts +11 -0
- package/dist/databases/postgres/migrations/004-create-refresh-token-table.migration.js +57 -0
- package/dist/databases/postgres/migrations/index.d.ts +3 -0
- package/dist/databases/postgres/migrations/index.js +3 -0
- package/dist/databases/postgres/migrations/migration.d.ts +6 -0
- package/dist/databases/postgres/migrations/migration.interface.d.ts +6 -0
- package/dist/databases/postgres/migrations/migration.interface.js +1 -0
- package/dist/databases/postgres/migrations/migration.js +14 -0
- package/dist/databases/postgres/migrations/runMigrations.d.ts +2 -0
- package/dist/databases/postgres/migrations/runMigrations.js +20 -0
- package/dist/databases/postgres/migrations/setup-for-auth.migration.d.ts +2 -0
- package/dist/databases/postgres/migrations/setup-for-auth.migration.js +18 -0
- package/dist/databases/postgres/migrations/setup-for-multitenant.migration.d.ts +2 -0
- package/dist/databases/postgres/migrations/setup-for-multitenant.migration.js +16 -0
- package/dist/databases/postgres/postgres.database.d.ts +31 -0
- package/dist/databases/postgres/postgres.database.js +69 -0
- package/dist/databases/postgres/queries/postgres-find-one.query.d.ts +3 -0
- package/dist/databases/postgres/queries/postgres-find-one.query.js +13 -0
- package/dist/databases/postgres/queries/postgres-find.query.d.ts +3 -0
- package/dist/databases/postgres/queries/postgres-find.query.js +11 -0
- package/dist/databases/postgres/queries/postgres-get-all.query.d.ts +3 -0
- package/dist/databases/postgres/queries/postgres-get-all.query.js +14 -0
- package/dist/databases/postgres/queries/postgres-get-by-id.query.d.ts +4 -0
- package/dist/databases/postgres/queries/postgres-get-by-id.query.js +26 -0
- package/dist/databases/postgres/queries/postgres-get-count.query.d.ts +2 -0
- package/dist/databases/postgres/queries/postgres-get-count.query.js +4 -0
- package/dist/databases/postgres/queries/postgres-get.query.d.ts +4 -0
- package/dist/databases/postgres/queries/postgres-get.query.js +26 -0
- package/dist/databases/postgres/utils/build-count-query.d.ts +3 -0
- package/dist/databases/postgres/utils/build-count-query.js +7 -0
- package/dist/databases/postgres/utils/build-join-clauses.d.ts +2 -0
- package/dist/databases/postgres/utils/build-join-clauses.js +12 -0
- package/dist/databases/postgres/utils/build-order-by-clause.d.ts +2 -0
- package/dist/databases/postgres/utils/build-order-by-clause.js +8 -0
- package/dist/databases/postgres/utils/build-pagination-clause.d.ts +2 -0
- package/dist/databases/postgres/utils/build-pagination-clause.js +9 -0
- package/dist/databases/postgres/utils/build-select-clause.d.ts +3 -0
- package/dist/databases/postgres/utils/build-select-clause.js +28 -0
- package/dist/databases/postgres/utils/build-where-clause.d.ts +5 -0
- package/dist/databases/postgres/utils/build-where-clause.js +50 -0
- package/dist/databases/postgres/utils/columns-and-values-from-entity.d.ts +5 -0
- package/dist/databases/postgres/utils/columns-and-values-from-entity.js +9 -0
- package/dist/databases/postgres/utils/convert-null-to-undefined.util.d.ts +2 -0
- package/dist/databases/postgres/utils/convert-null-to-undefined.util.js +70 -0
- package/dist/databases/postgres/utils/transform-join-results.d.ts +2 -0
- package/dist/databases/postgres/utils/transform-join-results.js +33 -0
- package/dist/databases/utils/database-to-idatabase.util.d.ts +3 -0
- package/dist/databases/utils/database-to-idatabase.util.js +14 -0
- package/dist/databases/utils/get-property-schema.util.d.ts +2 -0
- package/dist/databases/utils/get-property-schema.util.js +15 -0
- package/dist/databases/utils/index.d.ts +1 -0
- package/dist/databases/utils/index.js +1 -0
- package/dist/models/base-api-config.interface.d.ts +3 -2
- package/dist/models/index.d.ts +1 -1
- package/dist/models/index.js +1 -1
- package/dist/models/refresh-token.d.ts +9 -0
- package/dist/models/refresh-token.js +2 -0
- package/dist/models/refresh-token.model.d.ts +18 -0
- package/dist/models/refresh-token.model.js +13 -0
- package/dist/models/refresh-token.spec.d.ts +1 -0
- package/dist/models/refresh-token.spec.js +12 -0
- package/dist/services/auth.service.d.ts +11 -18
- package/dist/services/auth.service.js +29 -50
- package/dist/services/generic-api-service/generic-api-service.interface.d.ts +29 -0
- package/dist/services/generic-api-service/generic-api-service.interface.js +1 -0
- package/dist/services/generic-api-service/generic-api.service.d.ts +37 -0
- package/dist/services/generic-api-service/generic-api.service.js +178 -0
- package/dist/services/index.d.ts +2 -2
- package/dist/services/index.js +2 -2
- package/dist/services/multi-tenant-api.service.d.ts +9 -6
- package/dist/services/multi-tenant-api.service.js +10 -23
- package/dist/services/organization.service.d.ts +5 -5
- package/dist/services/organization.service.js +9 -6
- package/dist/services/password-reset-token.service.d.ts +3 -3
- package/dist/services/password-reset-token.service.js +5 -5
- package/dist/services/tenant-query-decorator.d.ts +1 -1
- package/dist/services/tenant-query-decorator.js +1 -1
- package/dist/services/user.service.d.ts +4 -6
- package/dist/services/user.service.js +4 -10
- package/dist/services/utils/audit-for-create.util.d.ts +2 -0
- package/dist/services/utils/audit-for-create.util.js +9 -0
- package/dist/services/utils/audit-for-update.util.d.ts +2 -0
- package/dist/services/utils/audit-for-update.util.js +7 -0
- package/dist/services/utils/strip-sender-provided-system-properties.util.d.ts +2 -0
- package/dist/services/utils/strip-sender-provided-system-properties.util.js +15 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utils/api.utils.js +2 -1
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/utils/sql.db.utils.d.ts +14 -0
- package/dist/utils/sql.db.utils.js +94 -0
- package/package.json +4 -2
- package/dist/services/generic-api-service.interface.d.ts +0 -27
- package/dist/services/generic-api.service.d.ts +0 -50
- package/dist/services/generic-api.service.js +0 -424
- package/dist/utils/db.utils.d.ts +0 -27
- package/dist/utils/db.utils.js +0 -323
- /package/dist/{controllers/api-controller.utils.d.ts → __tests__/test-database.interface.js} +0 -0
- /package/dist/{controllers/api-controller.utils.js → databases/models/database.interface.js} +0 -0
- /package/dist/{models/types/index.d.ts → databases/models/database.js} +0 -0
- /package/dist/{models/types/index.js → databases/mongo-db/models/no-sql-pipeline.interface.js} +0 -0
- /package/dist/{services/generic-api-service.interface.js → databases/operations/operation.js} +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { buildNoSqlMatch, buildFindOptions } from "../utils/index.js";
|
|
2
|
+
export async function find(db, queryObject, pluralResourceName) {
|
|
3
|
+
const collection = db.collection(pluralResourceName);
|
|
4
|
+
const matchDocument = buildNoSqlMatch(queryObject);
|
|
5
|
+
const filter = matchDocument.$match;
|
|
6
|
+
const options = buildFindOptions(queryObject);
|
|
7
|
+
const entities = await collection.find(filter, options).toArray();
|
|
8
|
+
return entities;
|
|
9
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import NoSqlPipeline from "../models/no-sql-pipeline.js";
|
|
2
|
+
export async function getAll(db, operations, pluralResourceName) {
|
|
3
|
+
const collection = db.collection(pluralResourceName);
|
|
4
|
+
const pipeline = new NoSqlPipeline()
|
|
5
|
+
.addOperations(operations)
|
|
6
|
+
.build();
|
|
7
|
+
let aggregateResult;
|
|
8
|
+
if (pipeline.length === 0) {
|
|
9
|
+
const cursor = collection.find();
|
|
10
|
+
aggregateResult = await cursor.toArray();
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const cursor = collection.aggregate(pipeline);
|
|
14
|
+
aggregateResult = await cursor.toArray();
|
|
15
|
+
}
|
|
16
|
+
return aggregateResult;
|
|
17
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Db } from "mongodb";
|
|
2
|
+
import { Operation } from "../../operations/operation.js";
|
|
3
|
+
import { IQueryOptions } from "@loomcore/common/models";
|
|
4
|
+
export declare function getById<T>(db: Db, operations: Operation[], queryObject: IQueryOptions, id: string, pluralResourceName: string): Promise<T | null>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { entityUtils } from "@loomcore/common/utils";
|
|
2
|
+
import { BadRequestError } from "../../../errors/index.js";
|
|
3
|
+
import NoSqlPipeline from "../models/no-sql-pipeline.js";
|
|
4
|
+
export async function getById(db, operations, queryObject, id, pluralResourceName) {
|
|
5
|
+
if (!entityUtils.isValidObjectId(id)) {
|
|
6
|
+
throw new BadRequestError('id is not a valid ObjectId');
|
|
7
|
+
}
|
|
8
|
+
const collection = db.collection(pluralResourceName);
|
|
9
|
+
queryObject.filters || (queryObject.filters = {});
|
|
10
|
+
queryObject.filters._id = { eq: id };
|
|
11
|
+
const pipeline = new NoSqlPipeline()
|
|
12
|
+
.addMatch(queryObject)
|
|
13
|
+
.addOperations(operations)
|
|
14
|
+
.build();
|
|
15
|
+
const entity = await collection.aggregate(pipeline).next();
|
|
16
|
+
return entity;
|
|
17
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Db } from "mongodb";
|
|
2
|
+
import { IModelSpec, IQueryOptions, IPagedResult } from "@loomcore/common/models";
|
|
3
|
+
import { Operation } from "../../operations/operation.js";
|
|
4
|
+
export declare function get<T>(db: Db, operations: Operation[], queryOptions: IQueryOptions, modelSpec: IModelSpec, pluralResourceName: string): Promise<IPagedResult<T>>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import NoSqlPipeline from "../models/no-sql-pipeline.js";
|
|
2
|
+
import { apiUtils } from "../../../utils/api.utils.js";
|
|
3
|
+
export async function get(db, operations, queryOptions, modelSpec, pluralResourceName) {
|
|
4
|
+
const collection = db.collection(pluralResourceName);
|
|
5
|
+
const pipeline = new NoSqlPipeline()
|
|
6
|
+
.addMatch(queryOptions, modelSpec)
|
|
7
|
+
.addOperations(operations)
|
|
8
|
+
.addQueryOptions(queryOptions, true)
|
|
9
|
+
.build();
|
|
10
|
+
const cursor = collection.aggregate(pipeline);
|
|
11
|
+
const aggregateResult = await cursor.next();
|
|
12
|
+
const pagedResult = apiUtils.getPagedResult(aggregateResult?.data || [], aggregateResult?.total || 0, queryOptions);
|
|
13
|
+
return pagedResult;
|
|
14
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function buildFindOptions(queryOptions) {
|
|
2
|
+
let findOptions = {};
|
|
3
|
+
if (queryOptions) {
|
|
4
|
+
if (queryOptions.orderBy) {
|
|
5
|
+
findOptions.sort = {
|
|
6
|
+
[queryOptions.orderBy]: queryOptions.sortDirection === 'asc' ? 1 : -1
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
if (queryOptions.page && queryOptions.pageSize) {
|
|
10
|
+
findOptions.skip = (queryOptions.page - 1) * queryOptions.pageSize;
|
|
11
|
+
findOptions.limit = queryOptions.pageSize;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return findOptions;
|
|
15
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { entityUtils } from '@loomcore/common/utils';
|
|
2
|
+
import { ObjectId } from 'mongodb';
|
|
3
|
+
import { PROPERTIES_THAT_ARE_NOT_OBJECT_IDS } from '../../models/constants.js';
|
|
4
|
+
import { getPropertySchema } from '../../utils/get-property-schema.util.js';
|
|
5
|
+
export function buildNoSqlMatch(queryOptions, modelSpec) {
|
|
6
|
+
const filters = queryOptions.filters || {};
|
|
7
|
+
const schema = modelSpec?.fullSchema;
|
|
8
|
+
let match = {};
|
|
9
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
10
|
+
if (value) {
|
|
11
|
+
const propSchema = schema ? getPropertySchema(key, schema) : undefined;
|
|
12
|
+
if (value.eq !== undefined) {
|
|
13
|
+
const isObjectIdField = propSchema?.format === 'objectid';
|
|
14
|
+
const valueToCompare = value.eq;
|
|
15
|
+
if ((key === '_id' || isObjectIdField || (!schema && key.endsWith('Id') && !PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(key)))
|
|
16
|
+
&& typeof valueToCompare === 'string' && entityUtils.isValidObjectId(valueToCompare)) {
|
|
17
|
+
match[key] = new ObjectId(valueToCompare);
|
|
18
|
+
}
|
|
19
|
+
else if (typeof valueToCompare === 'string' && !isNaN(Number(valueToCompare))) {
|
|
20
|
+
match[key] = Number(valueToCompare);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
match[key] = valueToCompare;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else if (value.in !== undefined && Array.isArray(value.in)) {
|
|
27
|
+
const isObjectIdArray = propSchema?.type === 'array' && propSchema.items?.format === 'objectid';
|
|
28
|
+
if (key === '_id' || isObjectIdArray || (!schema && (key.endsWith('Id') || key.endsWith('Ids')) && !PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(key))) {
|
|
29
|
+
const objectIds = value.in
|
|
30
|
+
.filter(val => typeof val === 'string' && entityUtils.isValidObjectId(val))
|
|
31
|
+
.map(val => new ObjectId(val));
|
|
32
|
+
if (objectIds.length > 0) {
|
|
33
|
+
match[key] = { $in: objectIds };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const convertedValues = value.in.map(val => typeof val === 'string' && !isNaN(Number(val)) ? Number(val) : val);
|
|
38
|
+
match[key] = { $in: convertedValues };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (value.gte !== undefined) {
|
|
42
|
+
match[key] = { $gte: value.gte };
|
|
43
|
+
}
|
|
44
|
+
else if (value.lte !== undefined) {
|
|
45
|
+
match[key] = { $lte: value.lte };
|
|
46
|
+
}
|
|
47
|
+
else if (value.gt !== undefined) {
|
|
48
|
+
match[key] = { $gt: value.gt };
|
|
49
|
+
}
|
|
50
|
+
else if (value.lt !== undefined) {
|
|
51
|
+
match[key] = { $lt: value.lt };
|
|
52
|
+
}
|
|
53
|
+
else if (value.contains !== undefined) {
|
|
54
|
+
match[key] = { $regex: value.contains, $options: 'i' };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { $match: match };
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function convertObjectIdsToStrings<T>(entity: T): T;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { ObjectId } from 'mongodb';
|
|
3
|
+
export function convertObjectIdsToStrings(entity) {
|
|
4
|
+
if (!entity)
|
|
5
|
+
return entity;
|
|
6
|
+
const clone = _.cloneDeep(entity);
|
|
7
|
+
const processEntity = (obj) => {
|
|
8
|
+
if (!obj)
|
|
9
|
+
return obj;
|
|
10
|
+
if (obj instanceof ObjectId) {
|
|
11
|
+
return obj.toString();
|
|
12
|
+
}
|
|
13
|
+
if (obj instanceof Date) {
|
|
14
|
+
return obj;
|
|
15
|
+
}
|
|
16
|
+
if (Array.isArray(obj)) {
|
|
17
|
+
return obj.map(item => processEntity(item));
|
|
18
|
+
}
|
|
19
|
+
if (typeof obj === 'object') {
|
|
20
|
+
const result = { ...obj };
|
|
21
|
+
for (const key of Object.keys(result)) {
|
|
22
|
+
const value = result[key];
|
|
23
|
+
if (value !== null && value !== undefined) {
|
|
24
|
+
result[key] = processEntity(value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
return obj;
|
|
30
|
+
};
|
|
31
|
+
return processEntity(clone);
|
|
32
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Join } from '../../operations/join.operation.js';
|
|
2
|
+
export function convertOperationsToPipeline(operations) {
|
|
3
|
+
let pipeline = [];
|
|
4
|
+
operations.forEach(operation => {
|
|
5
|
+
if (operation instanceof Join) {
|
|
6
|
+
const needsObjectIdConversion = operation.foreignField === '_id';
|
|
7
|
+
if (needsObjectIdConversion) {
|
|
8
|
+
pipeline.push({
|
|
9
|
+
$lookup: {
|
|
10
|
+
from: operation.from,
|
|
11
|
+
let: { localId: { $cond: [
|
|
12
|
+
{ $eq: [{ $type: `$${operation.localField}` }, 'string'] },
|
|
13
|
+
{ $toObjectId: `$${operation.localField}` },
|
|
14
|
+
`$${operation.localField}`
|
|
15
|
+
] } },
|
|
16
|
+
pipeline: [
|
|
17
|
+
{
|
|
18
|
+
$match: {
|
|
19
|
+
$expr: {
|
|
20
|
+
$eq: [`$${operation.foreignField}`, '$$localId']
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
as: `${operation.as}Arr`
|
|
26
|
+
}
|
|
27
|
+
}, {
|
|
28
|
+
$unwind: {
|
|
29
|
+
path: `$${operation.as}Arr`,
|
|
30
|
+
preserveNullAndEmptyArrays: true
|
|
31
|
+
}
|
|
32
|
+
}, {
|
|
33
|
+
$addFields: {
|
|
34
|
+
[operation.as]: `$${operation.as}Arr`
|
|
35
|
+
}
|
|
36
|
+
}, {
|
|
37
|
+
$project: {
|
|
38
|
+
[`${operation.as}Arr`]: 0
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
pipeline.push({
|
|
44
|
+
$lookup: {
|
|
45
|
+
from: operation.from,
|
|
46
|
+
localField: operation.localField,
|
|
47
|
+
foreignField: operation.foreignField,
|
|
48
|
+
as: `${operation.as}Arr`
|
|
49
|
+
}
|
|
50
|
+
}, {
|
|
51
|
+
$unwind: {
|
|
52
|
+
path: `$${operation.as}Arr`,
|
|
53
|
+
preserveNullAndEmptyArrays: true
|
|
54
|
+
}
|
|
55
|
+
}, {
|
|
56
|
+
$addFields: {
|
|
57
|
+
[operation.as]: `$${operation.as}Arr`
|
|
58
|
+
}
|
|
59
|
+
}, {
|
|
60
|
+
$project: {
|
|
61
|
+
[`${operation.as}Arr`]: 0
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return pipeline;
|
|
68
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function convertQueryOptionsToPipeline(queryOptions, pagination) {
|
|
2
|
+
let pipeline = [];
|
|
3
|
+
if (queryOptions.orderBy || (queryOptions.page && queryOptions.pageSize)) {
|
|
4
|
+
let results = [];
|
|
5
|
+
if (queryOptions.orderBy) {
|
|
6
|
+
results.push({
|
|
7
|
+
$sort: {
|
|
8
|
+
[queryOptions.orderBy]: queryOptions.sortDirection === 'asc' ? 1 : -1
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
if (queryOptions.page && queryOptions.pageSize) {
|
|
13
|
+
results.push({ $skip: (queryOptions.page - 1) * queryOptions.pageSize });
|
|
14
|
+
results.push({ $limit: queryOptions.pageSize });
|
|
15
|
+
}
|
|
16
|
+
if (pagination) {
|
|
17
|
+
pipeline.push({
|
|
18
|
+
$facet: {
|
|
19
|
+
data: results,
|
|
20
|
+
count: [{ $count: 'total' }]
|
|
21
|
+
}
|
|
22
|
+
}, {
|
|
23
|
+
$project: {
|
|
24
|
+
data: 1,
|
|
25
|
+
total: { $arrayElemAt: ['$count.total', 0] }
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return pipeline;
|
|
31
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { entityUtils } from '@loomcore/common/utils';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import { ObjectId } from 'mongodb';
|
|
4
|
+
import { PROPERTIES_THAT_ARE_NOT_OBJECT_IDS } from '../../models/constants.js';
|
|
5
|
+
export function convertStringsToObjectIds(entity, schema) {
|
|
6
|
+
if (!entity)
|
|
7
|
+
return entity;
|
|
8
|
+
const clone = _.cloneDeep(entity);
|
|
9
|
+
if (clone._id && typeof clone._id === 'string' && entityUtils.isValidObjectId(clone._id)) {
|
|
10
|
+
clone._id = new ObjectId(clone._id);
|
|
11
|
+
}
|
|
12
|
+
const processEntity = (obj, subSchema, path = []) => {
|
|
13
|
+
if (!obj || typeof obj !== 'object')
|
|
14
|
+
return obj;
|
|
15
|
+
if (obj instanceof Date) {
|
|
16
|
+
return obj;
|
|
17
|
+
}
|
|
18
|
+
if (subSchema.allOf && Array.isArray(subSchema.allOf)) {
|
|
19
|
+
let result = { ...obj };
|
|
20
|
+
for (const nestedSchema of subSchema.allOf) {
|
|
21
|
+
result = processEntity(result, nestedSchema, path);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(obj)) {
|
|
26
|
+
const items = subSchema.items;
|
|
27
|
+
if (!items)
|
|
28
|
+
return obj;
|
|
29
|
+
if (items.format === 'objectid') {
|
|
30
|
+
if (path.length === 1 && PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(path[0])) {
|
|
31
|
+
return obj;
|
|
32
|
+
}
|
|
33
|
+
return obj.map(item => {
|
|
34
|
+
if (typeof item === 'string' && entityUtils.isValidObjectId(item)) {
|
|
35
|
+
return new ObjectId(item);
|
|
36
|
+
}
|
|
37
|
+
return item;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (items.type === 'object') {
|
|
41
|
+
return obj.map((item, index) => processEntity(item, items, [...path, index.toString()]));
|
|
42
|
+
}
|
|
43
|
+
return obj;
|
|
44
|
+
}
|
|
45
|
+
const result = { ...obj };
|
|
46
|
+
if (subSchema.type === 'object' && subSchema.properties) {
|
|
47
|
+
for (const [key, propSchema] of Object.entries(subSchema.properties)) {
|
|
48
|
+
if (!propSchema || typeof propSchema !== 'object' || result[key] === null || result[key] === undefined) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const typedPropSchema = propSchema;
|
|
52
|
+
const fullPath = [...path, key];
|
|
53
|
+
const value = result[key];
|
|
54
|
+
const isObjectIdField = typedPropSchema.format === 'objectid';
|
|
55
|
+
if (isObjectIdField && path.length === 0 && PROPERTIES_THAT_ARE_NOT_OBJECT_IDS.includes(key)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (isObjectIdField && typeof value === 'string' && entityUtils.isValidObjectId(value)) {
|
|
59
|
+
result[key] = new ObjectId(value);
|
|
60
|
+
}
|
|
61
|
+
else if (typedPropSchema.type === 'array' && Array.isArray(value)) {
|
|
62
|
+
result[key] = processEntity(value, typedPropSchema, fullPath);
|
|
63
|
+
}
|
|
64
|
+
else if (typedPropSchema.type === 'object' && typeof value === 'object' && !Array.isArray(value)) {
|
|
65
|
+
result[key] = processEntity(value, typedPropSchema, fullPath);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
};
|
|
71
|
+
return processEntity(clone, schema);
|
|
72
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './build-find-options.util.js';
|
|
2
|
+
export * from './build-no-sql-match.util.js';
|
|
3
|
+
export * from './convert-object-ids-to-strings.util.js';
|
|
4
|
+
export * from './convert-operations-to-pipeline.util.js';
|
|
5
|
+
export * from './convert-query-options-to-pipeline.util.js';
|
|
6
|
+
export * from './convert-strings-to-object-ids.util.js';
|
|
7
|
+
export * from '../models/no-sql-pipeline.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './build-find-options.util.js';
|
|
2
|
+
export * from './build-no-sql-match.util.js';
|
|
3
|
+
export * from './convert-object-ids-to-strings.util.js';
|
|
4
|
+
export * from './convert-operations-to-pipeline.util.js';
|
|
5
|
+
export * from './convert-query-options-to-pipeline.util.js';
|
|
6
|
+
export * from './convert-strings-to-object-ids.util.js';
|
|
7
|
+
export * from '../models/no-sql-pipeline.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Client } from 'pg';
|
|
2
|
+
import { Operation } from "../../operations/operation.js";
|
|
3
|
+
import { IEntity, IQueryOptions } from '@loomcore/common/models';
|
|
4
|
+
export declare function batchUpdate<T extends IEntity>(client: Client, entities: Partial<T>[], operations: Operation[], queryObject: IQueryOptions, pluralResourceName: string): Promise<T[]>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Join } from "../../operations/join.operation.js";
|
|
2
|
+
import { BadRequestError } from "../../../errors/index.js";
|
|
3
|
+
import { buildJoinClauses } from '../utils/build-join-clauses.js';
|
|
4
|
+
import { columnsAndValuesFromEntity } from '../utils/columns-and-values-from-entity.js';
|
|
5
|
+
import { buildWhereClause } from '../utils/build-where-clause.js';
|
|
6
|
+
export async function batchUpdate(client, entities, operations, queryObject, pluralResourceName) {
|
|
7
|
+
if (!entities || entities.length === 0) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const entityIds = [];
|
|
11
|
+
for (const entity of entities) {
|
|
12
|
+
if (!entity._id || typeof entity._id !== 'string') {
|
|
13
|
+
throw new BadRequestError('Each entity in a batch update must have a valid _id.');
|
|
14
|
+
}
|
|
15
|
+
entityIds.push(entity._id);
|
|
16
|
+
}
|
|
17
|
+
queryObject.filters = queryObject.filters || {};
|
|
18
|
+
try {
|
|
19
|
+
await client.query('BEGIN');
|
|
20
|
+
for (const entity of entities) {
|
|
21
|
+
const { _id, ...updateData } = entity;
|
|
22
|
+
if (Object.keys(updateData).length === 0) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const { columns, values } = columnsAndValuesFromEntity(updateData);
|
|
26
|
+
const setClause = columns.map((col, index) => `${col} = $${index + 1}`).join(', ');
|
|
27
|
+
queryObject.filters._id = { eq: _id };
|
|
28
|
+
const { whereClause } = buildWhereClause(queryObject, values);
|
|
29
|
+
const query = `
|
|
30
|
+
UPDATE "${pluralResourceName}"
|
|
31
|
+
SET ${setClause}
|
|
32
|
+
${whereClause}
|
|
33
|
+
`;
|
|
34
|
+
await client.query(query, values);
|
|
35
|
+
}
|
|
36
|
+
await client.query('COMMIT');
|
|
37
|
+
const joinClauses = buildJoinClauses(operations, pluralResourceName);
|
|
38
|
+
const hasJoins = operations.some(op => op instanceof Join);
|
|
39
|
+
const tablePrefix = hasJoins ? pluralResourceName : undefined;
|
|
40
|
+
queryObject.filters._id = { in: entityIds };
|
|
41
|
+
const { whereClause, values } = buildWhereClause(queryObject, [], tablePrefix);
|
|
42
|
+
const selectQuery = `
|
|
43
|
+
SELECT * FROM "${pluralResourceName}" ${joinClauses}
|
|
44
|
+
${whereClause}
|
|
45
|
+
`;
|
|
46
|
+
const result = await client.query(selectQuery, values);
|
|
47
|
+
return result.rows;
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
await client.query('ROLLBACK');
|
|
51
|
+
if (err.code === '23505') {
|
|
52
|
+
throw new BadRequestError(`One or more ${pluralResourceName} have duplicate key violations`);
|
|
53
|
+
}
|
|
54
|
+
throw new BadRequestError(`Error updating ${pluralResourceName}: ${err.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { BadRequestError, DuplicateKeyError } from "../../../errors/index.js";
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
export async function createMany(client, pluralResourceName, entities) {
|
|
4
|
+
if (entities.length === 0) {
|
|
5
|
+
return {
|
|
6
|
+
insertedIds: [],
|
|
7
|
+
entities: []
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const entitiesWithIds = entities.map(entity => {
|
|
12
|
+
entity._id = entity._id ?? randomUUID().toString();
|
|
13
|
+
return entity;
|
|
14
|
+
});
|
|
15
|
+
const tableColumns = await client.query(`
|
|
16
|
+
SELECT column_name, column_default
|
|
17
|
+
FROM information_schema.columns
|
|
18
|
+
WHERE table_schema = current_schema()
|
|
19
|
+
AND table_name = $1
|
|
20
|
+
ORDER BY ordinal_position
|
|
21
|
+
`, [pluralResourceName]);
|
|
22
|
+
if (tableColumns.rows.length === 0) {
|
|
23
|
+
throw new BadRequestError(`Unable to resolve columns for ${pluralResourceName}`);
|
|
24
|
+
}
|
|
25
|
+
const allValues = [];
|
|
26
|
+
const valueClauses = [];
|
|
27
|
+
entitiesWithIds.forEach((entity, entityIndex) => {
|
|
28
|
+
const objectEntries = Object.entries(entity);
|
|
29
|
+
const values = tableColumns.rows.map(column => {
|
|
30
|
+
if (objectEntries.find(entry => entry[0] === column.column_name)) {
|
|
31
|
+
return objectEntries.find(entry => entry[0] === column.column_name)?.[1];
|
|
32
|
+
}
|
|
33
|
+
return column.column_default;
|
|
34
|
+
});
|
|
35
|
+
const placeholders = values.map((_, valueIndex) => {
|
|
36
|
+
const paramIndex = entityIndex * values.length + valueIndex + 1;
|
|
37
|
+
return `$${paramIndex}`;
|
|
38
|
+
}).join(', ');
|
|
39
|
+
valueClauses.push(`(${placeholders})`);
|
|
40
|
+
allValues.push(...values);
|
|
41
|
+
});
|
|
42
|
+
const query = `
|
|
43
|
+
INSERT INTO "${pluralResourceName}" (${tableColumns.rows.map(column => `"${column.column_name}"`).join(', ')})
|
|
44
|
+
VALUES ${valueClauses.join(', ')}
|
|
45
|
+
RETURNING _id
|
|
46
|
+
`;
|
|
47
|
+
const result = await client.query(query, allValues);
|
|
48
|
+
if (result.rows.length !== entitiesWithIds.length) {
|
|
49
|
+
throw new BadRequestError(`Error creating ${pluralResourceName}: Expected ${entitiesWithIds.length} rows, got ${result.rows.length}`);
|
|
50
|
+
}
|
|
51
|
+
const insertedIds = result.rows.map(row => row._id);
|
|
52
|
+
return {
|
|
53
|
+
insertedIds,
|
|
54
|
+
entities: entitiesWithIds
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
if (err.code === '23505') {
|
|
59
|
+
throw new DuplicateKeyError(`One or more ${pluralResourceName} already exist`);
|
|
60
|
+
}
|
|
61
|
+
throw new BadRequestError(`Error creating ${pluralResourceName}: ${err.message}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BadRequestError, DuplicateKeyError } from "../../../errors/index.js";
|
|
2
|
+
import { randomUUID } from 'crypto';
|
|
3
|
+
import { columnsAndValuesFromEntity } from '../utils/columns-and-values-from-entity.js';
|
|
4
|
+
export async function create(client, pluralResourceName, entity) {
|
|
5
|
+
try {
|
|
6
|
+
entity._id = entity._id ?? randomUUID().toString();
|
|
7
|
+
const { columns, values } = columnsAndValuesFromEntity(entity);
|
|
8
|
+
const placeholders = columns.map((_, index) => `$${index + 1}`).join(', ');
|
|
9
|
+
const query = `
|
|
10
|
+
INSERT INTO "${pluralResourceName}" (${columns.join(', ')})
|
|
11
|
+
VALUES (${placeholders})
|
|
12
|
+
RETURNING _id
|
|
13
|
+
`;
|
|
14
|
+
const result = await client.query(query, values);
|
|
15
|
+
if (result.rows.length === 0) {
|
|
16
|
+
throw new BadRequestError(`Error creating ${pluralResourceName}: No row returned`);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
insertedId: entity._id,
|
|
20
|
+
entity: entity
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
if (err.code === '23505') {
|
|
25
|
+
throw new DuplicateKeyError(`${pluralResourceName} already exists`);
|
|
26
|
+
}
|
|
27
|
+
throw new BadRequestError(`Error creating ${pluralResourceName}: ${err.message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DeleteResult } from "../../models/delete-result.js";
|
|
2
|
+
export async function deleteById(client, id, pluralResourceName) {
|
|
3
|
+
const query = `DELETE FROM "${pluralResourceName}" WHERE "_id" = $1`;
|
|
4
|
+
const result = await client.query(query, [id]);
|
|
5
|
+
return new DeleteResult(true, result.rowCount || 0);
|
|
6
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Client } from 'pg';
|
|
2
|
+
import { IQueryOptions } from "@loomcore/common/models";
|
|
3
|
+
import { DeleteResult } from "../../models/delete-result.js";
|
|
4
|
+
export declare function deleteMany(client: Client, queryObject: IQueryOptions, pluralResourceName: string): Promise<DeleteResult>;
|