@friggframework/core 2.0.0-next.45 → 2.0.0-next.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/application/commands/integration-commands.js +19 -0
- package/core/Worker.js +8 -21
- package/credential/repositories/credential-repository-mongo.js +14 -8
- package/credential/repositories/credential-repository-postgres.js +14 -8
- package/credential/repositories/credential-repository.js +3 -8
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +11 -2
- package/database/models/WebsocketConnection.js +11 -10
- package/database/prisma.js +63 -3
- package/database/repositories/health-check-repository-mongodb.js +3 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +3 -2
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/run-database-migration-use-case.js +137 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +91 -0
- package/database/utils/mongodb-schema-init.js +106 -0
- package/database/utils/prisma-runner.js +400 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/encrypt/Cryptor.js +14 -16
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +4 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +4 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +334 -0
- package/generated/prisma-mongodb/index-browser.js +316 -0
- package/generated/prisma-mongodb/index.d.ts +22897 -0
- package/generated/prisma-mongodb/index.js +359 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
- package/generated/prisma-mongodb/runtime/binary.js +289 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
- package/generated/prisma-mongodb/runtime/edge.js +34 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
- package/generated/prisma-mongodb/runtime/react-native.js +83 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-mongodb/schema.prisma +362 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +341 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +4 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +4 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +356 -0
- package/generated/prisma-postgresql/index-browser.js +338 -0
- package/generated/prisma-postgresql/index.d.ts +25071 -0
- package/generated/prisma-postgresql/index.js +381 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
- package/generated/prisma-postgresql/runtime/binary.js +289 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
- package/generated/prisma-postgresql/runtime/edge.js +34 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
- package/generated/prisma-postgresql/runtime/react-native.js +83 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-postgresql/schema.prisma +345 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +363 -0
- package/handlers/database-migration-handler.js +227 -0
- package/handlers/routers/auth.js +1 -1
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +256 -0
- package/handlers/routers/health.js +41 -6
- package/handlers/routers/integration-webhook-routers.js +2 -2
- package/handlers/use-cases/check-integrations-health-use-case.js +22 -10
- package/handlers/workers/db-migration.js +352 -0
- package/index.js +12 -0
- package/integrations/integration-router.js +60 -70
- package/integrations/repositories/integration-repository-interface.js +12 -0
- package/integrations/repositories/integration-repository-mongo.js +32 -0
- package/integrations/repositories/integration-repository-postgres.js +33 -0
- package/integrations/repositories/process-repository-postgres.js +2 -2
- package/integrations/tests/doubles/test-integration-repository.js +2 -2
- package/logs/logger.js +0 -4
- package/modules/entity.js +0 -1
- package/modules/repositories/module-repository-mongo.js +3 -12
- package/modules/repositories/module-repository-postgres.js +0 -11
- package/modules/repositories/module-repository.js +1 -12
- package/modules/use-cases/get-entity-options-by-id.js +1 -1
- package/modules/use-cases/get-module.js +1 -2
- package/modules/use-cases/refresh-entity-options.js +1 -1
- package/modules/use-cases/test-module-auth.js +1 -1
- package/package.json +82 -66
- package/prisma-mongodb/schema.prisma +21 -21
- package/prisma-postgresql/schema.prisma +15 -15
- package/queues/queuer-util.js +24 -21
- package/types/core/index.d.ts +2 -2
- package/types/module-plugin/index.d.ts +0 -2
- package/user/use-cases/authenticate-user.js +127 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +106 -0
- package/user/user.js +16 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +11 -10
- package/websocket/repositories/websocket-connection-repository-postgres.js +11 -10
- package/websocket/repositories/websocket-connection-repository.js +11 -10
- package/application/commands/integration-commands.test.js +0 -123
- package/database/encryption/encryption-integration.test.js +0 -553
- package/database/encryption/encryption-schema-registry.test.js +0 -392
- package/database/encryption/field-encryption-service.test.js +0 -525
- package/database/encryption/mongo-decryption-fix-verification.test.js +0 -348
- package/database/encryption/postgres-decryption-fix-verification.test.js +0 -371
- package/database/encryption/postgres-relation-decryption.test.js +0 -245
- package/database/encryption/prisma-encryption-extension.test.js +0 -439
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- package/handlers/auth-flow.integration.test.js +0 -147
- package/handlers/integration-event-dispatcher.test.js +0 -209
- package/handlers/routers/health.test.js +0 -210
- package/handlers/routers/integration-webhook-routers.test.js +0 -126
- package/handlers/webhook-flow.integration.test.js +0 -356
- package/handlers/workers/integration-defined-workers.test.js +0 -184
- package/integrations/tests/use-cases/create-integration.test.js +0 -131
- package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -150
- package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +0 -92
- package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -150
- package/integrations/tests/use-cases/get-integration-instance.test.js +0 -176
- package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -176
- package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
- package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
- package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
- package/integrations/tests/use-cases/update-integration.test.js +0 -141
- package/integrations/use-cases/create-process.test.js +0 -178
- package/integrations/use-cases/get-process.test.js +0 -190
- package/integrations/use-cases/load-integration-context-full.test.js +0 -329
- package/integrations/use-cases/load-integration-context.test.js +0 -114
- package/integrations/use-cases/update-process-metrics.test.js +0 -308
- package/integrations/use-cases/update-process-state.test.js +0 -256
- package/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- package/modules/module-hydration.test.js +0 -205
- package/modules/requester/requester.test.js +0 -28
- package/user/tests/use-cases/create-individual-user.test.js +0 -24
- package/user/tests/use-cases/create-organization-user.test.js +0 -28
- package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
- package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
- package/user/tests/use-cases/login-user.test.js +0 -220
- package/user/tests/user-password-encryption-isolation.test.js +0 -237
- package/user/tests/user-password-hashing.test.js +0 -235
|
@@ -1,439 +0,0 @@
|
|
|
1
|
-
const { createEncryptionExtension } = require('./prisma-encryption-extension');
|
|
2
|
-
|
|
3
|
-
describe('Prisma Encryption Extension', () => {
|
|
4
|
-
let mockCryptor;
|
|
5
|
-
let mockQuery;
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
// Mock Cryptor
|
|
9
|
-
mockCryptor = {
|
|
10
|
-
encrypt: jest
|
|
11
|
-
.fn()
|
|
12
|
-
.mockImplementation(
|
|
13
|
-
(value) => `encrypted:${value}:iv:enckey`
|
|
14
|
-
),
|
|
15
|
-
decrypt: jest
|
|
16
|
-
.fn()
|
|
17
|
-
.mockImplementation((value) => {
|
|
18
|
-
const parts = value.split(':');
|
|
19
|
-
return parts[1]; // Extract original value
|
|
20
|
-
}),
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
// Mock Prisma query function
|
|
24
|
-
mockQuery = jest.fn().mockImplementation((args) => args.mockResult);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('createEncryptionExtension', () => {
|
|
28
|
-
it('should create extension with valid config', () => {
|
|
29
|
-
const extension = createEncryptionExtension({
|
|
30
|
-
cryptor: mockCryptor,
|
|
31
|
-
enabled: true,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
expect(extension).toBeDefined();
|
|
35
|
-
expect(extension.name).toBe('frigg-field-encryption');
|
|
36
|
-
expect(extension.query).toBeDefined();
|
|
37
|
-
expect(extension.query.$allModels).toBeDefined();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should return no-op extension when disabled', () => {
|
|
41
|
-
const extension = createEncryptionExtension({
|
|
42
|
-
cryptor: mockCryptor,
|
|
43
|
-
enabled: false,
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// No-op extension is a function that returns its input
|
|
47
|
-
const mockClient = { $extends: jest.fn() };
|
|
48
|
-
const result = extension(mockClient);
|
|
49
|
-
|
|
50
|
-
expect(result).toBe(mockClient);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should throw if cryptor not provided when enabled', () => {
|
|
54
|
-
expect(() => {
|
|
55
|
-
createEncryptionExtension({ enabled: true });
|
|
56
|
-
}).toThrow('Cryptor instance required');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should not throw if cryptor not provided when disabled', () => {
|
|
60
|
-
expect(() => {
|
|
61
|
-
createEncryptionExtension({ enabled: false });
|
|
62
|
-
}).not.toThrow();
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('Query Interceptors', () => {
|
|
67
|
-
let extension;
|
|
68
|
-
let handlers;
|
|
69
|
-
|
|
70
|
-
beforeEach(() => {
|
|
71
|
-
extension = createEncryptionExtension({
|
|
72
|
-
cryptor: mockCryptor,
|
|
73
|
-
enabled: true,
|
|
74
|
-
});
|
|
75
|
-
handlers = extension.query.$allModels;
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('create', () => {
|
|
79
|
-
it('should encrypt data before create', async () => {
|
|
80
|
-
const args = {
|
|
81
|
-
data: {
|
|
82
|
-
data: { access_token: 'secret123' },
|
|
83
|
-
},
|
|
84
|
-
mockResult: {
|
|
85
|
-
id: '1',
|
|
86
|
-
data: { access_token: 'encrypted:secret123:iv:enckey' },
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
await handlers.create({
|
|
91
|
-
model: 'Credential',
|
|
92
|
-
operation: 'create',
|
|
93
|
-
args,
|
|
94
|
-
query: mockQuery,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('secret123');
|
|
98
|
-
expect(args.data.data.access_token).toBe(
|
|
99
|
-
'encrypted:secret123:iv:enckey'
|
|
100
|
-
);
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should decrypt result after create', async () => {
|
|
104
|
-
const args = {
|
|
105
|
-
data: { data: { access_token: 'secret123' } },
|
|
106
|
-
mockResult: {
|
|
107
|
-
id: '1',
|
|
108
|
-
data: { access_token: 'encrypted:secret123:iv:enckey' },
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const result = await handlers.create({
|
|
113
|
-
model: 'Credential',
|
|
114
|
-
operation: 'create',
|
|
115
|
-
args,
|
|
116
|
-
query: mockQuery,
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
expect(mockCryptor.decrypt).toHaveBeenCalledWith(
|
|
120
|
-
'encrypted:secret123:iv:enckey'
|
|
121
|
-
);
|
|
122
|
-
expect(result.data.access_token).toBe('secret123');
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it('should handle null result', async () => {
|
|
126
|
-
const args = {
|
|
127
|
-
data: { data: { access_token: 'secret123' } },
|
|
128
|
-
mockResult: null,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const result = await handlers.create({
|
|
132
|
-
model: 'Credential',
|
|
133
|
-
operation: 'create',
|
|
134
|
-
args,
|
|
135
|
-
query: mockQuery,
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
expect(result).toBeNull();
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
describe('createMany', () => {
|
|
143
|
-
it('should encrypt array of data', async () => {
|
|
144
|
-
const args = {
|
|
145
|
-
data: [
|
|
146
|
-
{ data: { access_token: 'secret1' } },
|
|
147
|
-
{ data: { access_token: 'secret2' } },
|
|
148
|
-
],
|
|
149
|
-
mockResult: { count: 2 },
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
await handlers.createMany({
|
|
153
|
-
model: 'Credential',
|
|
154
|
-
operation: 'createMany',
|
|
155
|
-
args,
|
|
156
|
-
query: mockQuery,
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('secret1');
|
|
160
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('secret2');
|
|
161
|
-
expect(args.data[0].data.access_token).toBe(
|
|
162
|
-
'encrypted:secret1:iv:enckey'
|
|
163
|
-
);
|
|
164
|
-
expect(args.data[1].data.access_token).toBe(
|
|
165
|
-
'encrypted:secret2:iv:enckey'
|
|
166
|
-
);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('should handle single object in createMany', async () => {
|
|
170
|
-
const args = {
|
|
171
|
-
data: { data: { access_token: 'secret' } },
|
|
172
|
-
mockResult: { count: 1 },
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
await handlers.createMany({
|
|
176
|
-
model: 'Credential',
|
|
177
|
-
operation: 'createMany',
|
|
178
|
-
args,
|
|
179
|
-
query: mockQuery,
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('secret');
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
describe('update', () => {
|
|
187
|
-
it('should encrypt update data', async () => {
|
|
188
|
-
const args = {
|
|
189
|
-
data: { data: { access_token: 'newsecret' } },
|
|
190
|
-
mockResult: {
|
|
191
|
-
id: '1',
|
|
192
|
-
data: { access_token: 'encrypted:newsecret:iv:enckey' },
|
|
193
|
-
},
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
await handlers.update({
|
|
197
|
-
model: 'Credential',
|
|
198
|
-
operation: 'update',
|
|
199
|
-
args,
|
|
200
|
-
query: mockQuery,
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('newsecret');
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('should decrypt result after update', async () => {
|
|
207
|
-
const args = {
|
|
208
|
-
data: { data: { access_token: 'newsecret' } },
|
|
209
|
-
mockResult: {
|
|
210
|
-
id: '1',
|
|
211
|
-
data: { access_token: 'encrypted:newsecret:iv:enckey' },
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const result = await handlers.update({
|
|
216
|
-
model: 'Credential',
|
|
217
|
-
operation: 'update',
|
|
218
|
-
args,
|
|
219
|
-
query: mockQuery,
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
expect(result.data.access_token).toBe('newsecret');
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
describe('upsert', () => {
|
|
227
|
-
it('should encrypt both create and update data', async () => {
|
|
228
|
-
const args = {
|
|
229
|
-
create: { data: { access_token: 'createsecret' } },
|
|
230
|
-
update: { data: { access_token: 'updatesecret' } },
|
|
231
|
-
mockResult: {
|
|
232
|
-
id: '1',
|
|
233
|
-
data: { access_token: 'encrypted:createsecret:iv:enckey' },
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
await handlers.upsert({
|
|
238
|
-
model: 'Credential',
|
|
239
|
-
operation: 'upsert',
|
|
240
|
-
args,
|
|
241
|
-
query: mockQuery,
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('createsecret');
|
|
245
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('updatesecret');
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
describe('findUnique', () => {
|
|
250
|
-
it('should decrypt result', async () => {
|
|
251
|
-
const args = {
|
|
252
|
-
where: { id: '1' },
|
|
253
|
-
mockResult: {
|
|
254
|
-
id: '1',
|
|
255
|
-
data: { access_token: 'encrypted:secret:iv:enckey' },
|
|
256
|
-
},
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
const result = await handlers.findUnique({
|
|
260
|
-
model: 'Credential',
|
|
261
|
-
operation: 'findUnique',
|
|
262
|
-
args,
|
|
263
|
-
query: mockQuery,
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
expect(mockCryptor.decrypt).toHaveBeenCalledWith(
|
|
267
|
-
'encrypted:secret:iv:enckey'
|
|
268
|
-
);
|
|
269
|
-
expect(result.data.access_token).toBe('secret');
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it('should handle null result', async () => {
|
|
273
|
-
const args = {
|
|
274
|
-
where: { id: '999' },
|
|
275
|
-
mockResult: null,
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const result = await handlers.findUnique({
|
|
279
|
-
model: 'Credential',
|
|
280
|
-
operation: 'findUnique',
|
|
281
|
-
args,
|
|
282
|
-
query: mockQuery,
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
expect(result).toBeNull();
|
|
286
|
-
expect(mockCryptor.decrypt).not.toHaveBeenCalled();
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
describe('findMany', () => {
|
|
291
|
-
it('should decrypt array of results', async () => {
|
|
292
|
-
const args = {
|
|
293
|
-
mockResult: [
|
|
294
|
-
{
|
|
295
|
-
id: '1',
|
|
296
|
-
data: { access_token: 'encrypted:secret1:iv:enckey' },
|
|
297
|
-
},
|
|
298
|
-
{
|
|
299
|
-
id: '2',
|
|
300
|
-
data: { access_token: 'encrypted:secret2:iv:enckey' },
|
|
301
|
-
},
|
|
302
|
-
],
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
const results = await handlers.findMany({
|
|
306
|
-
model: 'Credential',
|
|
307
|
-
operation: 'findMany',
|
|
308
|
-
args,
|
|
309
|
-
query: mockQuery,
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
expect(results).toHaveLength(2);
|
|
313
|
-
expect(results[0].data.access_token).toBe('secret1');
|
|
314
|
-
expect(results[1].data.access_token).toBe('secret2');
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it('should handle empty array', async () => {
|
|
318
|
-
const args = {
|
|
319
|
-
mockResult: [],
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const results = await handlers.findMany({
|
|
323
|
-
model: 'Credential',
|
|
324
|
-
operation: 'findMany',
|
|
325
|
-
args,
|
|
326
|
-
query: mockQuery,
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
expect(results).toEqual([]);
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
describe('delete', () => {
|
|
334
|
-
it('should decrypt deleted record', async () => {
|
|
335
|
-
const args = {
|
|
336
|
-
where: { id: '1' },
|
|
337
|
-
mockResult: {
|
|
338
|
-
id: '1',
|
|
339
|
-
data: { access_token: 'encrypted:secret:iv:enckey' },
|
|
340
|
-
},
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
const result = await handlers.delete({
|
|
344
|
-
model: 'Credential',
|
|
345
|
-
operation: 'delete',
|
|
346
|
-
args,
|
|
347
|
-
query: mockQuery,
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
expect(result.data.access_token).toBe('secret');
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
describe('count', () => {
|
|
355
|
-
it('should pass through without encryption', async () => {
|
|
356
|
-
const args = {
|
|
357
|
-
mockResult: { count: 5 },
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
const result = await handlers.count({
|
|
361
|
-
model: 'Credential',
|
|
362
|
-
operation: 'count',
|
|
363
|
-
args,
|
|
364
|
-
query: mockQuery,
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
expect(result).toEqual({ count: 5 });
|
|
368
|
-
expect(mockCryptor.encrypt).not.toHaveBeenCalled();
|
|
369
|
-
expect(mockCryptor.decrypt).not.toHaveBeenCalled();
|
|
370
|
-
});
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
describe('Model without encrypted fields', () => {
|
|
374
|
-
it('should pass through State model without encryption', async () => {
|
|
375
|
-
const args = {
|
|
376
|
-
data: { state: { some: 'data' } },
|
|
377
|
-
mockResult: { id: '1', state: { some: 'data' } },
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
const result = await handlers.create({
|
|
381
|
-
model: 'State',
|
|
382
|
-
operation: 'create',
|
|
383
|
-
args,
|
|
384
|
-
query: mockQuery,
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
expect(result.state).toEqual({ some: 'data' });
|
|
388
|
-
expect(mockCryptor.encrypt).not.toHaveBeenCalled();
|
|
389
|
-
expect(mockCryptor.decrypt).not.toHaveBeenCalled();
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
describe('Integration with FieldEncryptionService', () => {
|
|
395
|
-
it('should handle nested JSON paths correctly', async () => {
|
|
396
|
-
const extension = createEncryptionExtension({
|
|
397
|
-
cryptor: mockCryptor,
|
|
398
|
-
enabled: true,
|
|
399
|
-
});
|
|
400
|
-
const handlers = extension.query.$allModels;
|
|
401
|
-
|
|
402
|
-
const args = {
|
|
403
|
-
data: {
|
|
404
|
-
id: '123',
|
|
405
|
-
data: {
|
|
406
|
-
access_token: 'secret',
|
|
407
|
-
refresh_token: 'refresh',
|
|
408
|
-
other: 'public',
|
|
409
|
-
},
|
|
410
|
-
},
|
|
411
|
-
mockResult: {
|
|
412
|
-
id: '123',
|
|
413
|
-
data: {
|
|
414
|
-
access_token: 'encrypted:secret:iv:enckey',
|
|
415
|
-
refresh_token: 'encrypted:refresh:iv:enckey',
|
|
416
|
-
other: 'public',
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
const result = await handlers.create({
|
|
422
|
-
model: 'Credential',
|
|
423
|
-
operation: 'create',
|
|
424
|
-
args,
|
|
425
|
-
query: mockQuery,
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
// Verify encryption was called for encrypted fields
|
|
429
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('secret');
|
|
430
|
-
expect(mockCryptor.encrypt).toHaveBeenCalledWith('refresh');
|
|
431
|
-
// 'other' should not be encrypted
|
|
432
|
-
|
|
433
|
-
// Verify decryption in result
|
|
434
|
-
expect(result.data.access_token).toBe('secret');
|
|
435
|
-
expect(result.data.refresh_token).toBe('refresh');
|
|
436
|
-
expect(result.data.other).toBe('public');
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
});
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const { BaseError } = require('./base-error');
|
|
2
|
-
|
|
3
|
-
describe('BaseError', () => {
|
|
4
|
-
it('can be inherited and instantiated', () => {
|
|
5
|
-
class XyzError extends BaseError {}
|
|
6
|
-
const error = new XyzError();
|
|
7
|
-
expect(error).toHaveProperty('message', '');
|
|
8
|
-
expect(error).toHaveProperty('stack');
|
|
9
|
-
expect(error.stack).toContain('XyzError');
|
|
10
|
-
expect(error.stack).toContain('at new XyzError');
|
|
11
|
-
expect(error.stack).toContain('base-error.test.js:');
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('can set the error message', () => {
|
|
15
|
-
class TestError extends BaseError {}
|
|
16
|
-
const error = new TestError('Goblins!');
|
|
17
|
-
expect(error).toHaveProperty('message', 'Goblins!');
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('sets the error name correctly', () => {
|
|
21
|
-
class TestError extends BaseError {}
|
|
22
|
-
const error = new TestError();
|
|
23
|
-
expect(error).toHaveProperty('name', 'TestError');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('can set the parent cause', () => {
|
|
27
|
-
class TestError extends BaseError {}
|
|
28
|
-
const rootError = new Error('Davros!');
|
|
29
|
-
const error = new TestError('Daleks!', { cause: rootError });
|
|
30
|
-
expect(error).toHaveProperty('cause', rootError);
|
|
31
|
-
});
|
|
32
|
-
});
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
const fetch = require('node-fetch');
|
|
2
|
-
const { stripIndent } = require('common-tags');
|
|
3
|
-
const { FetchError } = require('./fetch-error');
|
|
4
|
-
const FormData = require('form-data');
|
|
5
|
-
|
|
6
|
-
describe('FetchError', () => {
|
|
7
|
-
it('can be instantiated with default arguments', () => {
|
|
8
|
-
const error = new FetchError();
|
|
9
|
-
expect(error).toHaveProperty('message');
|
|
10
|
-
expect(error).not.toHaveProperty('cause');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('can be created asynchronously with default values', async () => {
|
|
14
|
-
const error = await FetchError.create();
|
|
15
|
-
expect(error).toHaveProperty('message');
|
|
16
|
-
expect(error).not.toHaveProperty('cause');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('can be created asynchronously', async () => {
|
|
20
|
-
const resource = 'http://example.com';
|
|
21
|
-
const init = {};
|
|
22
|
-
const response = {
|
|
23
|
-
status: 500,
|
|
24
|
-
statusText: 'Space aliens!',
|
|
25
|
-
headers: Object.entries({ 'cache-control': '123' }), // needs to be an Iterable
|
|
26
|
-
text: async () => '<!doctype html>',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const error = await FetchError.create({
|
|
30
|
-
resource,
|
|
31
|
-
init,
|
|
32
|
-
response,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
expect(error).toHaveProperty('message');
|
|
36
|
-
expect(error.message).toContain('GET http://example.com');
|
|
37
|
-
expect(error.message).toContain('500 Space aliens!');
|
|
38
|
-
expect(error.message).toContain('"cache-control": "123"');
|
|
39
|
-
expect(error.message).toContain('<!doctype html>');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('can be passed an object for the body', async () => {
|
|
43
|
-
const error = new FetchError({ responseBody: { test: true } });
|
|
44
|
-
expect(error).toHaveProperty('message');
|
|
45
|
-
expect(error.message).toContain('"test": true');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('ignores response body if already streamed', async () => {
|
|
49
|
-
const response = { bodyUsed: true };
|
|
50
|
-
const error = await FetchError.create({ response });
|
|
51
|
-
|
|
52
|
-
expect(error).toHaveProperty('message');
|
|
53
|
-
expect(error.message).toContain('<response body is unavailable>');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it.only('prints a formData body legibly', async () => {
|
|
57
|
-
const response = {
|
|
58
|
-
status: 500,
|
|
59
|
-
statusText: 'Space aliens!',
|
|
60
|
-
headers: Object.entries({ 'cache-control': '123' }), // needs to be an Iterable
|
|
61
|
-
text: async () => '<!doctype html>',
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const params = new URLSearchParams();
|
|
65
|
-
params.append('test', 'test');
|
|
66
|
-
const init = {
|
|
67
|
-
method: 'POST',
|
|
68
|
-
credentials: 'include',
|
|
69
|
-
headers: {},
|
|
70
|
-
query: {},
|
|
71
|
-
body: params,
|
|
72
|
-
returnFullRes: false,
|
|
73
|
-
};
|
|
74
|
-
const error = await FetchError.create({ response, init });
|
|
75
|
-
|
|
76
|
-
expect(error).toHaveProperty('message');
|
|
77
|
-
expect(error.message).toContain('test=test');
|
|
78
|
-
});
|
|
79
|
-
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const { HaltError } = require('./halt-error');
|
|
2
|
-
|
|
3
|
-
describe('HaltError', () => {
|
|
4
|
-
it('can be instantiated', () => {
|
|
5
|
-
const rootError = new Error('Gremlinoids!!');
|
|
6
|
-
const error = new HaltError('STOP', { cause: rootError });
|
|
7
|
-
expect(error).toHaveProperty('message', 'STOP');
|
|
8
|
-
expect(error).toHaveProperty('cause', rootError);
|
|
9
|
-
expect(error).toHaveProperty('isHaltError', true);
|
|
10
|
-
});
|
|
11
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
RequiredPropertyError,
|
|
3
|
-
ParameterTypeError,
|
|
4
|
-
} = require('./validation-errors');
|
|
5
|
-
|
|
6
|
-
describe('RequiredPropertyError', () => {
|
|
7
|
-
it('can be instantiated with default arguments', () => {
|
|
8
|
-
const error = new RequiredPropertyError();
|
|
9
|
-
expect(error).toHaveProperty(
|
|
10
|
-
'message',
|
|
11
|
-
'Key "" is a required parameter.'
|
|
12
|
-
);
|
|
13
|
-
expect(error).not.toHaveProperty('cause');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it('allows setting the key name of the error', () => {
|
|
17
|
-
const error = new RequiredPropertyError({ key: 'abc' });
|
|
18
|
-
expect(error).toHaveProperty(
|
|
19
|
-
'message',
|
|
20
|
-
'Key "abc" is a required parameter.'
|
|
21
|
-
);
|
|
22
|
-
expect(error).not.toHaveProperty('cause');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('allows setting the parent of the error', () => {
|
|
26
|
-
const error = new RequiredPropertyError({ parent: class Xyz {} });
|
|
27
|
-
expect(error).toHaveProperty(
|
|
28
|
-
'message',
|
|
29
|
-
'(Xyz) Key "" is a required parameter.'
|
|
30
|
-
);
|
|
31
|
-
expect(error).not.toHaveProperty('cause');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('passes cause through to the Error parent class', () => {
|
|
35
|
-
const error = new RequiredPropertyError(
|
|
36
|
-
{
|
|
37
|
-
parent: class Qrs {},
|
|
38
|
-
key: 'def',
|
|
39
|
-
},
|
|
40
|
-
{ cause: new Error('Gremlins!!') }
|
|
41
|
-
);
|
|
42
|
-
const expectedMessage = '(Qrs) Key "def" is a required parameter.';
|
|
43
|
-
expect(error).toHaveProperty('message', expectedMessage);
|
|
44
|
-
expect(error).toHaveProperty('cause');
|
|
45
|
-
expect(error.cause).toHaveProperty('message', 'Gremlins!!');
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe('ParameterTypeError', () => {
|
|
50
|
-
it('can be instantiated with default arguments', () => {
|
|
51
|
-
const error = new ParameterTypeError();
|
|
52
|
-
expect(error).toHaveProperty(
|
|
53
|
-
'message',
|
|
54
|
-
'Expected value "" to be of type ""'
|
|
55
|
-
);
|
|
56
|
-
expect(error).not.toHaveProperty('cause');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('allows setting the key name of the error', () => {
|
|
60
|
-
const error = new ParameterTypeError({ key: 'abc' });
|
|
61
|
-
expect(error).toHaveProperty(
|
|
62
|
-
'message',
|
|
63
|
-
'Expected key "abc" with value "" to be of type ""'
|
|
64
|
-
);
|
|
65
|
-
expect(error).not.toHaveProperty('cause');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('allows setting the parent of the error', () => {
|
|
69
|
-
const error = new ParameterTypeError({ parent: class Xyz {} });
|
|
70
|
-
expect(error).toHaveProperty(
|
|
71
|
-
'message',
|
|
72
|
-
'(Xyz) Expected value "" to be of type ""'
|
|
73
|
-
);
|
|
74
|
-
expect(error).not.toHaveProperty('cause');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('allows setting the value of the error', () => {
|
|
78
|
-
const error = new ParameterTypeError({ value: 1 });
|
|
79
|
-
expect(error).toHaveProperty(
|
|
80
|
-
'message',
|
|
81
|
-
'Expected value "1" to be of type ""'
|
|
82
|
-
);
|
|
83
|
-
expect(error).not.toHaveProperty('cause');
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it('allows setting the expected type of the error', () => {
|
|
87
|
-
const error = new ParameterTypeError({ expectedType: class Xyz {} });
|
|
88
|
-
expect(error).toHaveProperty(
|
|
89
|
-
'message',
|
|
90
|
-
'Expected value "" to be of type "Xyz"'
|
|
91
|
-
);
|
|
92
|
-
expect(error).not.toHaveProperty('cause');
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('allows setting the expected type of the error to Array', () => {
|
|
96
|
-
const error = new ParameterTypeError({ expectedType: Array });
|
|
97
|
-
expect(error).toHaveProperty(
|
|
98
|
-
'message',
|
|
99
|
-
'Expected value "" to be of type "Array"'
|
|
100
|
-
);
|
|
101
|
-
expect(error).not.toHaveProperty('cause');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('passes cause through to the Error parent class', () => {
|
|
105
|
-
const rootError = new Error('Gremlins!!');
|
|
106
|
-
const error = new ParameterTypeError(
|
|
107
|
-
{
|
|
108
|
-
parent: class Parent {},
|
|
109
|
-
key: 'clé',
|
|
110
|
-
expectedType: class Expected {},
|
|
111
|
-
value: 'schmalue',
|
|
112
|
-
},
|
|
113
|
-
{ cause: rootError }
|
|
114
|
-
);
|
|
115
|
-
const expectedMessage =
|
|
116
|
-
'(Parent) Expected key "clé" with value "schmalue" to be of type "Expected"';
|
|
117
|
-
expect(error).toHaveProperty('message', expectedMessage);
|
|
118
|
-
expect(error).toHaveProperty('cause', rootError);
|
|
119
|
-
});
|
|
120
|
-
});
|