@loomcore/api 0.1.53 → 0.1.57
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/dist/__tests__/common-test.utils.d.ts +8 -2
- package/dist/__tests__/common-test.utils.js +50 -16
- package/dist/__tests__/index.d.ts +0 -1
- package/dist/__tests__/index.js +0 -1
- package/dist/__tests__/postgres-test-migrations/postgres-test-schema.d.ts +3 -0
- package/dist/__tests__/postgres-test-migrations/postgres-test-schema.js +97 -0
- package/dist/__tests__/postgres.test-database.js +9 -8
- package/dist/__tests__/setup/vitest-setup.js +15 -0
- package/dist/__tests__/test-objects.d.ts +6 -3
- package/dist/__tests__/test-objects.js +10 -2
- package/dist/controllers/api.controller.d.ts +2 -0
- package/dist/controllers/api.controller.js +66 -11
- package/dist/databases/migrations/migration-runner.js +6 -6
- package/dist/databases/models/database.interface.d.ts +9 -8
- package/dist/databases/mongo-db/commands/mongo-delete-by-id.command.d.ts +2 -1
- package/dist/databases/mongo-db/commands/mongo-full-updateby-id.command.d.ts +2 -1
- package/dist/databases/mongo-db/commands/mongo-partial-update-by-id.command.d.ts +2 -1
- package/dist/databases/mongo-db/migrations/{mongo-foundational.d.ts → mongo-initial-schema.d.ts} +1 -1
- package/dist/databases/mongo-db/migrations/mongo-initial-schema.js +264 -0
- package/dist/databases/mongo-db/mongo-db.database.d.ts +9 -8
- package/dist/databases/mongo-db/mongo-db.database.js +2 -2
- package/dist/databases/mongo-db/queries/mongo-get-by-id.query.d.ts +2 -1
- package/dist/databases/postgres/commands/postgres-batch-update.command.js +1 -1
- package/dist/databases/postgres/commands/postgres-create-many.command.d.ts +2 -1
- package/dist/databases/postgres/commands/postgres-create-many.command.js +23 -28
- package/dist/databases/postgres/commands/postgres-create.command.d.ts +2 -1
- package/dist/databases/postgres/commands/postgres-create.command.js +6 -5
- package/dist/databases/postgres/commands/postgres-delete-by-id.command.d.ts +2 -1
- package/dist/databases/postgres/commands/postgres-full-update-by-id.command.d.ts +2 -1
- package/dist/databases/postgres/commands/postgres-partial-update-by-id.command.d.ts +2 -1
- package/dist/databases/postgres/migrations/__tests__/test-migration-helper.d.ts +4 -0
- package/dist/databases/postgres/migrations/__tests__/test-migration-helper.js +67 -0
- package/dist/databases/postgres/migrations/index.d.ts +1 -2
- package/dist/databases/postgres/migrations/index.js +1 -2
- package/dist/databases/postgres/migrations/{postgres-foundational.d.ts → postgres-initial-schema.d.ts} +1 -1
- package/dist/databases/postgres/migrations/postgres-initial-schema.js +436 -0
- package/dist/databases/postgres/postgres.database.d.ts +10 -9
- package/dist/databases/postgres/postgres.database.js +2 -2
- package/dist/databases/postgres/queries/postgres-get-by-id.query.d.ts +2 -1
- package/dist/models/refresh-token.model.d.ts +5 -4
- package/dist/models/refresh-token.model.js +3 -3
- package/dist/services/auth.service.d.ts +3 -2
- package/dist/services/auth.service.js +4 -4
- package/dist/services/generic-api-service/generic-api-service.interface.d.ts +8 -7
- package/dist/services/generic-api-service/generic-api.service.d.ts +8 -7
- package/dist/services/generic-api-service/generic-api.service.js +23 -23
- package/dist/services/multi-tenant-api.service.d.ts +1 -1
- package/dist/services/multi-tenant-api.service.js +3 -3
- package/dist/services/organization.service.d.ts +4 -3
- package/dist/services/organization.service.js +3 -3
- package/dist/services/user.service.d.ts +3 -2
- package/dist/services/user.service.js +2 -2
- package/dist/services/utils/audit-for-create.util.js +2 -1
- package/dist/services/utils/audit-for-update.util.js +2 -1
- package/dist/services/utils/strip-sender-provided-system-properties.util.js +2 -1
- package/package.json +2 -2
- package/dist/__tests__/postgres-test-migrations/100-create-test-entities-table.migration.d.ts +0 -21
- package/dist/__tests__/postgres-test-migrations/100-create-test-entities-table.migration.js +0 -61
- package/dist/__tests__/postgres-test-migrations/101-create-categories-table.migration.d.ts +0 -21
- package/dist/__tests__/postgres-test-migrations/101-create-categories-table.migration.js +0 -51
- package/dist/__tests__/postgres-test-migrations/102-create-products-table.migration.d.ts +0 -21
- package/dist/__tests__/postgres-test-migrations/102-create-products-table.migration.js +0 -60
- package/dist/__tests__/postgres-test-migrations/103-create-test-items-table.migration.d.ts +0 -21
- package/dist/__tests__/postgres-test-migrations/103-create-test-items-table.migration.js +0 -59
- package/dist/__tests__/postgres-test-migrations/test-migrations.d.ts +0 -3
- package/dist/__tests__/postgres-test-migrations/test-migrations.js +0 -12
- package/dist/databases/mongo-db/migrations/mongo-foundational.js +0 -30
- package/dist/databases/postgres/migrations/001-create-migrations-table.migration.d.ts +0 -21
- package/dist/databases/postgres/migrations/001-create-migrations-table.migration.js +0 -51
- package/dist/databases/postgres/migrations/002-create-organizations-table.migration.d.ts +0 -21
- package/dist/databases/postgres/migrations/002-create-organizations-table.migration.js +0 -76
- package/dist/databases/postgres/migrations/003-create-users-table.migration.d.ts +0 -21
- package/dist/databases/postgres/migrations/003-create-users-table.migration.js +0 -77
- package/dist/databases/postgres/migrations/004-create-refresh-tokens-table.migration.d.ts +0 -21
- package/dist/databases/postgres/migrations/004-create-refresh-tokens-table.migration.js +0 -72
- package/dist/databases/postgres/migrations/005-create-meta-org.migration.d.ts +0 -22
- package/dist/databases/postgres/migrations/005-create-meta-org.migration.js +0 -64
- package/dist/databases/postgres/migrations/006-create-admin-user.migration.d.ts +0 -19
- package/dist/databases/postgres/migrations/006-create-admin-user.migration.js +0 -52
- package/dist/databases/postgres/migrations/007-create-roles-table.migration.d.ts +0 -21
- package/dist/databases/postgres/migrations/007-create-roles-table.migration.js +0 -65
- package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.d.ts +0 -21
- package/dist/databases/postgres/migrations/008-create-user-roles-table.migration.js +0 -75
- package/dist/databases/postgres/migrations/009-create-features-table.migration.d.ts +0 -21
- package/dist/databases/postgres/migrations/009-create-features-table.migration.js +0 -65
- package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.d.ts +0 -21
- package/dist/databases/postgres/migrations/010-create-authorizations-table.migration.js +0 -77
- package/dist/databases/postgres/migrations/011-create-admin-authorization.migration.d.ts +0 -23
- package/dist/databases/postgres/migrations/011-create-admin-authorization.migration.js +0 -130
- package/dist/databases/postgres/migrations/database-builder.d.ts +0 -15
- package/dist/databases/postgres/migrations/database-builder.interface.d.ts +0 -10
- package/dist/databases/postgres/migrations/database-builder.js +0 -65
- package/dist/databases/postgres/migrations/migration.interface.d.ts +0 -11
- package/dist/databases/postgres/migrations/migration.interface.js +0 -1
- package/dist/databases/postgres/migrations/postgres-foundational.js +0 -47
- /package/dist/{databases/postgres/migrations/database-builder.interface.js → __tests__/setup/vitest-setup.d.ts} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Db } from "mongodb";
|
|
2
2
|
import { DeleteResult as GenericDeleteResult } from "../../models/delete-result.js";
|
|
3
|
-
|
|
3
|
+
import type { AppIdType } from "@loomcore/common/types";
|
|
4
|
+
export declare function deleteById(db: Db, id: AppIdType, pluralResourceName: string): Promise<GenericDeleteResult>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Db } from "mongodb";
|
|
2
2
|
import { Operation } from "../../operations/operation.js";
|
|
3
|
-
|
|
3
|
+
import type { AppIdType } from "@loomcore/common/types";
|
|
4
|
+
export declare function fullUpdateById<T>(db: Db, operations: Operation[], id: AppIdType, entity: any, pluralResourceName: string): Promise<T>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Db } from "mongodb";
|
|
2
2
|
import { Operation } from "../../operations/operation.js";
|
|
3
|
-
|
|
3
|
+
import type { AppIdType } from "@loomcore/common/types";
|
|
4
|
+
export declare function partialUpdateById<T>(db: Db, operations: Operation[], id: AppIdType, entity: Partial<any>, pluralResourceName: string): Promise<T>;
|
package/dist/databases/mongo-db/migrations/{mongo-foundational.d.ts → mongo-initial-schema.d.ts}
RENAMED
|
@@ -9,4 +9,4 @@ export interface SyntheticMigration {
|
|
|
9
9
|
context: Db;
|
|
10
10
|
}) => Promise<void>;
|
|
11
11
|
}
|
|
12
|
-
export declare const
|
|
12
|
+
export declare const getMongoInitialSchema: (config: IBaseApiConfig) => SyntheticMigration[];
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { initializeSystemUserContext, EmptyUserContext, getSystemUserContext } from '@loomcore/common/models';
|
|
3
|
+
import { MongoDBDatabase } from '../mongo-db.database.js';
|
|
4
|
+
import { AuthService, OrganizationService } from '../../../services/index.js';
|
|
5
|
+
export const getMongoInitialSchema = (config) => {
|
|
6
|
+
const migrations = [];
|
|
7
|
+
const isMultiTenant = config.app.isMultiTenant === true;
|
|
8
|
+
if (isMultiTenant) {
|
|
9
|
+
migrations.push({
|
|
10
|
+
name: '00000000000001_schema-organizations',
|
|
11
|
+
up: async ({ context: db }) => {
|
|
12
|
+
await db.createCollection('organizations');
|
|
13
|
+
await db.collection('organizations').createIndex({ name: 1 }, { unique: true });
|
|
14
|
+
await db.collection('organizations').createIndex({ code: 1 }, { unique: true });
|
|
15
|
+
await db.collection('organizations').createIndex({ isMetaOrg: 1 });
|
|
16
|
+
},
|
|
17
|
+
down: async ({ context: db }) => {
|
|
18
|
+
await db.collection('organizations').drop();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
migrations.push({
|
|
23
|
+
name: '00000000000002_schema-users',
|
|
24
|
+
up: async ({ context: db }) => {
|
|
25
|
+
await db.createCollection('users');
|
|
26
|
+
if (isMultiTenant) {
|
|
27
|
+
await db.collection('users').createIndex({ _orgId: 1, email: 1 }, { unique: true });
|
|
28
|
+
await db.collection('users').createIndex({ _orgId: 1 });
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
await db.collection('users').createIndex({ email: 1 }, { unique: true });
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
down: async ({ context: db }) => {
|
|
35
|
+
await db.collection('users').drop();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
migrations.push({
|
|
39
|
+
name: '00000000000003_schema-refresh-tokens',
|
|
40
|
+
up: async ({ context: db }) => {
|
|
41
|
+
await db.createCollection('refreshTokens');
|
|
42
|
+
await db.collection('refreshTokens').createIndex({ token: 1 }, { unique: true });
|
|
43
|
+
await db.collection('refreshTokens').createIndex({ userId: 1 });
|
|
44
|
+
await db.collection('refreshTokens').createIndex({ deviceId: 1 });
|
|
45
|
+
if (isMultiTenant) {
|
|
46
|
+
await db.collection('refreshTokens').createIndex({ _orgId: 1 });
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
down: async ({ context: db }) => {
|
|
50
|
+
await db.collection('refreshTokens').drop();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
migrations.push({
|
|
54
|
+
name: '00000000000004_schema-roles',
|
|
55
|
+
up: async ({ context: db }) => {
|
|
56
|
+
await db.createCollection('roles');
|
|
57
|
+
if (isMultiTenant) {
|
|
58
|
+
await db.collection('roles').createIndex({ _orgId: 1, name: 1 }, { unique: true });
|
|
59
|
+
await db.collection('roles').createIndex({ _orgId: 1 });
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
await db.collection('roles').createIndex({ name: 1 }, { unique: true });
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
down: async ({ context: db }) => {
|
|
66
|
+
await db.collection('roles').drop();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
migrations.push({
|
|
70
|
+
name: '00000000000005_schema-user-roles',
|
|
71
|
+
up: async ({ context: db }) => {
|
|
72
|
+
await db.createCollection('user_roles');
|
|
73
|
+
if (isMultiTenant) {
|
|
74
|
+
await db.collection('user_roles').createIndex({ _orgId: 1, userId: 1, roleId: 1 }, { unique: true });
|
|
75
|
+
await db.collection('user_roles').createIndex({ _orgId: 1 });
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
await db.collection('user_roles').createIndex({ userId: 1, roleId: 1 }, { unique: true });
|
|
79
|
+
}
|
|
80
|
+
await db.collection('user_roles').createIndex({ userId: 1 });
|
|
81
|
+
await db.collection('user_roles').createIndex({ roleId: 1 });
|
|
82
|
+
},
|
|
83
|
+
down: async ({ context: db }) => {
|
|
84
|
+
await db.collection('user_roles').drop();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
migrations.push({
|
|
88
|
+
name: '00000000000006_schema-features',
|
|
89
|
+
up: async ({ context: db }) => {
|
|
90
|
+
await db.createCollection('features');
|
|
91
|
+
if (isMultiTenant) {
|
|
92
|
+
await db.collection('features').createIndex({ _orgId: 1, name: 1 }, { unique: true });
|
|
93
|
+
await db.collection('features').createIndex({ _orgId: 1 });
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
await db.collection('features').createIndex({ name: 1 }, { unique: true });
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
down: async ({ context: db }) => {
|
|
100
|
+
await db.collection('features').drop();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
migrations.push({
|
|
104
|
+
name: '00000000000007_schema-authorizations',
|
|
105
|
+
up: async ({ context: db }) => {
|
|
106
|
+
await db.createCollection('authorizations');
|
|
107
|
+
if (isMultiTenant) {
|
|
108
|
+
await db.collection('authorizations').createIndex({ _orgId: 1, roleId: 1, featureId: 1 }, { unique: true });
|
|
109
|
+
await db.collection('authorizations').createIndex({ _orgId: 1 });
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
await db.collection('authorizations').createIndex({ roleId: 1, featureId: 1 }, { unique: true });
|
|
113
|
+
}
|
|
114
|
+
await db.collection('authorizations').createIndex({ roleId: 1 });
|
|
115
|
+
await db.collection('authorizations').createIndex({ featureId: 1 });
|
|
116
|
+
},
|
|
117
|
+
down: async ({ context: db }) => {
|
|
118
|
+
await db.collection('authorizations').drop();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
if (isMultiTenant && config.app.metaOrgName && config.app.metaOrgCode) {
|
|
122
|
+
migrations.push({
|
|
123
|
+
name: '00000000000008_data-meta-org',
|
|
124
|
+
up: async ({ context: db }) => {
|
|
125
|
+
const _id = randomUUID().toString();
|
|
126
|
+
const metaOrg = {
|
|
127
|
+
_id,
|
|
128
|
+
name: config.app.metaOrgName,
|
|
129
|
+
code: config.app.metaOrgCode,
|
|
130
|
+
description: undefined,
|
|
131
|
+
status: 1,
|
|
132
|
+
isMetaOrg: true,
|
|
133
|
+
authToken: undefined,
|
|
134
|
+
_created: new Date(),
|
|
135
|
+
_createdBy: 'system',
|
|
136
|
+
_updated: new Date(),
|
|
137
|
+
_updatedBy: 'system',
|
|
138
|
+
_deleted: undefined,
|
|
139
|
+
_deletedBy: undefined
|
|
140
|
+
};
|
|
141
|
+
await db.collection('organizations').insertOne(metaOrg);
|
|
142
|
+
initializeSystemUserContext(config.email?.systemEmailAddress || 'system@example.com', metaOrg);
|
|
143
|
+
},
|
|
144
|
+
down: async ({ context: db }) => {
|
|
145
|
+
await db.collection('organizations').deleteMany({ isMetaOrg: true });
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
if (config.adminUser?.email && config.adminUser?.password) {
|
|
150
|
+
migrations.push({
|
|
151
|
+
name: '00000000000009_data-admin-user',
|
|
152
|
+
up: async ({ context: db }) => {
|
|
153
|
+
if (!config.adminUser?.email || !config.adminUser?.password) {
|
|
154
|
+
throw new Error('Admin user email and password must be provided in config');
|
|
155
|
+
}
|
|
156
|
+
const database = new MongoDBDatabase(db);
|
|
157
|
+
const authService = new AuthService(database);
|
|
158
|
+
const systemUserContext = getSystemUserContext();
|
|
159
|
+
const _id = randomUUID().toString();
|
|
160
|
+
await authService.createUser(systemUserContext, {
|
|
161
|
+
_id: _id,
|
|
162
|
+
_orgId: systemUserContext.organization?._id,
|
|
163
|
+
email: config.adminUser.email,
|
|
164
|
+
password: config.adminUser.password,
|
|
165
|
+
firstName: 'Admin',
|
|
166
|
+
lastName: 'User',
|
|
167
|
+
displayName: 'Admin User',
|
|
168
|
+
});
|
|
169
|
+
},
|
|
170
|
+
down: async ({ context: db }) => {
|
|
171
|
+
if (!config.adminUser?.email)
|
|
172
|
+
return;
|
|
173
|
+
await db.collection('users').deleteOne({ email: config.adminUser.email });
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (config.adminUser?.email && isMultiTenant) {
|
|
178
|
+
migrations.push({
|
|
179
|
+
name: '00000000000010_data-admin-authorizations',
|
|
180
|
+
up: async ({ context: db }) => {
|
|
181
|
+
if (!config.adminUser?.email) {
|
|
182
|
+
throw new Error('Admin user email not found in config');
|
|
183
|
+
}
|
|
184
|
+
const database = new MongoDBDatabase(db);
|
|
185
|
+
const organizationService = new OrganizationService(database);
|
|
186
|
+
const authService = new AuthService(database);
|
|
187
|
+
const metaOrg = await organizationService.getMetaOrg(EmptyUserContext);
|
|
188
|
+
if (!metaOrg) {
|
|
189
|
+
throw new Error('Meta organization not found. Ensure meta-org migration ran successfully.');
|
|
190
|
+
}
|
|
191
|
+
const adminUser = await authService.getUserByEmail(config.adminUser.email);
|
|
192
|
+
if (!adminUser) {
|
|
193
|
+
throw new Error('Admin user not found. Ensure admin-user migration ran successfully.');
|
|
194
|
+
}
|
|
195
|
+
const roleId = randomUUID().toString();
|
|
196
|
+
await db.collection('roles').insertOne({
|
|
197
|
+
_id: roleId,
|
|
198
|
+
_orgId: metaOrg._id,
|
|
199
|
+
name: 'admin'
|
|
200
|
+
});
|
|
201
|
+
const userRoleId = randomUUID().toString();
|
|
202
|
+
await db.collection('user_roles').insertOne({
|
|
203
|
+
_id: userRoleId,
|
|
204
|
+
_orgId: metaOrg._id,
|
|
205
|
+
userId: adminUser._id,
|
|
206
|
+
roleId: roleId,
|
|
207
|
+
_created: new Date(),
|
|
208
|
+
_createdBy: 'system',
|
|
209
|
+
_updated: new Date(),
|
|
210
|
+
_updatedBy: 'system',
|
|
211
|
+
_deleted: undefined,
|
|
212
|
+
_deletedBy: undefined
|
|
213
|
+
});
|
|
214
|
+
const featureId = randomUUID().toString();
|
|
215
|
+
await db.collection('features').insertOne({
|
|
216
|
+
_id: featureId,
|
|
217
|
+
_orgId: metaOrg._id,
|
|
218
|
+
name: 'admin'
|
|
219
|
+
});
|
|
220
|
+
const authorizationId = randomUUID().toString();
|
|
221
|
+
await db.collection('authorizations').insertOne({
|
|
222
|
+
_id: authorizationId,
|
|
223
|
+
_orgId: metaOrg._id,
|
|
224
|
+
roleId: roleId,
|
|
225
|
+
featureId: featureId,
|
|
226
|
+
startDate: undefined,
|
|
227
|
+
endDate: undefined,
|
|
228
|
+
config: undefined,
|
|
229
|
+
_created: new Date(),
|
|
230
|
+
_createdBy: 'system',
|
|
231
|
+
_updated: new Date(),
|
|
232
|
+
_updatedBy: 'system',
|
|
233
|
+
_deleted: undefined,
|
|
234
|
+
_deletedBy: undefined
|
|
235
|
+
});
|
|
236
|
+
},
|
|
237
|
+
down: async ({ context: db }) => {
|
|
238
|
+
const database = new MongoDBDatabase(db);
|
|
239
|
+
const organizationService = new OrganizationService(database);
|
|
240
|
+
const metaOrg = await organizationService.getMetaOrg(EmptyUserContext);
|
|
241
|
+
if (!metaOrg)
|
|
242
|
+
return;
|
|
243
|
+
const adminRole = await db.collection('roles').findOne({ _orgId: metaOrg._id, name: 'admin' });
|
|
244
|
+
const adminFeature = await db.collection('features').findOne({ _orgId: metaOrg._id, name: 'admin' });
|
|
245
|
+
if (adminRole && adminFeature) {
|
|
246
|
+
await db.collection('authorizations').deleteMany({
|
|
247
|
+
_orgId: metaOrg._id,
|
|
248
|
+
roleId: adminRole._id,
|
|
249
|
+
featureId: adminFeature._id
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
await db.collection('features').deleteMany({ _orgId: metaOrg._id, name: 'admin' });
|
|
253
|
+
if (adminRole) {
|
|
254
|
+
await db.collection('user_roles').deleteMany({
|
|
255
|
+
_orgId: metaOrg._id,
|
|
256
|
+
roleId: adminRole._id
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
await db.collection('roles').deleteMany({ _orgId: metaOrg._id, name: 'admin' });
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return migrations;
|
|
264
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Db } from "mongodb";
|
|
2
2
|
import { IModelSpec, IQueryOptions, IPagedResult, IEntity } from "@loomcore/common/models";
|
|
3
|
+
import type { AppIdType } from "@loomcore/common/types";
|
|
3
4
|
import { Operation } from "../operations/operation.js";
|
|
4
5
|
import { DeleteResult as GenericDeleteResult } from "../models/delete-result.js";
|
|
5
6
|
import { TSchema } from "@sinclair/typebox";
|
|
@@ -7,25 +8,25 @@ import { IDatabase } from "../models/database.interface.js";
|
|
|
7
8
|
export declare class MongoDBDatabase implements IDatabase {
|
|
8
9
|
private db;
|
|
9
10
|
constructor(db: Db);
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
preProcessEntity<T extends IEntity>(entity: Partial<T>, schema: TSchema): Partial<T>;
|
|
12
|
+
postProcessEntity<T extends IEntity>(single: T, schema: TSchema): T;
|
|
12
13
|
getAll<T extends IEntity>(operations: Operation[], pluralResourceName: string): Promise<T[]>;
|
|
13
14
|
get<T extends IEntity>(operations: Operation[], queryOptions: IQueryOptions, modelSpec: IModelSpec, pluralResourceName: string): Promise<IPagedResult<T>>;
|
|
14
|
-
getById<T extends IEntity>(operations: Operation[], queryObject: IQueryOptions, id:
|
|
15
|
+
getById<T extends IEntity>(operations: Operation[], queryObject: IQueryOptions, id: AppIdType, pluralResourceName: string): Promise<T | null>;
|
|
15
16
|
getCount(pluralResourceName: string): Promise<number>;
|
|
16
17
|
create<T extends IEntity>(entity: Partial<T>, pluralResourceName: string): Promise<{
|
|
17
|
-
insertedId:
|
|
18
|
+
insertedId: AppIdType;
|
|
18
19
|
entity: T;
|
|
19
20
|
}>;
|
|
20
21
|
createMany<T extends IEntity>(entities: Partial<T>[], pluralResourceName: string): Promise<{
|
|
21
|
-
insertedIds:
|
|
22
|
+
insertedIds: AppIdType[];
|
|
22
23
|
entities: T[];
|
|
23
24
|
}>;
|
|
24
25
|
batchUpdate<T extends IEntity>(entities: Partial<T>[], operations: Operation[], queryObject: IQueryOptions, pluralResourceName: string): Promise<T[]>;
|
|
25
|
-
fullUpdateById<T extends IEntity>(operations: Operation[], id:
|
|
26
|
-
partialUpdateById<T extends IEntity>(operations: Operation[], id:
|
|
26
|
+
fullUpdateById<T extends IEntity>(operations: Operation[], id: AppIdType, entity: Partial<T>, pluralResourceName: string): Promise<T>;
|
|
27
|
+
partialUpdateById<T extends IEntity>(operations: Operation[], id: AppIdType, entity: Partial<T>, pluralResourceName: string): Promise<T>;
|
|
27
28
|
update<T extends IEntity>(queryObject: IQueryOptions, entity: Partial<T>, operations: Operation[], pluralResourceName: string): Promise<T[]>;
|
|
28
|
-
deleteById(id:
|
|
29
|
+
deleteById(id: AppIdType, pluralResourceName: string): Promise<GenericDeleteResult>;
|
|
29
30
|
deleteMany(queryObject: IQueryOptions, pluralResourceName: string): Promise<GenericDeleteResult>;
|
|
30
31
|
find<T extends IEntity>(queryObject: IQueryOptions, pluralResourceName: string): Promise<T[]>;
|
|
31
32
|
findOne<T extends IEntity>(queryObject: IQueryOptions, pluralResourceName: string): Promise<T | null>;
|
|
@@ -8,13 +8,13 @@ export class MongoDBDatabase {
|
|
|
8
8
|
constructor(db) {
|
|
9
9
|
this.db = db;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
preProcessEntity(entity, schema) {
|
|
12
12
|
if (entity._id && !entityUtils.isValidObjectId(entity._id)) {
|
|
13
13
|
throw new BadRequestError('id is not a valid ObjectId');
|
|
14
14
|
}
|
|
15
15
|
return convertStringsToObjectIds(entity, schema);
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
postProcessEntity(single, schema) {
|
|
18
18
|
if (!single)
|
|
19
19
|
return single;
|
|
20
20
|
return convertObjectIdsToStrings(single);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Db } from "mongodb";
|
|
2
2
|
import { Operation } from "../../operations/operation.js";
|
|
3
3
|
import { IQueryOptions } from "@loomcore/common/models";
|
|
4
|
-
|
|
4
|
+
import type { AppIdType } from "@loomcore/common/types";
|
|
5
|
+
export declare function getById<T>(db: Db, operations: Operation[], queryObject: IQueryOptions, id: AppIdType, pluralResourceName: string): Promise<T | null>;
|
|
@@ -9,7 +9,7 @@ export async function batchUpdate(client, entities, operations, queryObject, plu
|
|
|
9
9
|
}
|
|
10
10
|
const entityIds = [];
|
|
11
11
|
for (const entity of entities) {
|
|
12
|
-
if (!entity._id || typeof entity._id !== 'string') {
|
|
12
|
+
if (!entity._id || (typeof entity._id !== 'string' && typeof entity._id !== 'number')) {
|
|
13
13
|
throw new BadRequestError('Each entity in a batch update must have a valid _id.');
|
|
14
14
|
}
|
|
15
15
|
entityIds.push(entity._id);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Client } from 'pg';
|
|
2
2
|
import { IEntity } from '@loomcore/common/models';
|
|
3
|
+
import type { AppIdType } from '@loomcore/common/types';
|
|
3
4
|
export declare function createMany<T extends IEntity>(client: Client, pluralResourceName: string, entities: Partial<T>[]): Promise<{
|
|
4
|
-
insertedIds:
|
|
5
|
+
insertedIds: AppIdType[];
|
|
5
6
|
entities: T[];
|
|
6
7
|
}>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { BadRequestError, DuplicateKeyError } from "../../../errors/index.js";
|
|
2
|
-
import { randomUUID } from 'crypto';
|
|
3
2
|
export async function createMany(client, pluralResourceName, entities) {
|
|
4
3
|
if (entities.length === 0) {
|
|
5
4
|
return {
|
|
@@ -8,50 +7,46 @@ export async function createMany(client, pluralResourceName, entities) {
|
|
|
8
7
|
};
|
|
9
8
|
}
|
|
10
9
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return entity;
|
|
10
|
+
entities.forEach(entity => {
|
|
11
|
+
delete entity._id;
|
|
14
12
|
});
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
const allColumns = new Set();
|
|
14
|
+
entities.forEach(entity => {
|
|
15
|
+
Object.keys(entity).forEach(key => {
|
|
16
|
+
if (key !== '_id') {
|
|
17
|
+
allColumns.add(key);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
const columns = Array.from(allColumns);
|
|
22
|
+
if (columns.length === 0) {
|
|
23
|
+
throw new BadRequestError(`No columns provided for ${pluralResourceName}`);
|
|
24
24
|
}
|
|
25
25
|
const allValues = [];
|
|
26
26
|
const valueClauses = [];
|
|
27
|
-
|
|
28
|
-
const
|
|
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
|
-
});
|
|
27
|
+
entities.forEach((entity, entityIndex) => {
|
|
28
|
+
const values = columns.map(column => entity[column]);
|
|
35
29
|
const placeholders = values.map((_, valueIndex) => {
|
|
36
|
-
const paramIndex = entityIndex *
|
|
30
|
+
const paramIndex = entityIndex * columns.length + valueIndex + 1;
|
|
37
31
|
return `$${paramIndex}`;
|
|
38
32
|
}).join(', ');
|
|
39
33
|
valueClauses.push(`(${placeholders})`);
|
|
40
34
|
allValues.push(...values);
|
|
41
35
|
});
|
|
42
36
|
const query = `
|
|
43
|
-
INSERT INTO "${pluralResourceName}" (${
|
|
37
|
+
INSERT INTO "${pluralResourceName}" (${columns.map(col => `"${col}"`).join(', ')})
|
|
44
38
|
VALUES ${valueClauses.join(', ')}
|
|
45
|
-
RETURNING
|
|
39
|
+
RETURNING *
|
|
46
40
|
`;
|
|
47
41
|
const result = await client.query(query, allValues);
|
|
48
|
-
if (result.rows.length !==
|
|
49
|
-
throw new BadRequestError(`Error creating ${pluralResourceName}: Expected ${
|
|
42
|
+
if (result.rows.length !== entities.length) {
|
|
43
|
+
throw new BadRequestError(`Error creating ${pluralResourceName}: Expected ${entities.length} rows, got ${result.rows.length}`);
|
|
50
44
|
}
|
|
51
|
-
const
|
|
45
|
+
const insertedRows = result.rows;
|
|
46
|
+
const insertedIds = insertedRows.map(row => row._id);
|
|
52
47
|
return {
|
|
53
48
|
insertedIds,
|
|
54
|
-
entities:
|
|
49
|
+
entities: insertedRows
|
|
55
50
|
};
|
|
56
51
|
}
|
|
57
52
|
catch (err) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Client } from 'pg';
|
|
2
2
|
import { IEntity } from "@loomcore/common/models";
|
|
3
|
+
import type { AppIdType } from "@loomcore/common/types";
|
|
3
4
|
export declare function create<T extends IEntity>(client: Client, pluralResourceName: string, entity: Partial<T>): Promise<{
|
|
4
|
-
insertedId:
|
|
5
|
+
insertedId: AppIdType;
|
|
5
6
|
entity: T;
|
|
6
7
|
}>;
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { BadRequestError, DuplicateKeyError } from "../../../errors/index.js";
|
|
2
|
-
import { randomUUID } from 'crypto';
|
|
3
2
|
import { columnsAndValuesFromEntity } from '../utils/columns-and-values-from-entity.js';
|
|
4
3
|
export async function create(client, pluralResourceName, entity) {
|
|
5
4
|
try {
|
|
6
|
-
|
|
5
|
+
delete entity._id;
|
|
7
6
|
const { columns, values } = columnsAndValuesFromEntity(entity);
|
|
8
7
|
const placeholders = columns.map((_, index) => `$${index + 1}`).join(', ');
|
|
9
8
|
const query = `
|
|
10
9
|
INSERT INTO "${pluralResourceName}" (${columns.join(', ')})
|
|
11
10
|
VALUES (${placeholders})
|
|
12
|
-
RETURNING
|
|
11
|
+
RETURNING *
|
|
13
12
|
`;
|
|
14
13
|
const result = await client.query(query, values);
|
|
15
14
|
if (result.rows.length === 0) {
|
|
16
15
|
throw new BadRequestError(`Error creating ${pluralResourceName}: No row returned`);
|
|
17
16
|
}
|
|
17
|
+
const insertedRow = result.rows[0];
|
|
18
|
+
const insertedId = insertedRow._id;
|
|
18
19
|
return {
|
|
19
|
-
insertedId:
|
|
20
|
-
entity:
|
|
20
|
+
insertedId: insertedId,
|
|
21
|
+
entity: insertedRow
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
catch (err) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { Client } from 'pg';
|
|
2
2
|
import { DeleteResult } from "../../models/delete-result.js";
|
|
3
|
-
|
|
3
|
+
import type { AppIdType } from '@loomcore/common/types';
|
|
4
|
+
export declare function deleteById(client: Client, id: AppIdType, pluralResourceName: string): Promise<DeleteResult>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Client } from 'pg';
|
|
2
2
|
import { Operation } from "../../operations/operation.js";
|
|
3
3
|
import { IEntity } from '@loomcore/common/models';
|
|
4
|
-
|
|
4
|
+
import type { AppIdType } from '@loomcore/common/types';
|
|
5
|
+
export declare function fullUpdateById<T extends IEntity>(client: Client, operations: Operation[], id: AppIdType, entity: Partial<T>, pluralResourceName: string): Promise<T>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Client } from 'pg';
|
|
2
2
|
import { Operation } from "../../operations/operation.js";
|
|
3
3
|
import { IEntity } from '@loomcore/common/models';
|
|
4
|
-
|
|
4
|
+
import type { AppIdType } from '@loomcore/common/types';
|
|
5
|
+
export declare function partialUpdateById<T extends IEntity>(client: Client, operations: Operation[], id: AppIdType, entity: Partial<T>, pluralResourceName: string): Promise<T>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Pool } from 'pg';
|
|
2
|
+
import { IBaseApiConfig } from '../../../../models/base-api-config.interface.js';
|
|
3
|
+
export declare function runInitialSchemaMigrations(pool: Pool, config: IBaseApiConfig): Promise<void>;
|
|
4
|
+
export declare function runTestSchemaMigrations(pool: Pool, config: IBaseApiConfig): Promise<void>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Umzug } from 'umzug';
|
|
2
|
+
import { getPostgresInitialSchema } from '../postgres-initial-schema.js';
|
|
3
|
+
import { getPostgresTestSchema } from '../../../../__tests__/postgres-test-migrations/postgres-test-schema.js';
|
|
4
|
+
export async function runInitialSchemaMigrations(pool, config) {
|
|
5
|
+
const initialSchema = getPostgresInitialSchema(config);
|
|
6
|
+
const umzug = new Umzug({
|
|
7
|
+
migrations: async () => {
|
|
8
|
+
return initialSchema.map(m => ({
|
|
9
|
+
name: m.name,
|
|
10
|
+
up: async () => {
|
|
11
|
+
await m.up({ context: pool });
|
|
12
|
+
},
|
|
13
|
+
down: async () => {
|
|
14
|
+
await m.down({ context: pool });
|
|
15
|
+
}
|
|
16
|
+
}));
|
|
17
|
+
},
|
|
18
|
+
context: pool,
|
|
19
|
+
storage: {
|
|
20
|
+
async executed({ context }) {
|
|
21
|
+
await context.query(`CREATE TABLE IF NOT EXISTS migrations (name text)`);
|
|
22
|
+
const result = await context.query(`SELECT name FROM migrations`);
|
|
23
|
+
return result.rows.map((r) => r.name);
|
|
24
|
+
},
|
|
25
|
+
async logMigration({ name, context }) {
|
|
26
|
+
await context.query(`INSERT INTO migrations (name) VALUES ($1)`, [name]);
|
|
27
|
+
},
|
|
28
|
+
async unlogMigration({ name, context }) {
|
|
29
|
+
await context.query(`DELETE FROM migrations WHERE name = $1`, [name]);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
logger: undefined,
|
|
33
|
+
});
|
|
34
|
+
await umzug.up();
|
|
35
|
+
}
|
|
36
|
+
export async function runTestSchemaMigrations(pool, config) {
|
|
37
|
+
const testSchema = getPostgresTestSchema(config);
|
|
38
|
+
const umzug = new Umzug({
|
|
39
|
+
migrations: async () => {
|
|
40
|
+
return testSchema.map(m => ({
|
|
41
|
+
name: m.name,
|
|
42
|
+
up: async () => {
|
|
43
|
+
await m.up({ context: pool });
|
|
44
|
+
},
|
|
45
|
+
down: async () => {
|
|
46
|
+
await m.down({ context: pool });
|
|
47
|
+
}
|
|
48
|
+
}));
|
|
49
|
+
},
|
|
50
|
+
context: pool,
|
|
51
|
+
storage: {
|
|
52
|
+
async executed({ context }) {
|
|
53
|
+
await context.query(`CREATE TABLE IF NOT EXISTS migrations (name text)`);
|
|
54
|
+
const result = await context.query(`SELECT name FROM migrations`);
|
|
55
|
+
return result.rows.map((r) => r.name);
|
|
56
|
+
},
|
|
57
|
+
async logMigration({ name, context }) {
|
|
58
|
+
await context.query(`INSERT INTO migrations (name) VALUES ($1)`, [name]);
|
|
59
|
+
},
|
|
60
|
+
async unlogMigration({ name, context }) {
|
|
61
|
+
await context.query(`DELETE FROM migrations WHERE name = $1`, [name]);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
logger: undefined,
|
|
65
|
+
});
|
|
66
|
+
await umzug.up();
|
|
67
|
+
}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './database-builder.js';
|
|
1
|
+
export * from './postgres-initial-schema.js';
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './database-builder.js';
|
|
1
|
+
export * from './postgres-initial-schema.js';
|
|
@@ -9,4 +9,4 @@ export interface SyntheticMigration {
|
|
|
9
9
|
context: Pool;
|
|
10
10
|
}) => Promise<void>;
|
|
11
11
|
}
|
|
12
|
-
export declare const
|
|
12
|
+
export declare const getPostgresInitialSchema: (config: IBaseApiConfig) => SyntheticMigration[];
|