@friggframework/core 2.0.0-next.45 → 2.0.0-next.46
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,150 +0,0 @@
|
|
|
1
|
-
jest.mock('../../../database/config', () => ({
|
|
2
|
-
DB_TYPE: 'mongodb',
|
|
3
|
-
getDatabaseType: jest.fn(() => 'mongodb'),
|
|
4
|
-
PRISMA_LOG_LEVEL: 'error,warn',
|
|
5
|
-
PRISMA_QUERY_LOGGING: false,
|
|
6
|
-
}));
|
|
7
|
-
|
|
8
|
-
const { DeleteIntegrationForUser } = require('../../use-cases/delete-integration-for-user');
|
|
9
|
-
const { TestIntegrationRepository } = require('../doubles/test-integration-repository');
|
|
10
|
-
const { DummyIntegration } = require('../doubles/dummy-integration-class');
|
|
11
|
-
|
|
12
|
-
describe('DeleteIntegrationForUser Use-Case', () => {
|
|
13
|
-
let integrationRepository;
|
|
14
|
-
let useCase;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
integrationRepository = new TestIntegrationRepository();
|
|
18
|
-
useCase = new DeleteIntegrationForUser({
|
|
19
|
-
integrationRepository,
|
|
20
|
-
integrationClasses: [DummyIntegration],
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
describe('happy path', () => {
|
|
25
|
-
it('deletes integration successfully', async () => {
|
|
26
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
27
|
-
|
|
28
|
-
await useCase.execute(record.id, 'user-1');
|
|
29
|
-
|
|
30
|
-
const found = await integrationRepository.findIntegrationById(record.id);
|
|
31
|
-
expect(found).toBeNull();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('tracks delete operation', async () => {
|
|
35
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
36
|
-
integrationRepository.clearHistory();
|
|
37
|
-
|
|
38
|
-
await useCase.execute(record.id, 'user-1');
|
|
39
|
-
|
|
40
|
-
const history = integrationRepository.getOperationHistory();
|
|
41
|
-
const deleteOperation = history.find(op => op.operation === 'delete');
|
|
42
|
-
expect(deleteOperation).toEqual({
|
|
43
|
-
operation: 'delete',
|
|
44
|
-
id: record.id,
|
|
45
|
-
existed: true,
|
|
46
|
-
success: true
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('deletes integration with multiple entities', async () => {
|
|
51
|
-
const record = await integrationRepository.createIntegration(['e1', 'e2', 'e3'], 'user-1', { type: 'dummy' });
|
|
52
|
-
|
|
53
|
-
await useCase.execute(record.id, 'user-1');
|
|
54
|
-
|
|
55
|
-
const found = await integrationRepository.findIntegrationById(record.id);
|
|
56
|
-
expect(found).toBeNull();
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
describe('error cases', () => {
|
|
61
|
-
it('throws error when integration not found', async () => {
|
|
62
|
-
const nonExistentId = 'non-existent-id';
|
|
63
|
-
|
|
64
|
-
await expect(useCase.execute(nonExistentId, 'user-1'))
|
|
65
|
-
.rejects
|
|
66
|
-
.toThrow(`Integration with id of ${nonExistentId} does not exist`);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('throws error when user does not own integration', async () => {
|
|
70
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
71
|
-
|
|
72
|
-
await expect(useCase.execute(record.id, 'different-user'))
|
|
73
|
-
.rejects
|
|
74
|
-
.toThrow(`Integration ${record.id} does not belong to User different-user`);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('throws error when integration class not found', async () => {
|
|
78
|
-
const useCaseWithoutClasses = new DeleteIntegrationForUser({
|
|
79
|
-
integrationRepository,
|
|
80
|
-
integrationClasses: [],
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
84
|
-
|
|
85
|
-
await expect(useCaseWithoutClasses.execute(record.id, 'user-1'))
|
|
86
|
-
.rejects
|
|
87
|
-
.toThrow();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('tracks failed delete operation for non-existent integration', async () => {
|
|
91
|
-
const nonExistentId = 'non-existent-id';
|
|
92
|
-
integrationRepository.clearHistory();
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
await useCase.execute(nonExistentId, 'user-1');
|
|
96
|
-
} catch (error) {
|
|
97
|
-
const history = integrationRepository.getOperationHistory();
|
|
98
|
-
const findOperation = history.find(op => op.operation === 'findById');
|
|
99
|
-
expect(findOperation).toEqual({
|
|
100
|
-
operation: 'findById',
|
|
101
|
-
id: nonExistentId,
|
|
102
|
-
found: false
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe('edge cases', () => {
|
|
109
|
-
it('handles deletion of already deleted integration', async () => {
|
|
110
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
111
|
-
|
|
112
|
-
await useCase.execute(record.id, 'user-1');
|
|
113
|
-
|
|
114
|
-
await expect(useCase.execute(record.id, 'user-1'))
|
|
115
|
-
.rejects
|
|
116
|
-
.toThrow(`Integration with id of ${record.id} does not exist`);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('handles integration with complex config during deletion', async () => {
|
|
120
|
-
const complexConfig = {
|
|
121
|
-
type: 'dummy',
|
|
122
|
-
settings: { nested: { deep: 'value' } },
|
|
123
|
-
credentials: { encrypted: true }
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', complexConfig);
|
|
127
|
-
|
|
128
|
-
await useCase.execute(record.id, 'user-1');
|
|
129
|
-
|
|
130
|
-
const found = await integrationRepository.findIntegrationById(record.id);
|
|
131
|
-
expect(found).toBeNull();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('handles null userId gracefully', async () => {
|
|
135
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
136
|
-
|
|
137
|
-
await expect(useCase.execute(record.id, null))
|
|
138
|
-
.rejects
|
|
139
|
-
.toThrow(`Integration ${record.id} does not belong to User null`);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it('handles undefined userId gracefully', async () => {
|
|
143
|
-
const record = await integrationRepository.createIntegration(['e1'], 'user-1', { type: 'dummy' });
|
|
144
|
-
|
|
145
|
-
await expect(useCase.execute(record.id, undefined))
|
|
146
|
-
.rejects
|
|
147
|
-
.toThrow(`Integration ${record.id} does not belong to User undefined`);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
});
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
const { FindIntegrationContextByExternalEntityIdUseCase } = require('../../use-cases/find-integration-context-by-external-entity-id');
|
|
2
|
-
const { TestModuleRepository } = require('../../../modules/tests/doubles/test-module-repository');
|
|
3
|
-
const { TestIntegrationRepository } = require('../doubles/test-integration-repository');
|
|
4
|
-
const { DummyIntegration } = require('../doubles/dummy-integration-class');
|
|
5
|
-
|
|
6
|
-
describe('FindIntegrationContextByExternalEntityIdUseCase', () => {
|
|
7
|
-
let moduleRepository;
|
|
8
|
-
let integrationRepository;
|
|
9
|
-
let loadIntegrationContextUseCase;
|
|
10
|
-
let useCase;
|
|
11
|
-
|
|
12
|
-
beforeEach(() => {
|
|
13
|
-
moduleRepository = new TestModuleRepository();
|
|
14
|
-
integrationRepository = new TestIntegrationRepository();
|
|
15
|
-
loadIntegrationContextUseCase = {
|
|
16
|
-
execute: jest.fn(),
|
|
17
|
-
};
|
|
18
|
-
useCase = new FindIntegrationContextByExternalEntityIdUseCase({
|
|
19
|
-
moduleRepository,
|
|
20
|
-
integrationRepository,
|
|
21
|
-
loadIntegrationContextUseCase,
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('throws when externalEntityId is missing', async () => {
|
|
26
|
-
await expect(useCase.execute({})).rejects.toHaveProperty(
|
|
27
|
-
'code',
|
|
28
|
-
'EXTERNAL_ENTITY_ID_REQUIRED',
|
|
29
|
-
);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('throws when entity is not found', async () => {
|
|
33
|
-
await expect(
|
|
34
|
-
useCase.execute({ externalEntityId: 'abc' }),
|
|
35
|
-
).rejects.toHaveProperty('code', 'ENTITY_NOT_FOUND');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('throws when entity user is missing', async () => {
|
|
39
|
-
moduleRepository.addEntity({
|
|
40
|
-
id: 'entity-1',
|
|
41
|
-
externalId: 'ext-1',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
await expect(
|
|
45
|
-
useCase.execute({ externalEntityId: 'ext-1' }),
|
|
46
|
-
).rejects.toHaveProperty('code', 'ENTITY_USER_NOT_FOUND');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('throws when integration is not found for user', async () => {
|
|
50
|
-
moduleRepository.addEntity({
|
|
51
|
-
id: 'entity-1',
|
|
52
|
-
externalId: 'ext-1',
|
|
53
|
-
userId: 'user-1',
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
await expect(
|
|
57
|
-
useCase.execute({ externalEntityId: 'ext-1' }),
|
|
58
|
-
).rejects.toHaveProperty('code', 'INTEGRATION_NOT_FOUND');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('returns context, entity, and record on success', async () => {
|
|
62
|
-
const entity = {
|
|
63
|
-
id: 'entity-1',
|
|
64
|
-
externalId: 'ext-1',
|
|
65
|
-
userId: 'user-1',
|
|
66
|
-
};
|
|
67
|
-
moduleRepository.addEntity(entity);
|
|
68
|
-
|
|
69
|
-
const integrationRecord = await integrationRepository.createIntegration(
|
|
70
|
-
[entity.id],
|
|
71
|
-
entity.userId,
|
|
72
|
-
{ type: 'dummy' },
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const expectedContext = {
|
|
76
|
-
record: integrationRecord,
|
|
77
|
-
modules: [{ id: 'module-1' }],
|
|
78
|
-
};
|
|
79
|
-
loadIntegrationContextUseCase.execute.mockResolvedValue(
|
|
80
|
-
expectedContext,
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const result = await useCase.execute({ externalEntityId: 'ext-1' });
|
|
84
|
-
|
|
85
|
-
expect(loadIntegrationContextUseCase.execute).toHaveBeenCalledWith({
|
|
86
|
-
integrationRecord,
|
|
87
|
-
});
|
|
88
|
-
expect(result.context).toEqual(expectedContext);
|
|
89
|
-
expect(result.entity).toEqual(entity);
|
|
90
|
-
expect(result.record).toEqual(integrationRecord);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
jest.mock('../../../database/config', () => ({
|
|
2
|
-
DB_TYPE: 'mongodb',
|
|
3
|
-
getDatabaseType: jest.fn(() => 'mongodb'),
|
|
4
|
-
PRISMA_LOG_LEVEL: 'error,warn',
|
|
5
|
-
PRISMA_QUERY_LOGGING: false,
|
|
6
|
-
}));
|
|
7
|
-
|
|
8
|
-
const { GetIntegrationForUser } = require('../../use-cases/get-integration-for-user');
|
|
9
|
-
const { TestIntegrationRepository } = require('../doubles/test-integration-repository');
|
|
10
|
-
const { TestModuleFactory } = require('../../../modules/tests/doubles/test-module-factory');
|
|
11
|
-
const { TestModuleRepository } = require('../../../modules/tests/doubles/test-module-repository');
|
|
12
|
-
const { DummyIntegration } = require('../doubles/dummy-integration-class');
|
|
13
|
-
|
|
14
|
-
describe('GetIntegrationForUser Use-Case', () => {
|
|
15
|
-
let integrationRepository;
|
|
16
|
-
let moduleRepository;
|
|
17
|
-
let moduleFactory;
|
|
18
|
-
let useCase;
|
|
19
|
-
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
integrationRepository = new TestIntegrationRepository();
|
|
22
|
-
moduleRepository = new TestModuleRepository();
|
|
23
|
-
moduleFactory = new TestModuleFactory();
|
|
24
|
-
useCase = new GetIntegrationForUser({
|
|
25
|
-
integrationRepository,
|
|
26
|
-
integrationClasses: [DummyIntegration],
|
|
27
|
-
moduleFactory,
|
|
28
|
-
moduleRepository,
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe('happy path', () => {
|
|
33
|
-
it('returns integration dto', async () => {
|
|
34
|
-
const entity = { id: 'entity-1', _id: 'entity-1' };
|
|
35
|
-
moduleRepository.addEntity(entity);
|
|
36
|
-
|
|
37
|
-
const record = await integrationRepository.createIntegration([entity.id], 'user-1', { type: 'dummy' });
|
|
38
|
-
|
|
39
|
-
const dto = await useCase.execute(record.id, 'user-1');
|
|
40
|
-
expect(dto.id).toBe(record.id);
|
|
41
|
-
expect(dto.userId).toBe('user-1');
|
|
42
|
-
expect(dto.config.type).toBe('dummy');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('returns integration with multiple entities', async () => {
|
|
46
|
-
const entity1 = { id: 'entity-1', _id: 'entity-1' };
|
|
47
|
-
const entity2 = { id: 'entity-2', _id: 'entity-2' };
|
|
48
|
-
moduleRepository.addEntity(entity1);
|
|
49
|
-
moduleRepository.addEntity(entity2);
|
|
50
|
-
|
|
51
|
-
const record = await integrationRepository.createIntegration([entity1.id, entity2.id], 'user-1', { type: 'dummy' });
|
|
52
|
-
|
|
53
|
-
const dto = await useCase.execute(record.id, 'user-1');
|
|
54
|
-
expect(dto.entities).toEqual([entity1, entity2]);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('returns integration with complex config', async () => {
|
|
58
|
-
const entity = { id: 'entity-1', _id: 'entity-1' };
|
|
59
|
-
moduleRepository.addEntity(entity);
|
|
60
|
-
|
|
61
|
-
const complexConfig = {
|
|
62
|
-
type: 'dummy',
|
|
63
|
-
settings: { api: { timeout: 5000 }, debug: true },
|
|
64
|
-
features: ['webhooks', 'sync']
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const record = await integrationRepository.createIntegration([entity.id], 'user-1', complexConfig);
|
|
68
|
-
|
|
69
|
-
const dto = await useCase.execute(record.id, 'user-1');
|
|
70
|
-
expect(dto.config).toEqual(complexConfig);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe('error cases', () => {
|
|
75
|
-
it('throws error when integration not found', async () => {
|
|
76
|
-
const nonExistentId = 'non-existent-id';
|
|
77
|
-
|
|
78
|
-
await expect(useCase.execute(nonExistentId, 'user-1'))
|
|
79
|
-
.rejects
|
|
80
|
-
.toThrow();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('throws error when user does not own integration', async () => {
|
|
84
|
-
const entity = { id: 'entity-1', _id: 'entity-1' };
|
|
85
|
-
moduleRepository.addEntity(entity);
|
|
86
|
-
|
|
87
|
-
const record = await integrationRepository.createIntegration([entity.id], 'user-1', { type: 'dummy' });
|
|
88
|
-
|
|
89
|
-
await expect(useCase.execute(record.id, 'different-user'))
|
|
90
|
-
.rejects
|
|
91
|
-
.toThrow();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('throws error when integration class not found', async () => {
|
|
95
|
-
const useCaseWithoutClasses = new GetIntegrationForUser({
|
|
96
|
-
integrationRepository,
|
|
97
|
-
integrationClasses: [],
|
|
98
|
-
moduleFactory,
|
|
99
|
-
moduleRepository,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
const entity = { id: 'entity-1', _id: 'entity-1' };
|
|
103
|
-
moduleRepository.addEntity(entity);
|
|
104
|
-
|
|
105
|
-
const record = await integrationRepository.createIntegration([entity.id], 'user-1', { type: 'dummy' });
|
|
106
|
-
|
|
107
|
-
await expect(useCaseWithoutClasses.execute(record.id, 'user-1'))
|
|
108
|
-
.rejects
|
|
109
|
-
.toThrow();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('handles missing entities gracefully', async () => {
|
|
113
|
-
const record = await integrationRepository.createIntegration(['missing-entity'], 'user-1', { type: 'dummy' });
|
|
114
|
-
|
|
115
|
-
await expect(useCase.execute(record.id, 'user-1'))
|
|
116
|
-
.rejects
|
|
117
|
-
.toThrow();
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('edge cases', () => {
|
|
122
|
-
it('handles userId as string vs number comparison', async () => {
|
|
123
|
-
const entity = { id: 'entity-1', _id: 'entity-1' };
|
|
124
|
-
moduleRepository.addEntity(entity);
|
|
125
|
-
|
|
126
|
-
const record = await integrationRepository.createIntegration([entity.id], 'user-1', { type: 'dummy' });
|
|
127
|
-
|
|
128
|
-
const dto1 = await useCase.execute(record.id, 'user-1');
|
|
129
|
-
const dto2 = await useCase.execute(record.id, 'user-1');
|
|
130
|
-
|
|
131
|
-
expect(dto1.userId).toBe(dto2.userId);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('returns all integration properties', async () => {
|
|
135
|
-
const entity = { id: 'entity-1', _id: 'entity-1' };
|
|
136
|
-
moduleRepository.addEntity(entity);
|
|
137
|
-
|
|
138
|
-
const record = await integrationRepository.createIntegration([entity.id], 'user-1', { type: 'dummy' });
|
|
139
|
-
|
|
140
|
-
record.status = 'ACTIVE';
|
|
141
|
-
record.version = '1.0.0';
|
|
142
|
-
record.messages = { info: [{ title: 'Test', message: 'Message' }] };
|
|
143
|
-
|
|
144
|
-
const dto = await useCase.execute(record.id, 'user-1');
|
|
145
|
-
expect(dto.status).toBe('ACTIVE');
|
|
146
|
-
expect(dto.version).toBe('1.0.0');
|
|
147
|
-
expect(dto.messages).toEqual({ info: [{ title: 'Test', message: 'Message' }] });
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
jest.mock('../../../database/config', () => ({
|
|
2
|
-
DB_TYPE: 'mongodb',
|
|
3
|
-
getDatabaseType: jest.fn(() => 'mongodb'),
|
|
4
|
-
PRISMA_LOG_LEVEL: 'error,warn',
|
|
5
|
-
PRISMA_QUERY_LOGGING: false,
|
|
6
|
-
}));
|
|
7
|
-
|
|
8
|
-
const { GetIntegrationInstance } = require('../../use-cases/get-integration-instance');
|
|
9
|
-
const { TestIntegrationRepository } = require('../doubles/test-integration-repository');
|
|
10
|
-
const { TestModuleFactory } = require('../../../modules/tests/doubles/test-module-factory');
|
|
11
|
-
const { DummyIntegration } = require('../doubles/dummy-integration-class');
|
|
12
|
-
|
|
13
|
-
describe('GetIntegrationInstance Use-Case', () => {
|
|
14
|
-
let integrationRepository;
|
|
15
|
-
let moduleFactory;
|
|
16
|
-
let useCase;
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
integrationRepository = new TestIntegrationRepository();
|
|
20
|
-
moduleFactory = new TestModuleFactory();
|
|
21
|
-
useCase = new GetIntegrationInstance({
|
|
22
|
-
integrationRepository,
|
|
23
|
-
integrationClasses: [DummyIntegration],
|
|
24
|
-
moduleFactory,
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('happy path', () => {
|
|
29
|
-
it('returns hydrated integration instance', async () => {
|
|
30
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'dummy' });
|
|
31
|
-
|
|
32
|
-
const instance = await useCase.execute(record.id, 'user-1');
|
|
33
|
-
|
|
34
|
-
expect(instance.id).toBe(record.id);
|
|
35
|
-
expect(instance.getConfig().type).toBe('dummy');
|
|
36
|
-
expect(instance.entities).toEqual(record.entitiesIds);
|
|
37
|
-
expect(instance.userId).toBe('user-1');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('returns instance with multiple modules', async () => {
|
|
41
|
-
const record = await integrationRepository.createIntegration(['entity-1', 'entity-2'], 'user-1', { type: 'dummy' });
|
|
42
|
-
|
|
43
|
-
const instance = await useCase.execute(record.id, 'user-1');
|
|
44
|
-
|
|
45
|
-
expect(instance.entities).toEqual(['entity-1', 'entity-2']);
|
|
46
|
-
expect(Object.keys(instance.modules)).toHaveLength(1);
|
|
47
|
-
expect(instance.modules['stubModule']).toBeDefined();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('initializes integration instance properly', async () => {
|
|
51
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'dummy' });
|
|
52
|
-
|
|
53
|
-
const instance = await useCase.execute(record.id, 'user-1');
|
|
54
|
-
|
|
55
|
-
expect(typeof instance.send).toBe('function');
|
|
56
|
-
expect(typeof instance.getConfig).toBe('function');
|
|
57
|
-
expect(typeof instance.initialize).toBe('function');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('preserves all integration properties', async () => {
|
|
61
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'dummy', custom: 'value' });
|
|
62
|
-
|
|
63
|
-
record.status = 'ACTIVE';
|
|
64
|
-
record.version = '2.0.0';
|
|
65
|
-
record.messages = { logs: [{ title: 'Test', message: 'Log entry' }] };
|
|
66
|
-
|
|
67
|
-
const instance = await useCase.execute(record.id, 'user-1');
|
|
68
|
-
|
|
69
|
-
expect(instance.status).toBe('ACTIVE');
|
|
70
|
-
expect(instance.version).toBe('2.0.0');
|
|
71
|
-
expect(instance.messages).toEqual({ logs: [{ title: 'Test', message: 'Log entry' }] });
|
|
72
|
-
expect(instance.getConfig().custom).toBe('value');
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('error cases', () => {
|
|
77
|
-
it('throws error when integration not found', async () => {
|
|
78
|
-
const nonExistentId = 'non-existent-id';
|
|
79
|
-
|
|
80
|
-
await expect(useCase.execute(nonExistentId, 'user-1'))
|
|
81
|
-
.rejects
|
|
82
|
-
.toThrow(`No integration found by the ID of ${nonExistentId}`);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('throws error when user does not own integration', async () => {
|
|
86
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'dummy' });
|
|
87
|
-
|
|
88
|
-
await expect(useCase.execute(record.id, 'different-user'))
|
|
89
|
-
.rejects
|
|
90
|
-
.toThrow(`Integration ${record.id} does not belong to User different-user`);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('throws error when integration class not found', async () => {
|
|
94
|
-
const useCaseWithoutClasses = new GetIntegrationInstance({
|
|
95
|
-
integrationRepository,
|
|
96
|
-
integrationClasses: [],
|
|
97
|
-
moduleFactory,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'dummy' });
|
|
101
|
-
|
|
102
|
-
await expect(useCaseWithoutClasses.execute(record.id, 'user-1'))
|
|
103
|
-
.rejects
|
|
104
|
-
.toThrow('No integration class found for type: dummy');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('throws error when integration has unknown type', async () => {
|
|
108
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'unknown-type' });
|
|
109
|
-
|
|
110
|
-
await expect(useCase.execute(record.id, 'user-1'))
|
|
111
|
-
.rejects
|
|
112
|
-
.toThrow('No integration class found for type: unknown-type');
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('edge cases', () => {
|
|
117
|
-
it('handles integration with no entities', async () => {
|
|
118
|
-
const record = await integrationRepository.createIntegration([], 'user-1', { type: 'dummy' });
|
|
119
|
-
|
|
120
|
-
const instance = await useCase.execute(record.id, 'user-1');
|
|
121
|
-
|
|
122
|
-
expect(instance.entities).toEqual([]);
|
|
123
|
-
expect(Object.keys(instance.modules)).toHaveLength(0);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('handles integration with null config values', async () => {
|
|
127
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'dummy', nullValue: null });
|
|
128
|
-
|
|
129
|
-
const instance = await useCase.execute(record.id, 'user-1');
|
|
130
|
-
|
|
131
|
-
expect(instance.getConfig().nullValue).toBeNull();
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('handles userId comparison edge cases', async () => {
|
|
135
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'dummy' });
|
|
136
|
-
|
|
137
|
-
const instance1 = await useCase.execute(record.id, 'user-1');
|
|
138
|
-
const instance2 = await useCase.execute(record.id, 'user-1');
|
|
139
|
-
|
|
140
|
-
expect(instance1.userId).toBe(instance2.userId);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('returns fresh instance on each call', async () => {
|
|
144
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', { type: 'dummy' });
|
|
145
|
-
|
|
146
|
-
const instance1 = await useCase.execute(record.id, 'user-1');
|
|
147
|
-
const instance2 = await useCase.execute(record.id, 'user-1');
|
|
148
|
-
|
|
149
|
-
expect(instance1).not.toBe(instance2);
|
|
150
|
-
expect(instance1.id).toBe(instance2.id);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('handles complex nested config structures', async () => {
|
|
154
|
-
const complexConfig = {
|
|
155
|
-
type: 'dummy',
|
|
156
|
-
settings: {
|
|
157
|
-
api: {
|
|
158
|
-
timeout: 5000,
|
|
159
|
-
retries: 3,
|
|
160
|
-
endpoints: ['users', 'orders']
|
|
161
|
-
},
|
|
162
|
-
features: {
|
|
163
|
-
webhooks: true,
|
|
164
|
-
sync: { interval: 300 }
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const record = await integrationRepository.createIntegration(['entity-1'], 'user-1', complexConfig);
|
|
170
|
-
|
|
171
|
-
const instance = await useCase.execute(record.id, 'user-1');
|
|
172
|
-
|
|
173
|
-
expect(instance.getConfig()).toEqual(complexConfig);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|