@loomcore/api 0.1.85 → 0.1.87
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__/models/product.model.d.ts +3 -2
- package/dist/__tests__/models/product.model.js +1 -1
- package/dist/__tests__/postgres-test-migrations/postgres-test-schema.js +5 -5
- package/dist/databases/operations/__tests__/models/test-agent.model.d.ts +15 -15
- package/dist/databases/operations/__tests__/models/test-agent.model.js +2 -2
- package/dist/databases/operations/__tests__/models/test-client-report.model.d.ts +44 -44
- package/dist/databases/operations/__tests__/models/test-client-report.model.js +2 -2
- package/dist/databases/operations/__tests__/models/test-district.model.d.ts +2 -2
- package/dist/databases/operations/__tests__/models/test-district.model.js +1 -1
- package/dist/databases/operations/__tests__/models/test-email-address.model.d.ts +6 -6
- package/dist/databases/operations/__tests__/models/test-email-address.model.js +3 -3
- package/dist/databases/operations/__tests__/models/test-person.model.d.ts +16 -16
- package/dist/databases/operations/__tests__/models/test-person.model.js +5 -5
- package/dist/databases/operations/__tests__/models/test-phone-number.model.d.ts +6 -6
- package/dist/databases/operations/__tests__/models/test-phone-number.model.js +3 -3
- package/dist/databases/operations/__tests__/models/test-policy.model.d.ts +18 -18
- package/dist/databases/operations/__tests__/models/test-policy.model.js +2 -2
- package/dist/databases/operations/__tests__/models/test-premium.model.d.ts +2 -2
- package/dist/databases/operations/__tests__/models/test-premium.model.js +1 -1
- package/dist/databases/operations/__tests__/models/test-school.model.d.ts +2 -2
- package/dist/databases/operations/__tests__/models/test-school.model.js +1 -1
- package/dist/databases/postgres/commands/postgres-partial-update-by-id.command.js +3 -0
- package/dist/databases/postgres/migrations/postgres-initial-schema.js +49 -49
- package/dist/databases/postgres/postgres.database.js +11 -9
- package/dist/databases/postgres/utils/build-join-clauses.js +25 -5
- package/dist/databases/postgres/utils/build-where-clause.js +12 -1
- package/dist/databases/postgres/utils/columns-and-values-from-entity.js +4 -2
- package/dist/databases/postgres/utils/convert-keys.util.d.ts +4 -0
- package/dist/databases/postgres/utils/convert-keys.util.js +85 -0
- package/dist/databases/postgres/utils/convert-null-to-undefined.util.js +5 -3
- package/dist/services/user.service.js +3 -3
- package/package.json +2 -2
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { IAuditable, IEntity } from "@loomcore/common/models";
|
|
2
2
|
import { ICategory } from "./category.model.js";
|
|
3
|
+
import { AppIdType } from "@loomcore/common/types";
|
|
3
4
|
export interface IProduct extends IEntity, IAuditable {
|
|
4
5
|
name: string;
|
|
5
6
|
description?: string;
|
|
6
7
|
internalNumber?: string;
|
|
7
|
-
categoryId:
|
|
8
|
+
categoryId: AppIdType;
|
|
8
9
|
category?: ICategory;
|
|
9
10
|
}
|
|
10
11
|
export declare const ProductSchema: import("@sinclair/typebox").TObject<{
|
|
11
12
|
name: import("@sinclair/typebox").TString;
|
|
12
13
|
description: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
13
14
|
internalNumber: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
14
|
-
categoryId: import("@sinclair/typebox").TString
|
|
15
|
+
categoryId: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TNumber]>;
|
|
15
16
|
}>;
|
|
16
17
|
export declare const ProductSpec: import("@loomcore/common/models").IModelSpec<import("@sinclair/typebox").TSchema>;
|
|
17
18
|
export declare const PublicProductSchema: import("@sinclair/typebox").TObject<{}>;
|
|
@@ -4,7 +4,7 @@ export const ProductSchema = Type.Object({
|
|
|
4
4
|
name: Type.String(),
|
|
5
5
|
description: Type.Optional(Type.String()),
|
|
6
6
|
internalNumber: Type.Optional(Type.String()),
|
|
7
|
-
categoryId: Type.String({ title: 'Category ID' }),
|
|
7
|
+
categoryId: Type.Union([Type.String({ title: 'Category ID' }), Type.Number({ title: 'Category ID' })]),
|
|
8
8
|
});
|
|
9
9
|
export const ProductSpec = entityUtils.getModelSpec(ProductSchema, { isAuditable: true });
|
|
10
10
|
export const PublicProductSchema = Type.Omit(ProductSpec.fullSchema, ['internalNumber']);
|
|
@@ -11,7 +11,7 @@ export const getPostgresTestSchema = (config) => {
|
|
|
11
11
|
${orgColumnDef}
|
|
12
12
|
"name" VARCHAR(255) NOT NULL,
|
|
13
13
|
"description" TEXT,
|
|
14
|
-
"
|
|
14
|
+
"is_active" BOOLEAN,
|
|
15
15
|
"tags" TEXT[],
|
|
16
16
|
"count" INTEGER,
|
|
17
17
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
@@ -53,15 +53,15 @@ export const getPostgresTestSchema = (config) => {
|
|
|
53
53
|
${orgColumnDef}
|
|
54
54
|
"name" VARCHAR(255) NOT NULL,
|
|
55
55
|
"description" TEXT,
|
|
56
|
-
"
|
|
57
|
-
"
|
|
56
|
+
"internal_number" VARCHAR(255),
|
|
57
|
+
"category_id" INTEGER NOT NULL,
|
|
58
58
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
59
59
|
"_createdBy" INTEGER NOT NULL,
|
|
60
60
|
"_updated" TIMESTAMPTZ NOT NULL,
|
|
61
61
|
"_updatedBy" INTEGER NOT NULL,
|
|
62
62
|
"_deleted" TIMESTAMPTZ,
|
|
63
63
|
"_deletedBy" INTEGER,
|
|
64
|
-
CONSTRAINT "fk_products_category" FOREIGN KEY ("
|
|
64
|
+
CONSTRAINT "fk_products_category" FOREIGN KEY ("category_id") REFERENCES "categories"("_id") ON DELETE CASCADE
|
|
65
65
|
)
|
|
66
66
|
`);
|
|
67
67
|
},
|
|
@@ -79,7 +79,7 @@ export const getPostgresTestSchema = (config) => {
|
|
|
79
79
|
${orgColumnDef}
|
|
80
80
|
"name" VARCHAR(255) NOT NULL,
|
|
81
81
|
"value" INTEGER,
|
|
82
|
-
"
|
|
82
|
+
"event_date" TIMESTAMPTZ,
|
|
83
83
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
84
84
|
"_createdBy" INTEGER NOT NULL,
|
|
85
85
|
"_updated" TIMESTAMPTZ NOT NULL,
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import { IAuditable, IEntity } from "@loomcore/common/models";
|
|
2
2
|
import { ITestPersonModel } from "./test-person.model.js";
|
|
3
3
|
export interface ITestAgentModel extends IEntity, IAuditable {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
personId: number;
|
|
5
|
+
agentPerson?: ITestPersonModel;
|
|
6
6
|
}
|
|
7
7
|
export declare const testAgentSchema: import("@sinclair/typebox").TObject<{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
9
|
+
agentPerson: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
10
|
+
firstName: import("@sinclair/typebox").TString;
|
|
11
|
+
middleName: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
12
|
+
lastName: import("@sinclair/typebox").TString;
|
|
13
|
+
clientPhoneNumbers: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
14
|
+
phoneNumber: import("@sinclair/typebox").TString;
|
|
15
|
+
phoneNumberType: import("@sinclair/typebox").TString;
|
|
16
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
17
17
|
}>>;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
clientEmailAddresses: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
19
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
20
|
+
emailAddress: import("@sinclair/typebox").TString;
|
|
21
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
22
22
|
}>>;
|
|
23
23
|
}>>;
|
|
24
24
|
}>;
|
|
@@ -2,7 +2,7 @@ import { entityUtils } from "@loomcore/common/utils";
|
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { testPersonSchema } from "./test-person.model.js";
|
|
4
4
|
export const testAgentSchema = Type.Object({
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
personId: Type.Number(),
|
|
6
|
+
agentPerson: Type.Optional(testPersonSchema),
|
|
7
7
|
});
|
|
8
8
|
export const testAgentModelSpec = entityUtils.getModelSpec(testAgentSchema, { isAuditable: true });
|
|
@@ -3,68 +3,68 @@ import { ITestAgentModel } from "./test-agent.model.js";
|
|
|
3
3
|
import { ITestPolicyModel } from "./test-policy.model.js";
|
|
4
4
|
import type { IAuditable, IEntity } from "@loomcore/common/models";
|
|
5
5
|
export interface ITestClientReportsModel extends IEntity, IAuditable {
|
|
6
|
-
|
|
6
|
+
clientPerson: ITestPersonModel;
|
|
7
7
|
agent?: ITestAgentModel;
|
|
8
|
-
|
|
8
|
+
clientPolicies?: ITestPolicyModel[];
|
|
9
9
|
}
|
|
10
10
|
export declare const testClientReportsSchema: import("@sinclair/typebox").TObject<{
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
clientPerson: import("@sinclair/typebox").TObject<{
|
|
12
|
+
firstName: import("@sinclair/typebox").TString;
|
|
13
|
+
middleName: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
14
|
+
lastName: import("@sinclair/typebox").TString;
|
|
15
|
+
clientPhoneNumbers: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
16
|
+
phoneNumber: import("@sinclair/typebox").TString;
|
|
17
|
+
phoneNumberType: import("@sinclair/typebox").TString;
|
|
18
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
19
19
|
}>>;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
clientEmailAddresses: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
21
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
22
|
+
emailAddress: import("@sinclair/typebox").TString;
|
|
23
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
24
24
|
}>>;
|
|
25
25
|
}>;
|
|
26
26
|
agent: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
28
|
+
agentPerson: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
29
|
+
firstName: import("@sinclair/typebox").TString;
|
|
30
|
+
middleName: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
31
|
+
lastName: import("@sinclair/typebox").TString;
|
|
32
|
+
clientPhoneNumbers: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
33
|
+
phoneNumber: import("@sinclair/typebox").TString;
|
|
34
|
+
phoneNumberType: import("@sinclair/typebox").TString;
|
|
35
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
36
36
|
}>>;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
clientEmailAddresses: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
38
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
39
|
+
emailAddress: import("@sinclair/typebox").TString;
|
|
40
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
41
41
|
}>>;
|
|
42
42
|
}>>;
|
|
43
43
|
}>>;
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
clientPolicies: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
45
|
+
clientId: import("@sinclair/typebox").TNumber;
|
|
46
46
|
amount: import("@sinclair/typebox").TNumber;
|
|
47
47
|
frequency: import("@sinclair/typebox").TString;
|
|
48
48
|
agents: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
50
|
+
agentPerson: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
51
|
+
firstName: import("@sinclair/typebox").TString;
|
|
52
|
+
middleName: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
53
|
+
lastName: import("@sinclair/typebox").TString;
|
|
54
|
+
clientPhoneNumbers: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
55
|
+
phoneNumber: import("@sinclair/typebox").TString;
|
|
56
|
+
phoneNumberType: import("@sinclair/typebox").TString;
|
|
57
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
58
58
|
}>>;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
clientEmailAddresses: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
60
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
61
|
+
emailAddress: import("@sinclair/typebox").TString;
|
|
62
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
63
63
|
}>>;
|
|
64
64
|
}>>;
|
|
65
65
|
}>>>;
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
policyPremiums: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
67
|
+
policyId: import("@sinclair/typebox").TNumber;
|
|
68
68
|
amount: import("@sinclair/typebox").TNumber;
|
|
69
69
|
date: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TDate, import("@sinclair/typebox").TString]>;
|
|
70
70
|
}>>>;
|
|
@@ -4,8 +4,8 @@ import { testPolicySchema } from "./test-policy.model.js";
|
|
|
4
4
|
import { entityUtils } from "@loomcore/common/utils";
|
|
5
5
|
import { Type } from "@sinclair/typebox";
|
|
6
6
|
export const testClientReportsSchema = Type.Object({
|
|
7
|
-
|
|
7
|
+
clientPerson: testPersonSchema,
|
|
8
8
|
agent: Type.Optional(testAgentSchema),
|
|
9
|
-
|
|
9
|
+
clientPolicies: Type.Optional(Type.Array(testPolicySchema))
|
|
10
10
|
});
|
|
11
11
|
export const testClientReportsModelSpec = entityUtils.getModelSpec(testClientReportsSchema, { isAuditable: true });
|
|
@@ -2,11 +2,11 @@ import type { IAuditable, IEntity } from "@loomcore/common/models";
|
|
|
2
2
|
import { ITestStateModel } from "./test-state.model.js";
|
|
3
3
|
export interface ITestDistrictModel extends IEntity, IAuditable {
|
|
4
4
|
name: string;
|
|
5
|
-
|
|
5
|
+
stateId: number;
|
|
6
6
|
state?: ITestStateModel;
|
|
7
7
|
}
|
|
8
8
|
export declare const testDistrictSchema: import("@sinclair/typebox").TObject<{
|
|
9
9
|
name: import("@sinclair/typebox").TString;
|
|
10
|
-
|
|
10
|
+
stateId: import("@sinclair/typebox").TNumber;
|
|
11
11
|
}>;
|
|
12
12
|
export declare const testDistrictModelSpec: import("@loomcore/common/models").IModelSpec<import("@sinclair/typebox").TSchema>;
|
|
@@ -2,6 +2,6 @@ import { entityUtils } from "@loomcore/common/utils";
|
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
export const testDistrictSchema = Type.Object({
|
|
4
4
|
name: Type.String(),
|
|
5
|
-
|
|
5
|
+
stateId: Type.Number(),
|
|
6
6
|
});
|
|
7
7
|
export const testDistrictModelSpec = entityUtils.getModelSpec(testDistrictSchema, { isAuditable: true });
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { IAuditable, IEntity } from "@loomcore/common/models";
|
|
2
2
|
export interface ITestEmailAddressModel extends IEntity, IAuditable {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
personId: number;
|
|
4
|
+
emailAddress: string;
|
|
5
|
+
isDefault: boolean;
|
|
6
6
|
}
|
|
7
7
|
export declare const testEmailAddressSchema: import("@sinclair/typebox").TObject<{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
9
|
+
emailAddress: import("@sinclair/typebox").TString;
|
|
10
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
11
11
|
}>;
|
|
12
12
|
export declare const testEmailAddressModelSpec: import("@loomcore/common/models").IModelSpec<import("@sinclair/typebox").TSchema>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { entityUtils } from "@loomcore/common/utils";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
export const testEmailAddressSchema = Type.Object({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
personId: Type.Number(),
|
|
5
|
+
emailAddress: Type.String(),
|
|
6
|
+
isDefault: Type.Boolean(),
|
|
7
7
|
});
|
|
8
8
|
export const testEmailAddressModelSpec = entityUtils.getModelSpec(testEmailAddressSchema);
|
|
@@ -3,26 +3,26 @@ import { ITestEmailAddressModel } from "./test-email-address.model.js";
|
|
|
3
3
|
import { ITestPhoneNumberModel } from "./test-phone-number.model.js";
|
|
4
4
|
import { ITestSchoolModel } from "./test-school.model.js";
|
|
5
5
|
export interface ITestPersonModel extends IEntity, IAuditable {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
firstName: string;
|
|
7
|
+
middleName: string | null;
|
|
8
|
+
lastName: string;
|
|
9
|
+
clientEmailAddresses: ITestEmailAddressModel[];
|
|
10
|
+
clientPhoneNumbers: ITestPhoneNumberModel[];
|
|
11
11
|
school?: ITestSchoolModel;
|
|
12
12
|
}
|
|
13
13
|
export declare const testPersonSchema: import("@sinclair/typebox").TObject<{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
firstName: import("@sinclair/typebox").TString;
|
|
15
|
+
middleName: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
16
|
+
lastName: import("@sinclair/typebox").TString;
|
|
17
|
+
clientPhoneNumbers: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
18
|
+
phoneNumber: import("@sinclair/typebox").TString;
|
|
19
|
+
phoneNumberType: import("@sinclair/typebox").TString;
|
|
20
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
21
21
|
}>>;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
clientEmailAddresses: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
23
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
24
|
+
emailAddress: import("@sinclair/typebox").TString;
|
|
25
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
26
26
|
}>>;
|
|
27
27
|
}>;
|
|
28
28
|
export declare const testPersonModelSpec: import("@loomcore/common/models").IModelSpec<import("@sinclair/typebox").TSchema>;
|
|
@@ -3,10 +3,10 @@ import { Type } from "@sinclair/typebox";
|
|
|
3
3
|
import { testEmailAddressSchema } from "./test-email-address.model.js";
|
|
4
4
|
import { testPhoneNumberSchema } from "./test-phone-number.model.js";
|
|
5
5
|
export const testPersonSchema = Type.Object({
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
firstName: Type.String(),
|
|
7
|
+
middleName: Type.Optional(Type.String()),
|
|
8
|
+
lastName: Type.String(),
|
|
9
|
+
clientPhoneNumbers: Type.Array(testPhoneNumberSchema),
|
|
10
|
+
clientEmailAddresses: Type.Array(testEmailAddressSchema),
|
|
11
11
|
});
|
|
12
12
|
export const testPersonModelSpec = entityUtils.getModelSpec(testPersonSchema, { isAuditable: true });
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { IAuditable, IEntity } from "@loomcore/common/models";
|
|
2
2
|
export interface ITestPhoneNumberModel extends IEntity, IAuditable {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
phoneNumber: string;
|
|
4
|
+
phoneNumberType: string;
|
|
5
|
+
isDefault: boolean;
|
|
6
6
|
}
|
|
7
7
|
export declare const testPhoneNumberSchema: import("@sinclair/typebox").TObject<{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
phoneNumber: import("@sinclair/typebox").TString;
|
|
9
|
+
phoneNumberType: import("@sinclair/typebox").TString;
|
|
10
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
11
11
|
}>;
|
|
12
12
|
export declare const testPhoneNumberModelSpec: import("@loomcore/common/models").IModelSpec<import("@sinclair/typebox").TSchema>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { entityUtils } from "@loomcore/common/utils";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
export const testPhoneNumberSchema = Type.Object({
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
phoneNumber: Type.String(),
|
|
5
|
+
phoneNumberType: Type.String(),
|
|
6
|
+
isDefault: Type.Boolean(),
|
|
7
7
|
});
|
|
8
8
|
export const testPhoneNumberModelSpec = entityUtils.getModelSpec(testPhoneNumberSchema, { isAuditable: true });
|
|
@@ -2,36 +2,36 @@ import type { IAuditable, IEntity } from "@loomcore/common/models";
|
|
|
2
2
|
import { ITestAgentModel } from "./test-agent.model.js";
|
|
3
3
|
import { ITestPremiumModel } from "./test-premium.model.js";
|
|
4
4
|
export interface ITestPolicyModel extends IEntity, IAuditable {
|
|
5
|
-
|
|
5
|
+
clientId: number;
|
|
6
6
|
amount: number;
|
|
7
7
|
frequency: string;
|
|
8
8
|
agents?: ITestAgentModel[];
|
|
9
|
-
|
|
9
|
+
policyPremiums?: ITestPremiumModel[];
|
|
10
10
|
}
|
|
11
11
|
export declare const testPolicySchema: import("@sinclair/typebox").TObject<{
|
|
12
|
-
|
|
12
|
+
clientId: import("@sinclair/typebox").TNumber;
|
|
13
13
|
amount: import("@sinclair/typebox").TNumber;
|
|
14
14
|
frequency: import("@sinclair/typebox").TString;
|
|
15
15
|
agents: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
17
|
+
agentPerson: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
|
|
18
|
+
firstName: import("@sinclair/typebox").TString;
|
|
19
|
+
middleName: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
20
|
+
lastName: import("@sinclair/typebox").TString;
|
|
21
|
+
clientPhoneNumbers: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
22
|
+
phoneNumber: import("@sinclair/typebox").TString;
|
|
23
|
+
phoneNumberType: import("@sinclair/typebox").TString;
|
|
24
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
25
25
|
}>>;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
clientEmailAddresses: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
27
|
+
personId: import("@sinclair/typebox").TNumber;
|
|
28
|
+
emailAddress: import("@sinclair/typebox").TString;
|
|
29
|
+
isDefault: import("@sinclair/typebox").TBoolean;
|
|
30
30
|
}>>;
|
|
31
31
|
}>>;
|
|
32
32
|
}>>>;
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
policyPremiums: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
|
|
34
|
+
policyId: import("@sinclair/typebox").TNumber;
|
|
35
35
|
amount: import("@sinclair/typebox").TNumber;
|
|
36
36
|
date: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TDate, import("@sinclair/typebox").TString]>;
|
|
37
37
|
}>>>;
|
|
@@ -3,10 +3,10 @@ import { Type } from "@sinclair/typebox";
|
|
|
3
3
|
import { testAgentSchema } from "./test-agent.model.js";
|
|
4
4
|
import { testPremiumSchema } from "./test-premium.model.js";
|
|
5
5
|
export const testPolicySchema = Type.Object({
|
|
6
|
-
|
|
6
|
+
clientId: Type.Number(),
|
|
7
7
|
amount: Type.Number(),
|
|
8
8
|
frequency: Type.String(),
|
|
9
9
|
agents: Type.Optional(Type.Array(testAgentSchema)),
|
|
10
|
-
|
|
10
|
+
policyPremiums: Type.Optional(Type.Array(testPremiumSchema))
|
|
11
11
|
});
|
|
12
12
|
export const testPolicyModelSpec = entityUtils.getModelSpec(testPolicySchema, { isAuditable: true });
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { IAuditable, IEntity } from "@loomcore/common/models";
|
|
2
2
|
export interface ITestPremiumModel extends IEntity, IAuditable {
|
|
3
|
-
|
|
3
|
+
policyId: number;
|
|
4
4
|
amount: number;
|
|
5
5
|
date: Date | string;
|
|
6
6
|
}
|
|
7
7
|
export declare const testPremiumSchema: import("@sinclair/typebox").TObject<{
|
|
8
|
-
|
|
8
|
+
policyId: import("@sinclair/typebox").TNumber;
|
|
9
9
|
amount: import("@sinclair/typebox").TNumber;
|
|
10
10
|
date: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TDate, import("@sinclair/typebox").TString]>;
|
|
11
11
|
}>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { entityUtils } from "@loomcore/common/utils";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
export const testPremiumSchema = Type.Object({
|
|
4
|
-
|
|
4
|
+
policyId: Type.Number(),
|
|
5
5
|
amount: Type.Number(),
|
|
6
6
|
date: Type.Union([Type.Date(), Type.String()])
|
|
7
7
|
});
|
|
@@ -2,11 +2,11 @@ import type { IAuditable, IEntity } from "@loomcore/common/models";
|
|
|
2
2
|
import { ITestDistrictModel } from "./test-district.model.js";
|
|
3
3
|
export interface ITestSchoolModel extends IEntity, IAuditable {
|
|
4
4
|
name: string;
|
|
5
|
-
|
|
5
|
+
districtId: number;
|
|
6
6
|
district?: ITestDistrictModel;
|
|
7
7
|
}
|
|
8
8
|
export declare const testSchoolSchema: import("@sinclair/typebox").TObject<{
|
|
9
9
|
name: import("@sinclair/typebox").TString;
|
|
10
|
-
|
|
10
|
+
districtId: import("@sinclair/typebox").TNumber;
|
|
11
11
|
}>;
|
|
12
12
|
export declare const testSchoolModelSpec: import("@loomcore/common/models").IModelSpec<import("@sinclair/typebox").TSchema>;
|
|
@@ -2,6 +2,6 @@ import { entityUtils } from "@loomcore/common/utils";
|
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
export const testSchoolSchema = Type.Object({
|
|
4
4
|
name: Type.String(),
|
|
5
|
-
|
|
5
|
+
districtId: Type.Number(),
|
|
6
6
|
});
|
|
7
7
|
export const testSchoolModelSpec = entityUtils.getModelSpec(testSchoolSchema, { isAuditable: true });
|
|
@@ -3,7 +3,10 @@ import { buildJoinClauses } from '../utils/build-join-clauses.js';
|
|
|
3
3
|
import { columnsAndValuesFromEntity } from '../utils/columns-and-values-from-entity.js';
|
|
4
4
|
export async function partialUpdateById(client, operations, id, entity, pluralResourceName) {
|
|
5
5
|
try {
|
|
6
|
+
console.log('entity', JSON.stringify(entity, null, 2));
|
|
6
7
|
const { columns, values } = columnsAndValuesFromEntity(entity);
|
|
8
|
+
console.log('columns', JSON.stringify(columns, null, 2));
|
|
9
|
+
console.log('values', JSON.stringify(values, null, 2));
|
|
7
10
|
const updateColumns = columns.filter(col => col !== '"_id"');
|
|
8
11
|
const updateValues = values.filter((_, index) => columns[index] !== '"_id"');
|
|
9
12
|
if (updateColumns.length === 0) {
|
|
@@ -25,8 +25,8 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
25
25
|
"code" VARCHAR(255) NOT NULL UNIQUE,
|
|
26
26
|
"description" TEXT,
|
|
27
27
|
"status" INTEGER NOT NULL,
|
|
28
|
-
"
|
|
29
|
-
"
|
|
28
|
+
"is_meta_org" BOOLEAN NOT NULL,
|
|
29
|
+
"auth_token" TEXT,
|
|
30
30
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
31
31
|
"_createdBy" INTEGER NOT NULL,
|
|
32
32
|
"_updated" TIMESTAMPTZ NOT NULL,
|
|
@@ -50,15 +50,15 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
50
50
|
CREATE TABLE IF NOT EXISTS "persons" (
|
|
51
51
|
"_id" INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
52
52
|
${orgColumnDef}
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
53
|
+
"external_id" VARCHAR(255) UNIQUE,
|
|
54
|
+
"first_name" VARCHAR(255) NOT NULL,
|
|
55
|
+
"middle_name" VARCHAR(255),
|
|
56
|
+
"last_name" VARCHAR(255) NOT NULL,
|
|
57
|
+
"date_of_birth" DATE,
|
|
58
|
+
"is_agent" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
59
|
+
"is_client" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
60
|
+
"is_employee" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
61
|
+
"extended_types" INTEGER,
|
|
62
62
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
63
63
|
"_createdBy" INTEGER NOT NULL,
|
|
64
64
|
"_updated" TIMESTAMPTZ NOT NULL,
|
|
@@ -81,16 +81,16 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
81
81
|
? 'CONSTRAINT "uk_users_email" UNIQUE ("_orgId", "email")'
|
|
82
82
|
: 'CONSTRAINT "uk_users_email" UNIQUE ("email")';
|
|
83
83
|
uniqueConstraint += `,
|
|
84
|
-
CONSTRAINT "
|
|
84
|
+
CONSTRAINT "fk_users_person_id" FOREIGN KEY("person_id") REFERENCES "persons"("_id") ON DELETE CASCADE`;
|
|
85
85
|
await pool.query(`
|
|
86
86
|
CREATE TABLE IF NOT EXISTS "users" (
|
|
87
87
|
"_id" INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
88
88
|
${orgColumnDef}
|
|
89
|
-
"
|
|
89
|
+
"external_id" VARCHAR(255) UNIQUE,
|
|
90
90
|
"email" VARCHAR(255) NOT NULL,
|
|
91
|
-
"
|
|
91
|
+
"display_name" VARCHAR(255),
|
|
92
92
|
"password" VARCHAR(255) NOT NULL,
|
|
93
|
-
"
|
|
93
|
+
"person_id" INTEGER UNIQUE,
|
|
94
94
|
"_lastLoggedIn" TIMESTAMPTZ,
|
|
95
95
|
"_lastPasswordChange" TIMESTAMPTZ,
|
|
96
96
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
@@ -117,12 +117,12 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
117
117
|
"_id" INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
118
118
|
${orgColumnDef}
|
|
119
119
|
"token" VARCHAR(255) NOT NULL,
|
|
120
|
-
"
|
|
121
|
-
"
|
|
122
|
-
"
|
|
120
|
+
"device_id" VARCHAR(255) NOT NULL,
|
|
121
|
+
"user_id" INTEGER NOT NULL,
|
|
122
|
+
"expires_on" BIGINT NOT NULL,
|
|
123
123
|
"created" TIMESTAMPTZ NOT NULL,
|
|
124
|
-
"
|
|
125
|
-
CONSTRAINT "fk_refresh_tokens_user" FOREIGN KEY ("
|
|
124
|
+
"created_by" INTEGER NOT NULL,
|
|
125
|
+
CONSTRAINT "fk_refresh_tokens_user" FOREIGN KEY ("user_id") REFERENCES "users"("_id") ON DELETE CASCADE
|
|
126
126
|
)
|
|
127
127
|
`);
|
|
128
128
|
},
|
|
@@ -144,7 +144,7 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
144
144
|
${orgColumnDef}
|
|
145
145
|
"email" VARCHAR(255) NOT NULL,
|
|
146
146
|
"token" VARCHAR(255) NOT NULL,
|
|
147
|
-
"
|
|
147
|
+
"expires_on" BIGINT NOT NULL,
|
|
148
148
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
149
149
|
"_createdBy" INTEGER NOT NULL,
|
|
150
150
|
"_updated" TIMESTAMPTZ NOT NULL,
|
|
@@ -156,7 +156,7 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
156
156
|
`);
|
|
157
157
|
},
|
|
158
158
|
down: async ({ context: pool }) => {
|
|
159
|
-
await pool.query('DROP TABLE IF EXISTS "
|
|
159
|
+
await pool.query('DROP TABLE IF EXISTS "password_reset_tokens"');
|
|
160
160
|
}
|
|
161
161
|
});
|
|
162
162
|
if (isAuthEnabled)
|
|
@@ -187,22 +187,22 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
187
187
|
up: async ({ context: pool }) => {
|
|
188
188
|
const orgColumnDef = isMultiTenant ? '"_orgId" INTEGER,' : '';
|
|
189
189
|
const uniqueConstraint = isMultiTenant
|
|
190
|
-
? 'CONSTRAINT "uk_user_roles" UNIQUE ("_orgId", "
|
|
191
|
-
: 'CONSTRAINT "uk_user_roles" UNIQUE ("
|
|
190
|
+
? 'CONSTRAINT "uk_user_roles" UNIQUE ("_orgId", "user_id", "role_id")'
|
|
191
|
+
: 'CONSTRAINT "uk_user_roles" UNIQUE ("user_id", "role_id")';
|
|
192
192
|
await pool.query(`
|
|
193
193
|
CREATE TABLE IF NOT EXISTS "user_roles" (
|
|
194
194
|
"_id" INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
195
195
|
${orgColumnDef}
|
|
196
|
-
"
|
|
197
|
-
"
|
|
196
|
+
"user_id" INTEGER NOT NULL,
|
|
197
|
+
"role_id" INTEGER NOT NULL,
|
|
198
198
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
199
199
|
"_createdBy" INTEGER NOT NULL,
|
|
200
200
|
"_updated" TIMESTAMPTZ NOT NULL,
|
|
201
201
|
"_updatedBy" INTEGER NOT NULL,
|
|
202
202
|
"_deleted" TIMESTAMPTZ,
|
|
203
203
|
"_deletedBy" INTEGER,
|
|
204
|
-
CONSTRAINT "fk_user_roles_user" FOREIGN KEY ("
|
|
205
|
-
CONSTRAINT "fk_user_roles_role" FOREIGN KEY ("
|
|
204
|
+
CONSTRAINT "fk_user_roles_user" FOREIGN KEY ("user_id") REFERENCES "users"("_id") ON DELETE CASCADE,
|
|
205
|
+
CONSTRAINT "fk_user_roles_role" FOREIGN KEY ("role_id") REFERENCES "roles"("_id") ON DELETE CASCADE,
|
|
206
206
|
${uniqueConstraint}
|
|
207
207
|
)
|
|
208
208
|
`);
|
|
@@ -239,16 +239,16 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
239
239
|
up: async ({ context: pool }) => {
|
|
240
240
|
const orgColumnDef = isMultiTenant ? '"_orgId" INTEGER,' : '';
|
|
241
241
|
const uniqueConstraint = isMultiTenant
|
|
242
|
-
? 'CONSTRAINT "uk_authorizations" UNIQUE ("_orgId", "
|
|
243
|
-
: 'CONSTRAINT "uk_authorizations" UNIQUE ("
|
|
242
|
+
? 'CONSTRAINT "uk_authorizations" UNIQUE ("_orgId", "role_id", "feature_id")'
|
|
243
|
+
: 'CONSTRAINT "uk_authorizations" UNIQUE ("role_id", "feature_id")';
|
|
244
244
|
await pool.query(`
|
|
245
245
|
CREATE TABLE IF NOT EXISTS "authorizations" (
|
|
246
246
|
"_id" INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
247
247
|
${orgColumnDef}
|
|
248
|
-
"
|
|
249
|
-
"
|
|
250
|
-
"
|
|
251
|
-
"
|
|
248
|
+
"role_id" INTEGER NOT NULL,
|
|
249
|
+
"feature_id" INTEGER NOT NULL,
|
|
250
|
+
"start_date" TIMESTAMPTZ,
|
|
251
|
+
"end_date" TIMESTAMPTZ,
|
|
252
252
|
"config" JSONB,
|
|
253
253
|
"_created" TIMESTAMPTZ NOT NULL,
|
|
254
254
|
"_createdBy" INTEGER NOT NULL,
|
|
@@ -256,8 +256,8 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
256
256
|
"_updatedBy" INTEGER NOT NULL,
|
|
257
257
|
"_deleted" TIMESTAMPTZ,
|
|
258
258
|
"_deletedBy" INTEGER,
|
|
259
|
-
CONSTRAINT "fk_authorizations_role" FOREIGN KEY ("
|
|
260
|
-
CONSTRAINT "fk_authorizations_feature" FOREIGN KEY ("
|
|
259
|
+
CONSTRAINT "fk_authorizations_role" FOREIGN KEY ("role_id") REFERENCES "roles"("_id") ON DELETE CASCADE,
|
|
260
|
+
CONSTRAINT "fk_authorizations_feature" FOREIGN KEY ("feature_id") REFERENCES "features"("_id") ON DELETE CASCADE,
|
|
261
261
|
${uniqueConstraint}
|
|
262
262
|
)
|
|
263
263
|
`);
|
|
@@ -271,9 +271,9 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
271
271
|
name: '00000000000010_data-meta-org',
|
|
272
272
|
up: async ({ context: pool }) => {
|
|
273
273
|
const result = await pool.query(`
|
|
274
|
-
INSERT INTO "organizations" ("name", "code", "status", "
|
|
274
|
+
INSERT INTO "organizations" ("name", "code", "status", "is_meta_org", "_created", "_createdBy", "_updated", "_updatedBy")
|
|
275
275
|
VALUES ($1, $2, 1, true, NOW(), 0, NOW(), 0)
|
|
276
|
-
RETURNING "_id", "name", "code", "status", "
|
|
276
|
+
RETURNING "_id", "name", "code", "status", "is_meta_org", "_created", "_createdBy", "_updated", "_updatedBy"
|
|
277
277
|
`, [config.multiTenant?.metaOrgName, config.multiTenant?.metaOrgCode]);
|
|
278
278
|
if (result.rowCount === 0) {
|
|
279
279
|
throw new Error('Failed to create meta organization');
|
|
@@ -281,7 +281,7 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
281
281
|
initializeSystemUserContext(config.email?.systemEmailAddress || 'system@example.com', result.rows[0]);
|
|
282
282
|
},
|
|
283
283
|
down: async ({ context: pool }) => {
|
|
284
|
-
await pool.query(`DELETE FROM "organizations" WHERE "
|
|
284
|
+
await pool.query(`DELETE FROM "organizations" WHERE "is_meta_org" = TRUE`);
|
|
285
285
|
}
|
|
286
286
|
});
|
|
287
287
|
}
|
|
@@ -367,11 +367,11 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
367
367
|
const roleId = roleResult.rows[0]._id;
|
|
368
368
|
const userRoleResult = isMultiTenant
|
|
369
369
|
? await client.query(`
|
|
370
|
-
INSERT INTO "user_roles" ("_orgId", "
|
|
370
|
+
INSERT INTO "user_roles" ("_orgId", "user_id", "role_id", "_created", "_createdBy", "_updated", "_updatedBy")
|
|
371
371
|
VALUES ($1, $2, $3, NOW(), 0, NOW(), 0)
|
|
372
372
|
`, [metaOrg._id, adminUser._id, roleId])
|
|
373
373
|
: await client.query(`
|
|
374
|
-
INSERT INTO "user_roles" ("
|
|
374
|
+
INSERT INTO "user_roles" ("user_id", "role_id", "_created", "_createdBy", "_updated", "_updatedBy")
|
|
375
375
|
VALUES ($1, $2, NOW(), 0, NOW(), 0)
|
|
376
376
|
`, [adminUser._id, roleId]);
|
|
377
377
|
if (userRoleResult.rowCount === 0) {
|
|
@@ -395,14 +395,14 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
395
395
|
const authorizationResult = isMultiTenant
|
|
396
396
|
? await client.query(`
|
|
397
397
|
INSERT INTO "authorizations" (
|
|
398
|
-
"_orgId", "
|
|
398
|
+
"_orgId", "role_id", "feature_id",
|
|
399
399
|
"_created", "_createdBy", "_updated", "_updatedBy"
|
|
400
400
|
)
|
|
401
401
|
VALUES ($1, $2, $3, NOW(), 0, NOW(), 0)
|
|
402
402
|
`, [metaOrg._id, roleId, featureId])
|
|
403
403
|
: await client.query(`
|
|
404
404
|
INSERT INTO "authorizations" (
|
|
405
|
-
"
|
|
405
|
+
"role_id", "feature_id",
|
|
406
406
|
"_created", "_createdBy", "_updated", "_updatedBy"
|
|
407
407
|
)
|
|
408
408
|
VALUES ($1, $2, NOW(), 0, NOW(), 0)
|
|
@@ -435,11 +435,11 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
435
435
|
await client.query(`
|
|
436
436
|
DELETE FROM "authorizations"
|
|
437
437
|
WHERE "_orgId" = $1
|
|
438
|
-
AND "
|
|
438
|
+
AND "feature_id" IN (
|
|
439
439
|
SELECT "_id" FROM "features"
|
|
440
440
|
WHERE "_orgId" = $1 AND "name" = 'admin'
|
|
441
441
|
)
|
|
442
|
-
AND "
|
|
442
|
+
AND "role_id" IN (
|
|
443
443
|
SELECT "_id" FROM "roles"
|
|
444
444
|
WHERE "_orgId" = $1 AND "name" = 'admin'
|
|
445
445
|
)
|
|
@@ -451,7 +451,7 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
451
451
|
await client.query(`
|
|
452
452
|
DELETE FROM "user_roles"
|
|
453
453
|
WHERE "_orgId" = $1
|
|
454
|
-
AND "
|
|
454
|
+
AND "role_id" IN (
|
|
455
455
|
SELECT "_id" FROM "roles"
|
|
456
456
|
WHERE "_orgId" = $1 AND "name" = 'admin'
|
|
457
457
|
)
|
|
@@ -464,11 +464,11 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
464
464
|
else {
|
|
465
465
|
await client.query(`
|
|
466
466
|
DELETE FROM "authorizations"
|
|
467
|
-
WHERE "
|
|
467
|
+
WHERE "feature_id" IN (
|
|
468
468
|
SELECT "_id" FROM "features"
|
|
469
469
|
WHERE "name" = 'admin'
|
|
470
470
|
)
|
|
471
|
-
AND "
|
|
471
|
+
AND "role_id" IN (
|
|
472
472
|
SELECT "_id" FROM "roles"
|
|
473
473
|
WHERE "name" = 'admin'
|
|
474
474
|
)
|
|
@@ -479,7 +479,7 @@ export const getPostgresInitialSchema = (config) => {
|
|
|
479
479
|
`);
|
|
480
480
|
await client.query(`
|
|
481
481
|
DELETE FROM "user_roles"
|
|
482
|
-
WHERE "
|
|
482
|
+
WHERE "role_id" IN (
|
|
483
483
|
SELECT "_id" FROM "roles"
|
|
484
484
|
WHERE "name" = 'admin'
|
|
485
485
|
)
|
|
@@ -13,16 +13,18 @@ import { get as getQuery } from "./queries/postgres-get.query.js";
|
|
|
13
13
|
import { getById as getByIdQuery } from "./queries/postgres-get-by-id.query.js";
|
|
14
14
|
import { getCount as getCountQuery } from "./queries/postgres-get-count.query.js";
|
|
15
15
|
import { convertNullToUndefined } from "./utils/convert-null-to-undefined.util.js";
|
|
16
|
+
import { convertKeysToSnakeCase, convertKeysToCamelCase } from "./utils/convert-keys.util.js";
|
|
16
17
|
export class PostgresDatabase {
|
|
17
18
|
client;
|
|
18
19
|
constructor(client) {
|
|
19
20
|
this.client = client;
|
|
20
21
|
}
|
|
21
22
|
preProcessEntity(entity, modelSpec) {
|
|
22
|
-
return entity;
|
|
23
|
+
return convertKeysToSnakeCase(entity);
|
|
23
24
|
}
|
|
24
25
|
postProcessEntity(entity, modelSpec) {
|
|
25
|
-
|
|
26
|
+
const withNullsConverted = convertNullToUndefined(entity, modelSpec);
|
|
27
|
+
return convertKeysToCamelCase(withNullsConverted);
|
|
26
28
|
}
|
|
27
29
|
async getAll(operations, pluralResourceName) {
|
|
28
30
|
return getAllQuery(this.client, operations, pluralResourceName);
|
|
@@ -70,21 +72,21 @@ export class PostgresDatabase {
|
|
|
70
72
|
const now = new Date();
|
|
71
73
|
let query = `
|
|
72
74
|
SELECT DISTINCT
|
|
73
|
-
ur."
|
|
75
|
+
ur."user_id" as "userId",
|
|
74
76
|
r."name" as "role",
|
|
75
77
|
f."name" as "feature",
|
|
76
78
|
a."config",
|
|
77
79
|
a."_id",
|
|
78
80
|
a."_orgId"
|
|
79
81
|
FROM "user_roles" ur
|
|
80
|
-
INNER JOIN "roles" r ON ur."
|
|
81
|
-
INNER JOIN "authorizations" a ON r."_id" = a."
|
|
82
|
-
INNER JOIN "features" f ON a."
|
|
83
|
-
WHERE ur."
|
|
82
|
+
INNER JOIN "roles" r ON ur."role_id" = r."_id"
|
|
83
|
+
INNER JOIN "authorizations" a ON r."_id" = a."role_id"
|
|
84
|
+
INNER JOIN "features" f ON a."feature_id" = f."_id"
|
|
85
|
+
WHERE ur."user_id" = $1
|
|
84
86
|
AND ur."_deleted" IS NULL
|
|
85
87
|
AND a."_deleted" IS NULL
|
|
86
|
-
AND (a."
|
|
87
|
-
AND (a."
|
|
88
|
+
AND (a."start_date" IS NULL OR a."start_date" <= $2)
|
|
89
|
+
AND (a."end_date" IS NULL OR a."end_date" >= $2)
|
|
88
90
|
`;
|
|
89
91
|
const values = [userId, now];
|
|
90
92
|
if (orgId) {
|
|
@@ -2,11 +2,24 @@ import { Join } from "../../operations/join.operation.js";
|
|
|
2
2
|
import { JoinMany } from "../../operations/join-many.operation.js";
|
|
3
3
|
import { JoinThrough } from "../../operations/join-through.operation.js";
|
|
4
4
|
import { JoinThroughMany } from "../../operations/join-through-many.operation.js";
|
|
5
|
+
import { toSnakeCase } from "./convert-keys.util.js";
|
|
6
|
+
function convertFieldToSnakeCase(field) {
|
|
7
|
+
if (field.startsWith('_')) {
|
|
8
|
+
return field;
|
|
9
|
+
}
|
|
10
|
+
else if (field === 'id') {
|
|
11
|
+
return '_id';
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
return toSnakeCase(field);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
5
17
|
function resolveLocalField(localField, mainTableName, operations) {
|
|
6
18
|
if (!localField.includes('.')) {
|
|
19
|
+
const snakeField = convertFieldToSnakeCase(localField);
|
|
7
20
|
return mainTableName
|
|
8
|
-
? `"${mainTableName}"."${
|
|
9
|
-
: `"${
|
|
21
|
+
? `"${mainTableName}"."${snakeField}"`
|
|
22
|
+
: `"${snakeField}"`;
|
|
10
23
|
}
|
|
11
24
|
const [alias, field] = localField.split('.');
|
|
12
25
|
const objectJoin = operations.find(op => op instanceof JoinThrough && op.as === alias);
|
|
@@ -17,7 +30,8 @@ function resolveLocalField(localField, mainTableName, operations) {
|
|
|
17
30
|
if (arrayJoin) {
|
|
18
31
|
return `(${alias}.aggregated->>'${field}')::integer`;
|
|
19
32
|
}
|
|
20
|
-
|
|
33
|
+
const snakeField = convertFieldToSnakeCase(field);
|
|
34
|
+
return `${alias}."${snakeField}"`;
|
|
21
35
|
}
|
|
22
36
|
function findEnrichmentTarget(operation, operations) {
|
|
23
37
|
if (!operation.localField.includes('.')) {
|
|
@@ -76,7 +90,10 @@ export function buildJoinClauses(operations, mainTableName) {
|
|
|
76
90
|
const { target, field } = enrichment;
|
|
77
91
|
const targetLocalFieldRef = target.localField.includes('.')
|
|
78
92
|
? resolveLocalField(target.localField, mainTableName, operations)
|
|
79
|
-
: (
|
|
93
|
+
: (() => {
|
|
94
|
+
const snakeField = convertFieldToSnakeCase(target.localField);
|
|
95
|
+
return mainTableName ? `"${mainTableName}"."${snakeField}"` : `"${snakeField}"`;
|
|
96
|
+
})();
|
|
80
97
|
let enrichmentJoins = '';
|
|
81
98
|
let jsonBuildObjects = '';
|
|
82
99
|
for (const enrich of enrichments) {
|
|
@@ -188,7 +205,10 @@ export function buildJoinClauses(operations, mainTableName) {
|
|
|
188
205
|
const { target, field } = enrichment;
|
|
189
206
|
const targetLocalFieldRef = target.localField.includes('.')
|
|
190
207
|
? resolveLocalField(target.localField, mainTableName, operations)
|
|
191
|
-
: (
|
|
208
|
+
: (() => {
|
|
209
|
+
const snakeField = convertFieldToSnakeCase(target.localField);
|
|
210
|
+
return mainTableName ? `"${mainTableName}"."${snakeField}"` : `"${snakeField}"`;
|
|
211
|
+
})();
|
|
192
212
|
const isTargetJoinMany = target instanceof JoinMany;
|
|
193
213
|
let enrichmentJoins = '';
|
|
194
214
|
let jsonBuildObjects = '';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { toSnakeCase } from "./convert-keys.util.js";
|
|
1
2
|
export function buildWhereClause(queryObject, values = [], tablePrefix) {
|
|
2
3
|
const filters = queryObject.filters || {};
|
|
3
4
|
const conditions = [];
|
|
@@ -7,7 +8,17 @@ export function buildWhereClause(queryObject, values = [], tablePrefix) {
|
|
|
7
8
|
};
|
|
8
9
|
for (const [key, value] of Object.entries(filters)) {
|
|
9
10
|
if (value) {
|
|
10
|
-
|
|
11
|
+
let snakeKey;
|
|
12
|
+
if (key.startsWith('_')) {
|
|
13
|
+
snakeKey = key;
|
|
14
|
+
}
|
|
15
|
+
else if (key === 'id') {
|
|
16
|
+
snakeKey = '_id';
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
snakeKey = toSnakeCase(key);
|
|
20
|
+
}
|
|
21
|
+
const qualifiedKey = qualifyColumn(snakeKey);
|
|
11
22
|
if (value.eq !== undefined) {
|
|
12
23
|
conditions.push(`${qualifiedKey} = $${paramIndex}`);
|
|
13
24
|
values.push(value.eq);
|
|
@@ -2,8 +2,10 @@ export function columnsAndValuesFromEntity(entity) {
|
|
|
2
2
|
const columns = [];
|
|
3
3
|
const values = [];
|
|
4
4
|
for (const [key, value] of Object.entries(entity)) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
if (value !== undefined) {
|
|
6
|
+
columns.push(`"${key}"`);
|
|
7
|
+
values.push(value);
|
|
8
|
+
}
|
|
7
9
|
}
|
|
8
10
|
return { columns, values };
|
|
9
11
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
export function toSnakeCase(str) {
|
|
3
|
+
let result = str.replace(/([a-z])([A-Z])/g, '$1_$2');
|
|
4
|
+
result = result.toLowerCase()
|
|
5
|
+
.replace(/[\s-]/g, '_');
|
|
6
|
+
result = result.replace(/_+/g, '_')
|
|
7
|
+
.replace(/^_|_$/g, '');
|
|
8
|
+
return result;
|
|
9
|
+
}
|
|
10
|
+
export function toCamelCase(str) {
|
|
11
|
+
return str.split(/[-_]+/)
|
|
12
|
+
.map((word, index) => {
|
|
13
|
+
if (index === 0) {
|
|
14
|
+
return word.toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
17
|
+
})
|
|
18
|
+
.join('');
|
|
19
|
+
}
|
|
20
|
+
export function convertKeysToSnakeCase(obj) {
|
|
21
|
+
if (!obj)
|
|
22
|
+
return obj;
|
|
23
|
+
if (Array.isArray(obj)) {
|
|
24
|
+
return obj.map(item => convertKeysToSnakeCase(item));
|
|
25
|
+
}
|
|
26
|
+
if (obj instanceof Date) {
|
|
27
|
+
return obj;
|
|
28
|
+
}
|
|
29
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
30
|
+
return obj;
|
|
31
|
+
}
|
|
32
|
+
const clone = _.cloneDeep(obj);
|
|
33
|
+
const result = {};
|
|
34
|
+
for (const [key, value] of Object.entries(clone)) {
|
|
35
|
+
const snakeKey = key.startsWith('_') ? key : toSnakeCase(key);
|
|
36
|
+
if (value !== null && value !== undefined) {
|
|
37
|
+
if (Array.isArray(value)) {
|
|
38
|
+
result[snakeKey] = value.map(item => convertKeysToSnakeCase(item));
|
|
39
|
+
}
|
|
40
|
+
else if (typeof value === 'object' && !(value instanceof Date)) {
|
|
41
|
+
result[snakeKey] = convertKeysToSnakeCase(value);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
result[snakeKey] = value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
result[snakeKey] = value;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
export function convertKeysToCamelCase(obj) {
|
|
54
|
+
if (!obj)
|
|
55
|
+
return obj;
|
|
56
|
+
if (Array.isArray(obj)) {
|
|
57
|
+
return obj.map(item => convertKeysToCamelCase(item));
|
|
58
|
+
}
|
|
59
|
+
if (obj instanceof Date) {
|
|
60
|
+
return obj;
|
|
61
|
+
}
|
|
62
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
63
|
+
return obj;
|
|
64
|
+
}
|
|
65
|
+
const clone = _.cloneDeep(obj);
|
|
66
|
+
const result = {};
|
|
67
|
+
for (const [key, value] of Object.entries(clone)) {
|
|
68
|
+
const camelKey = key.startsWith('_') ? key : toCamelCase(key);
|
|
69
|
+
if (value !== null && value !== undefined) {
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
result[camelKey] = value.map(item => convertKeysToCamelCase(item));
|
|
72
|
+
}
|
|
73
|
+
else if (typeof value === 'object' && !(value instanceof Date)) {
|
|
74
|
+
result[camelKey] = convertKeysToCamelCase(value);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
result[camelKey] = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
result[camelKey] = value;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
+
import { toCamelCase } from './convert-keys.util.js';
|
|
2
3
|
function isPropertyOptional(key, schema) {
|
|
3
4
|
if (!schema || typeof schema !== 'object')
|
|
4
5
|
return false;
|
|
@@ -36,12 +37,13 @@ export function convertNullToUndefined(data, schema) {
|
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
39
|
if (subSchema.type === 'object' && subSchema.properties) {
|
|
39
|
-
for (const [key,
|
|
40
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
41
|
+
const schemaKey = key.startsWith('_') ? key : toCamelCase(key);
|
|
42
|
+
const propSchema = subSchema.properties[schemaKey];
|
|
40
43
|
if (!propSchema || typeof propSchema !== 'object')
|
|
41
44
|
continue;
|
|
42
45
|
const typedPropSchema = propSchema;
|
|
43
|
-
|
|
44
|
-
if (isPropertyOptional(key, subSchema)) {
|
|
46
|
+
if (isPropertyOptional(schemaKey, subSchema)) {
|
|
45
47
|
if (value === null) {
|
|
46
48
|
delete obj[key];
|
|
47
49
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { UserSpec, PublicUserSchema } from '@loomcore/common/models';
|
|
1
|
+
import { UserSpec } from '@loomcore/common/models';
|
|
3
2
|
import { MultiTenantApiService } from './index.js';
|
|
4
3
|
import { ServerError } from '../errors/index.js';
|
|
5
4
|
export class UserService extends MultiTenantApiService {
|
|
@@ -15,7 +14,8 @@ export class UserService extends MultiTenantApiService {
|
|
|
15
14
|
preparedEntity.email = preparedEntity.email.toLowerCase();
|
|
16
15
|
}
|
|
17
16
|
if (!isCreate) {
|
|
18
|
-
|
|
17
|
+
const { password, ...cleanedEntity } = preparedEntity;
|
|
18
|
+
return cleanedEntity;
|
|
19
19
|
}
|
|
20
20
|
return preparedEntity;
|
|
21
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loomcore/api",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.87",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Loom Core Api - An opinionated Node.js api using Typescript, Express, and MongoDb or PostgreSQL",
|
|
6
6
|
"scripts": {
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"qs": "^6.14.1"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"@loomcore/common": "^0.0.
|
|
60
|
+
"@loomcore/common": "^0.0.50",
|
|
61
61
|
"@sinclair/typebox": "0.34.33",
|
|
62
62
|
"cookie-parser": "^1.4.6",
|
|
63
63
|
"cors": "^2.8.5",
|