@friggframework/core 2.0.0--canary.686.3e27a92.0 → 2.0.0--canary.663.8de669c.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +0 -28
- package/README.md +0 -105
- package/application/commands/integration-commands.js +10 -20
- package/application/commands/scheduler-commands.js +7 -11
- package/application/commands/user-commands.js +5 -17
- package/application/index.js +2 -6
- package/core/Worker.js +7 -31
- package/core/create-handler.js +0 -9
- package/credential/repositories/credential-repository-documentdb.js +0 -34
- package/credential/repositories/credential-repository-interface.js +0 -15
- package/credential/repositories/credential-repository-mongo.js +0 -23
- package/credential/repositories/credential-repository-postgres.js +0 -28
- package/database/config.js +24 -48
- package/database/documentdb-utils.js +1 -6
- package/database/encryption/encryption-config.js +31 -10
- package/database/encryption/encryption-schema-registry.js +12 -93
- package/database/prisma.js +2 -4
- package/generated/prisma-mongodb/edge.js +4 -43
- package/generated/prisma-mongodb/index-browser.js +1 -40
- package/generated/prisma-mongodb/index.d.ts +111 -4739
- package/generated/prisma-mongodb/index.js +4 -43
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -92
- package/generated/prisma-mongodb/wasm.js +4 -43
- package/generated/prisma-postgresql/edge.js +4 -43
- package/generated/prisma-postgresql/index-browser.js +1 -40
- package/generated/prisma-postgresql/index.d.ts +33 -4966
- package/generated/prisma-postgresql/index.js +4 -43
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -87
- package/generated/prisma-postgresql/wasm.js +4 -43
- package/handlers/app-definition-loader.js +19 -121
- package/handlers/app-handler-helpers.js +3 -25
- package/handlers/backend-utils.js +25 -31
- package/handlers/middleware/admin-auth.js +1 -14
- package/handlers/routers/auth.js +10 -46
- package/handlers/routers/health.js +4 -13
- package/handlers/routers/integration-defined-routers.js +92 -144
- package/handlers/routers/integration-webhook-routers.js +64 -102
- package/handlers/workers/integration-defined-workers.js +2 -5
- package/index.js +1 -65
- package/infrastructure/scheduler/index.js +6 -19
- package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
- package/integrations/index.js +0 -1
- package/integrations/integration-router.js +96 -131
- package/integrations/repositories/integration-repository-documentdb.js +3 -35
- package/integrations/repositories/integration-repository-interface.js +1 -17
- package/integrations/repositories/integration-repository-mongo.js +76 -59
- package/integrations/repositories/integration-repository-postgres.js +76 -62
- package/integrations/tests/doubles/test-integration-repository.js +1 -12
- package/integrations/use-cases/create-integration.js +20 -55
- package/integrations/use-cases/get-integrations-for-user.js +40 -80
- package/integrations/use-cases/index.js +1 -7
- package/integrations/utils/map-integration-dto.js +14 -43
- package/modules/index.js +0 -14
- package/modules/module-factory.js +0 -12
- package/modules/module.js +2 -53
- package/modules/repositories/module-repository-documentdb.js +6 -51
- package/modules/repositories/module-repository-interface.js +2 -19
- package/modules/repositories/module-repository-mongo.js +74 -67
- package/modules/repositories/module-repository-postgres.js +74 -66
- package/modules/repositories/module-repository.js +1 -2
- package/modules/requester/requester.js +2 -27
- package/modules/use-cases/get-entities-for-user.js +13 -26
- package/modules/use-cases/get-entity-options-by-id.js +4 -10
- package/modules/use-cases/get-entity-options-by-type.js +1 -1
- package/modules/use-cases/get-module-instance-from-type.js +1 -1
- package/modules/use-cases/get-module.js +4 -10
- package/modules/use-cases/process-authorization-callback.js +19 -53
- package/modules/use-cases/refresh-entity-options.js +4 -10
- package/modules/use-cases/test-module-auth.js +4 -10
- package/package.json +5 -6
- package/prisma-mongodb/schema.prisma +0 -92
- package/prisma-postgresql/schema.prisma +0 -87
- package/queues/index.js +0 -25
- package/types/index.d.ts +0 -2
- package/user/repositories/user-repository-documentdb.js +2 -27
- package/user/repositories/user-repository-interface.js +0 -18
- package/user/repositories/user-repository-mongo.js +0 -24
- package/user/repositories/user-repository-postgres.js +0 -18
- package/user/use-cases/authenticate-user.js +1 -7
- package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
- package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
- package/user/user.js +1 -64
- package/credential/use-cases/credential-operations-for-user.js +0 -202
- package/credential/use-cases/reauthorize-credential-for-user.js +0 -73
- package/database/organization-where.js +0 -18
- package/database/unique-violation.js +0 -25
- package/extensions/bootstrap-runner.js +0 -43
- package/extensions/extension-loader.js +0 -187
- package/extensions/index.js +0 -41
- package/extensions/initialize-app.js +0 -123
- package/extensions/route-mounter.js +0 -103
- package/extensions/schema-composer.js +0 -188
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -216
- package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
- package/integrations/repositories/integration-not-found.js +0 -14
- package/integrations/schedule-config.js +0 -80
- package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
- package/integrations/use-cases/integration-operations-for-user.js +0 -256
- package/integrations/use-cases/resolve-integration-ownership.js +0 -177
- package/management-api/create-management-api-router.js +0 -49
- package/management-api/error-response.js +0 -116
- package/management-api/management-api-config.js +0 -151
- package/management-api/meta-router.js +0 -76
- package/management-api/openapi/build-openapi.js +0 -144
- package/management-api/openapi/components.js +0 -408
- package/management-api/openapi/v1-operations.js +0 -83
- package/management-api/openapi/v2-operations.js +0 -264
- package/management-api/route-registry.js +0 -411
- package/management-api/test-support/http-client.js +0 -39
- package/management-api/use-cases/get-management-api-meta.js +0 -57
- package/management-api/v1-deprecation.js +0 -120
- package/management-api/v2/create-v2-router.js +0 -85
- package/management-api/v2/dependencies.js +0 -281
- package/management-api/v2/dto.js +0 -143
- package/management-api/v2/handlers/authorization.js +0 -94
- package/management-api/v2/handlers/credentials.js +0 -33
- package/management-api/v2/handlers/entities.js +0 -77
- package/management-api/v2/handlers/integrations.js +0 -128
- package/management-api/v2/handlers/proxy.js +0 -34
- package/management-api/v2/request.js +0 -85
- package/management-api/v2/test-support/fake-dependencies.js +0 -103
- package/modules/domain/authorization-session.js +0 -144
- package/modules/entity-scope.js +0 -15
- package/modules/entity-type-registry.js +0 -311
- package/modules/proxy/proxy-errors.js +0 -20
- package/modules/proxy/proxy-path.js +0 -179
- package/modules/proxy/proxy-policy.js +0 -248
- package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
- package/modules/repositories/authorization-session-repository-factory.js +0 -31
- package/modules/repositories/authorization-session-repository-interface.js +0 -47
- package/modules/repositories/authorization-session-repository-prisma.js +0 -87
- package/modules/repositories/entity-not-found.js +0 -18
- package/modules/requester/proxy-request.js +0 -244
- package/modules/scope-config.js +0 -192
- package/modules/scope-policy.js +0 -112
- package/modules/scope-resolution.js +0 -288
- package/modules/scope-visibility.js +0 -247
- package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
- package/modules/tests/doubles/ownership-matching.js +0 -18
- package/modules/use-cases/authorize-entity-for-user.js +0 -61
- package/modules/use-cases/create-entity-from-credential.js +0 -113
- package/modules/use-cases/entity-operations-for-user.js +0 -162
- package/modules/use-cases/entity-types.js +0 -139
- package/modules/use-cases/execute-entity-proxy-request.js +0 -286
- package/modules/use-cases/get-authorization-requirements.js +0 -100
- package/modules/use-cases/get-entity-module-for-user.js +0 -113
- package/modules/use-cases/manage-access.js +0 -21
- package/modules/use-cases/multi-step-authorization.js +0 -329
- package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
- package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
- package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
- package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
- package/providers/index.js +0 -23
- package/providers/resolve-provider.js +0 -93
- package/providers/runtime-detection.js +0 -124
- package/queues/providers/index.js +0 -11
- package/queues/providers/netlify-background-provider.js +0 -150
- package/queues/providers/qstash-queue-provider.js +0 -219
- package/queues/providers/sqs-queue-provider.js +0 -70
- package/queues/queue-client-interface.js +0 -55
- package/queues/queue-message-auth.js +0 -193
- package/queues/queue-provider-factory.js +0 -101
- package/queues/queue-provider.js +0 -65
- package/queues/sqs-queue-client.js +0 -81
- package/types/modules/index.d.ts +0 -197
- package/types/user/index.d.ts +0 -134
- package/user/adopter-jwt-config.js +0 -177
- package/user/adopter-jwt-errors.js +0 -59
- package/user/adopter-token-verifier.js +0 -122
- package/user/app-user-helpers.js +0 -97
- package/user/create-user-authentication.js +0 -64
- package/user/membership.js +0 -51
- package/user/repositories/organization-membership-record.js +0 -19
- package/user/repositories/organization-membership-repository-documentdb.js +0 -106
- package/user/repositories/organization-membership-repository-factory.js +0 -42
- package/user/repositories/organization-membership-repository-interface.js +0 -57
- package/user/repositories/organization-membership-repository-mongo.js +0 -13
- package/user/repositories/organization-membership-repository-postgres.js +0 -29
- package/user/repositories/organization-membership-repository-prisma.js +0 -81
- package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
- package/user/use-cases/ensure-organization-membership.js +0 -92
- package/user/use-cases/find-individual-users-by-organization.js +0 -54
|
@@ -2,17 +2,9 @@
|
|
|
2
2
|
const {
|
|
3
3
|
mapIntegrationClassToIntegrationDTO,
|
|
4
4
|
} = require('../utils/map-integration-dto');
|
|
5
|
-
const {
|
|
6
|
-
describeRecord,
|
|
7
|
-
visibilityClauses,
|
|
8
|
-
matchesListFilters,
|
|
9
|
-
} = require('../../modules/scope-visibility');
|
|
10
|
-
const { DEFAULT_SCOPE_POLICY } = require('../../modules/scope-policy');
|
|
11
5
|
|
|
12
6
|
/**
|
|
13
|
-
* Use case for retrieving
|
|
14
|
-
* or their active organization owns, created in that organization (ADR-056
|
|
15
|
-
* §3.6), each with `tenancy` ({scope, owner, organizationId, access}).
|
|
7
|
+
* Use case for retrieving all integrations for a specific user.
|
|
16
8
|
* @class GetIntegrationsForUser
|
|
17
9
|
*/
|
|
18
10
|
class GetIntegrationsForUser {
|
|
@@ -23,16 +15,13 @@ class GetIntegrationsForUser {
|
|
|
23
15
|
* @param {Array<import('../integration').Integration>} params.integrationClasses - Array of available integration classes.
|
|
24
16
|
* @param {import('../../modules/module-factory').ModuleFactory} params.moduleFactory - Service for module instantiation and management.
|
|
25
17
|
* @param {import('../../modules/repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository - Repository for module and entity data operations.
|
|
26
|
-
* @param {Object} [params.scopePolicy] - from createScopePolicy
|
|
27
18
|
*/
|
|
28
19
|
constructor({
|
|
29
20
|
integrationRepository,
|
|
30
21
|
integrationClasses,
|
|
31
22
|
moduleFactory,
|
|
32
23
|
moduleRepository,
|
|
33
|
-
scopePolicy,
|
|
34
24
|
}) {
|
|
35
|
-
this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
|
|
36
25
|
/**
|
|
37
26
|
* @type {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface}
|
|
38
27
|
*/
|
|
@@ -43,86 +32,57 @@ class GetIntegrationsForUser {
|
|
|
43
32
|
}
|
|
44
33
|
|
|
45
34
|
/**
|
|
35
|
+
* Executes the retrieval of all integrations for a user.
|
|
46
36
|
* @async
|
|
47
|
-
* @param {
|
|
48
|
-
* @
|
|
49
|
-
* @returns {Promise<Object[]>} Array of integration DTOs.
|
|
37
|
+
* @param {string} userId - ID of the user whose integrations to retrieve.
|
|
38
|
+
* @returns {Promise<Object[]>} Array of integration DTOs for the specified user.
|
|
50
39
|
*/
|
|
51
|
-
async execute(
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
visibilityClauses(ctx, 'integration')
|
|
55
|
-
);
|
|
40
|
+
async execute(userId) {
|
|
41
|
+
const integrationRecords =
|
|
42
|
+
await this.integrationRepository.findIntegrationsByUserId(userId);
|
|
56
43
|
|
|
57
44
|
const integrations = [];
|
|
58
|
-
for (const record of records) {
|
|
59
|
-
// The query is the filter; the same rule again means a repository
|
|
60
|
-
// that returns too much cannot widen the list.
|
|
61
|
-
const tenancy = describeRecord(record, ctx, 'integration');
|
|
62
|
-
if (!tenancy || !matchesListFilters(tenancy, filters)) continue;
|
|
63
|
-
integrations.push({ ...(await this.toDto(record)), tenancy });
|
|
64
|
-
}
|
|
65
|
-
return integrations;
|
|
66
|
-
}
|
|
67
45
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const integrationClass = this.integrationClasses.find(
|
|
75
|
-
(integrationClass) =>
|
|
76
|
-
integrationClass.Definition.name ===
|
|
77
|
-
integrationRecord.config.type
|
|
78
|
-
);
|
|
46
|
+
for (const integrationRecord of integrationRecords) {
|
|
47
|
+
const entities = await this.moduleRepository.findEntitiesByIds(
|
|
48
|
+
integrationRecord.entitiesIds
|
|
49
|
+
);
|
|
79
50
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
integrationRecord.userId
|
|
51
|
+
const integrationClass = this.integrationClasses.find(
|
|
52
|
+
(integrationClass) =>
|
|
53
|
+
integrationClass.Definition.name ===
|
|
54
|
+
integrationRecord.config.type
|
|
85
55
|
);
|
|
86
|
-
modules.push(moduleInstance);
|
|
87
|
-
}
|
|
88
56
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
options: integrationClass.getOptionDetails(),
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
57
|
+
const modules = [];
|
|
58
|
+
for (const entity of entities) {
|
|
59
|
+
const moduleInstance =
|
|
60
|
+
await this.moduleFactory.getModuleInstance(
|
|
61
|
+
entity.id,
|
|
62
|
+
integrationRecord.userId
|
|
63
|
+
);
|
|
64
|
+
modules.push(moduleInstance);
|
|
65
|
+
}
|
|
102
66
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
67
|
+
const integrationData = {
|
|
68
|
+
id: integrationRecord.id,
|
|
69
|
+
userId: integrationRecord.userId,
|
|
70
|
+
entities: entities,
|
|
71
|
+
config: integrationRecord.config,
|
|
72
|
+
status: integrationRecord.status,
|
|
73
|
+
version: integrationRecord.version,
|
|
74
|
+
messages: integrationRecord.messages || { errors: [], warnings: [] },
|
|
75
|
+
modules,
|
|
76
|
+
options: integrationClass.getOptionDetails(),
|
|
77
|
+
};
|
|
114
78
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
userId
|
|
119
|
-
);
|
|
120
|
-
const integrations = [];
|
|
121
|
-
for (const record of records) {
|
|
122
|
-
integrations.push(await this.hydrator.toDto(record));
|
|
79
|
+
integrations.push(
|
|
80
|
+
mapIntegrationClassToIntegrationDTO(integrationData)
|
|
81
|
+
);
|
|
123
82
|
}
|
|
83
|
+
|
|
124
84
|
return integrations;
|
|
125
85
|
}
|
|
126
86
|
}
|
|
127
87
|
|
|
128
|
-
module.exports = { GetIntegrationsForUser
|
|
88
|
+
module.exports = { GetIntegrationsForUser };
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
const {
|
|
2
|
-
GetIntegrationsForUser,
|
|
3
|
-
GetIntegrationsByUserId,
|
|
4
|
-
} = require('./get-integrations-for-user');
|
|
1
|
+
const { GetIntegrationsForUser } = require('./get-integrations-for-user');
|
|
5
2
|
const { DeleteIntegrationForUser } = require('./delete-integration-for-user');
|
|
6
3
|
const { CreateIntegration } = require('./create-integration');
|
|
7
4
|
const { GetIntegration } = require('./get-integration');
|
|
@@ -9,11 +6,9 @@ const { CreateProcess } = require('./create-process');
|
|
|
9
6
|
const { UpdateProcessState } = require('./update-process-state');
|
|
10
7
|
const { UpdateProcessMetrics } = require('./update-process-metrics');
|
|
11
8
|
const { GetProcess } = require('./get-process');
|
|
12
|
-
const { DispatchOngoingSyncs } = require('./dispatch-ongoing-syncs');
|
|
13
9
|
|
|
14
10
|
module.exports = {
|
|
15
11
|
GetIntegrationsForUser,
|
|
16
|
-
GetIntegrationsByUserId,
|
|
17
12
|
DeleteIntegrationForUser,
|
|
18
13
|
CreateIntegration,
|
|
19
14
|
GetIntegration,
|
|
@@ -21,5 +16,4 @@ module.exports = {
|
|
|
21
16
|
UpdateProcessState,
|
|
22
17
|
UpdateProcessMetrics,
|
|
23
18
|
GetProcess,
|
|
24
|
-
DispatchOngoingSyncs,
|
|
25
19
|
};
|
|
@@ -19,48 +19,19 @@ function mapIntegrationClassToIntegrationDTO(integration) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
let deprecationLogged = false;
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* @param {Array<Function>} integrationClasses - integration classes
|
|
37
|
-
* @param {Object} [entities] - `appDefinition.entities` (ADR-059)
|
|
38
|
-
* @returns {Array<Object>} API module definitions
|
|
39
|
-
*/
|
|
40
|
-
function getModulesDefinitionFromIntegrationClasses(
|
|
41
|
-
integrationClasses,
|
|
42
|
-
entities
|
|
43
|
-
) {
|
|
44
|
-
// Required here, not at the top: modules/ must not load at require time
|
|
45
|
-
// of every integration use case that only wants the DTO mapper.
|
|
46
|
-
const {
|
|
47
|
-
getEntityTypeRegistry,
|
|
48
|
-
getEntityTypeDefinitions,
|
|
49
|
-
} = require('../../modules/entity-type-registry');
|
|
50
|
-
if (!deprecationLogged) {
|
|
51
|
-
deprecationLogged = true;
|
|
52
|
-
const { getLogger } = require('../../logs');
|
|
53
|
-
getLogger('frigg.integrations').warn(
|
|
54
|
-
'getModulesDefinitionFromIntegrationClasses is deprecated; use getEntityTypeRegistry (ADR-059)',
|
|
55
|
-
{ eventName: 'frigg.integrations.deprecated_module_definitions' }
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
return getEntityTypeDefinitions(
|
|
59
|
-
getEntityTypeRegistry({ integrations: integrationClasses, entities })
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
module.exports = {
|
|
64
|
-
mapIntegrationClassToIntegrationDTO,
|
|
65
|
-
getModulesDefinitionFromIntegrationClasses,
|
|
23
|
+
const getModulesDefinitionFromIntegrationClasses = (integrationClasses) => {
|
|
24
|
+
return [
|
|
25
|
+
...new Set(
|
|
26
|
+
integrationClasses
|
|
27
|
+
.map((integration) =>
|
|
28
|
+
Object.values(integration.Definition.modules).map(
|
|
29
|
+
(module) => module.definition
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
.flat()
|
|
33
|
+
),
|
|
34
|
+
];
|
|
66
35
|
};
|
|
36
|
+
|
|
37
|
+
module.exports = { mapIntegrationClassToIntegrationDTO, getModulesDefinitionFromIntegrationClasses };
|
package/modules/index.js
CHANGED
|
@@ -4,15 +4,6 @@ const { OAuth2Requester } = require('./requester/oauth-2');
|
|
|
4
4
|
const { Requester } = require('./requester/requester');
|
|
5
5
|
const { ModuleConstants } = require('./ModuleConstants');
|
|
6
6
|
const { ModuleFactory } = require('./module-factory');
|
|
7
|
-
const {
|
|
8
|
-
getEntityTypeRegistry,
|
|
9
|
-
EntityRegistryError,
|
|
10
|
-
} = require('./entity-type-registry');
|
|
11
|
-
const {
|
|
12
|
-
resolveScope,
|
|
13
|
-
listBindingScopes,
|
|
14
|
-
OwnershipConfigError,
|
|
15
|
-
} = require('./entity-scope');
|
|
16
7
|
|
|
17
8
|
module.exports = {
|
|
18
9
|
ApiKeyRequester,
|
|
@@ -21,9 +12,4 @@ module.exports = {
|
|
|
21
12
|
Requester,
|
|
22
13
|
ModuleConstants,
|
|
23
14
|
ModuleFactory,
|
|
24
|
-
getEntityTypeRegistry,
|
|
25
|
-
EntityRegistryError,
|
|
26
|
-
resolveScope,
|
|
27
|
-
listBindingScopes,
|
|
28
|
-
OwnershipConfigError,
|
|
29
15
|
};
|
|
@@ -45,18 +45,6 @@ class ModuleFactory {
|
|
|
45
45
|
);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
// definition.env may be an async function (e.g. the db-credentials
|
|
49
|
-
// extension's bootstrap loads OAuth app credentials from the
|
|
50
|
-
// database); resolve it first. A static env keeps the plain
|
|
51
|
-
// constructor, exactly as before.
|
|
52
|
-
if (typeof moduleDefinition.env === 'function') {
|
|
53
|
-
return await Module.create({
|
|
54
|
-
userId,
|
|
55
|
-
entity,
|
|
56
|
-
definition: moduleDefinition,
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
48
|
return new Module({
|
|
61
49
|
userId,
|
|
62
50
|
entity,
|
package/modules/module.js
CHANGED
|
@@ -21,17 +21,8 @@ class Module extends Delegate {
|
|
|
21
21
|
* @param {string} params.userId The user id
|
|
22
22
|
* @param {Object} params.entity The entity record from the database
|
|
23
23
|
* @param {string} [params.state] Optional OAuth state value forwarded to the API client (round-trips through the OAuth provider).
|
|
24
|
-
* @param {Object} [params.resolvedEnv] definition.env as Module.create() resolved it (may be undefined).
|
|
25
|
-
* @param {boolean} [params.envResolved=false] true when resolvedEnv carries the resolved env, even an undefined one; otherwise the static definition.env is used.
|
|
26
24
|
*/
|
|
27
|
-
constructor({
|
|
28
|
-
definition,
|
|
29
|
-
userId = null,
|
|
30
|
-
entity: entityObj = null,
|
|
31
|
-
state = null,
|
|
32
|
-
resolvedEnv,
|
|
33
|
-
envResolved = false,
|
|
34
|
-
}) {
|
|
25
|
+
constructor({ definition, userId = null, entity: entityObj = null, state = null }) {
|
|
35
26
|
super({ definition, userId, entity: entityObj });
|
|
36
27
|
|
|
37
28
|
this.validateDefinition(definition);
|
|
@@ -63,23 +54,8 @@ class Module extends Delegate {
|
|
|
63
54
|
|
|
64
55
|
Object.assign(this, this.definition.requiredAuthMethods);
|
|
65
56
|
|
|
66
|
-
// A pre-resolved env (Module.create()) supports definition.env as an
|
|
67
|
-
// async function, e.g. OAuth app credentials loaded from the database
|
|
68
|
-
// by the db-credentials extension; otherwise the static object.
|
|
69
|
-
// "Resolved" is an explicit flag, not a non-null value: a function env
|
|
70
|
-
// may legitimately resolve to undefined (no static env, no DB row),
|
|
71
|
-
// and falling back to definition.env would then read the function.
|
|
72
|
-
const envValues = envResolved ? resolvedEnv : this.definition.env;
|
|
73
|
-
if (typeof envValues === 'function') {
|
|
74
|
-
// Spreading a function yields no keys, so the API client would
|
|
75
|
-
// silently get no client_id / client_secret.
|
|
76
|
-
throw new TypeError(
|
|
77
|
-
`Module ${this.name}: definition.env is a function; build the module with await Module.create(...)`
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
57
|
const apiParams = {
|
|
82
|
-
...
|
|
58
|
+
...this.definition.env,
|
|
83
59
|
delegate: this,
|
|
84
60
|
logger: this.logger,
|
|
85
61
|
...(state ? { state } : {}),
|
|
@@ -91,33 +67,6 @@ class Module extends Delegate {
|
|
|
91
67
|
this.api = new this.apiClass(apiParams);
|
|
92
68
|
}
|
|
93
69
|
|
|
94
|
-
/**
|
|
95
|
-
* Resolves definition.env, supporting both a static object and a
|
|
96
|
-
* (possibly async) function.
|
|
97
|
-
*
|
|
98
|
-
* @param {Object} definition - Module definition
|
|
99
|
-
* @returns {Promise<Object|undefined>} Resolved env values
|
|
100
|
-
*/
|
|
101
|
-
static async resolveEnv(definition) {
|
|
102
|
-
if (typeof definition.env === 'function') {
|
|
103
|
-
return await definition.env();
|
|
104
|
-
}
|
|
105
|
-
return definition.env;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Async factory that resolves definition.env first, so a module whose
|
|
110
|
-
* env is a function gets its values. Prefer it over `new Module(...)`
|
|
111
|
-
* wherever the call site can await.
|
|
112
|
-
*
|
|
113
|
-
* @param {Object} params - same as the constructor, minus resolvedEnv
|
|
114
|
-
* @returns {Promise<Module>}
|
|
115
|
-
*/
|
|
116
|
-
static async create({ definition, userId = null, entity = null, state = null }) {
|
|
117
|
-
const resolvedEnv = await Module.resolveEnv(definition);
|
|
118
|
-
return new Module({ definition, userId, entity, state, resolvedEnv, envResolved: true });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
70
|
getName() {
|
|
122
71
|
return this.name;
|
|
123
72
|
}
|
|
@@ -3,14 +3,12 @@ const {
|
|
|
3
3
|
toObjectId,
|
|
4
4
|
fromObjectId,
|
|
5
5
|
findMany,
|
|
6
|
-
findManyDrained,
|
|
7
6
|
findOne,
|
|
8
7
|
insertOne,
|
|
9
8
|
updateOne,
|
|
10
9
|
deleteOne,
|
|
11
10
|
} = require('../../database/documentdb-utils');
|
|
12
11
|
const { ModuleRepositoryInterface } = require('./module-repository-interface');
|
|
13
|
-
const { entityNotFoundError } = require('./entity-not-found');
|
|
14
12
|
const { DocumentDBEncryptionService } = require('../../database/documentdb-encryption-service');
|
|
15
13
|
|
|
16
14
|
/**
|
|
@@ -35,11 +33,11 @@ class ModuleRepositoryDocumentDB extends ModuleRepositoryInterface {
|
|
|
35
33
|
async findEntityById(entityId) {
|
|
36
34
|
const objectId = toObjectId(entityId);
|
|
37
35
|
if (!objectId) {
|
|
38
|
-
throw
|
|
36
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
39
37
|
}
|
|
40
38
|
const doc = await findOne(this.prisma, 'Entity', { _id: objectId });
|
|
41
39
|
if (!doc) {
|
|
42
|
-
throw
|
|
40
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
43
41
|
}
|
|
44
42
|
const credential = await this._fetchCredential(doc.credentialId);
|
|
45
43
|
return this._mapEntity(doc, credential);
|
|
@@ -78,29 +76,6 @@ class ModuleRepositoryDocumentDB extends ModuleRepositoryInterface {
|
|
|
78
76
|
return docs.map((doc) => this._mapEntity(doc, credentialMap.get(fromObjectId(doc.credentialId)) || null));
|
|
79
77
|
}
|
|
80
78
|
|
|
81
|
-
/**
|
|
82
|
-
* Find entities matching any of the owner/organization pairs, in one query.
|
|
83
|
-
* `organizationId: null` matches rows with no organization (a raw MongoDB
|
|
84
|
-
* `null` also matches a missing field). The caller (a use case) decides
|
|
85
|
-
* which pairs; this only runs them.
|
|
86
|
-
*
|
|
87
|
-
* @param {Array<{userId: string, organizationId: (string|null)}>} clauses
|
|
88
|
-
* @returns {Promise<Array>}
|
|
89
|
-
*/
|
|
90
|
-
async findEntitiesByOwnership(clauses) {
|
|
91
|
-
if (!Array.isArray(clauses) || clauses.length === 0) return [];
|
|
92
|
-
// Drained: a list can hold more than the first cursor batch (~101).
|
|
93
|
-
const docs = await findManyDrained(this.prisma, 'Entity', {
|
|
94
|
-
$or: clauses.map(({ userId, organizationId }) => ({
|
|
95
|
-
userId: toObjectId(userId),
|
|
96
|
-
organizationId:
|
|
97
|
-
organizationId === null ? null : toObjectId(organizationId),
|
|
98
|
-
})),
|
|
99
|
-
});
|
|
100
|
-
const credentialMap = await this._fetchCredentialsBulk(docs.map((doc) => doc.credentialId));
|
|
101
|
-
return docs.map((doc) => this._mapEntity(doc, credentialMap.get(fromObjectId(doc.credentialId)) || null));
|
|
102
|
-
}
|
|
103
|
-
|
|
104
79
|
async unsetCredential(entityId) {
|
|
105
80
|
const objectId = toObjectId(entityId);
|
|
106
81
|
if (!objectId) return false;
|
|
@@ -140,14 +115,7 @@ class ModuleRepositoryDocumentDB extends ModuleRepositoryInterface {
|
|
|
140
115
|
);
|
|
141
116
|
}
|
|
142
117
|
|
|
143
|
-
|
|
144
|
-
* @param {Object} entityData
|
|
145
|
-
* @param {{organizationId?: (string|null)}} [tenancy] - the organization the entity
|
|
146
|
-
* belongs to (ADR-056 §3.6). A separate argument, not a field of `entityData`,
|
|
147
|
-
* because `entityData` carries provider fields and one named `organizationId`
|
|
148
|
-
* must not be taken for ours.
|
|
149
|
-
*/
|
|
150
|
-
async createEntity(entityData, tenancy = {}) {
|
|
118
|
+
async createEntity(entityData) {
|
|
151
119
|
const {
|
|
152
120
|
user,
|
|
153
121
|
userId,
|
|
@@ -165,7 +133,6 @@ class ModuleRepositoryDocumentDB extends ModuleRepositoryInterface {
|
|
|
165
133
|
name: name ?? null,
|
|
166
134
|
moduleName: moduleName ?? null,
|
|
167
135
|
externalId: externalId ?? null,
|
|
168
|
-
organizationId: toObjectId(tenancy.organizationId) || null,
|
|
169
136
|
data: dynamicData,
|
|
170
137
|
};
|
|
171
138
|
const insertedId = await insertOne(this.prisma, 'Entity', document);
|
|
@@ -280,9 +247,8 @@ class ModuleRepositoryDocumentDB extends ModuleRepositoryInterface {
|
|
|
280
247
|
const objectIds = ids.map(id => toObjectId(id)).filter(Boolean);
|
|
281
248
|
if (objectIds.length === 0) return new Map();
|
|
282
249
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
const rawCredentials = await findManyDrained(this.prisma, 'Credential', {
|
|
250
|
+
// Use raw findMany to bypass Prisma encryption extension
|
|
251
|
+
const rawCredentials = await findMany(this.prisma, 'Credential', {
|
|
286
252
|
_id: { $in: objectIds }
|
|
287
253
|
});
|
|
288
254
|
|
|
@@ -363,30 +329,19 @@ class ModuleRepositoryDocumentDB extends ModuleRepositoryInterface {
|
|
|
363
329
|
if (filter.name) query.name = filter.name;
|
|
364
330
|
if (filter.moduleName) query.moduleName = filter.moduleName;
|
|
365
331
|
if (filter.externalId) query.externalId = filter.externalId;
|
|
366
|
-
// `null` means "no organization"; absent means "any". A raw MongoDB
|
|
367
|
-
// `{ field: null }` matches both a null value and a missing field.
|
|
368
|
-
if (filter.organizationId !== undefined) {
|
|
369
|
-
query.organizationId =
|
|
370
|
-
filter.organizationId === null
|
|
371
|
-
? null
|
|
372
|
-
: toObjectId(filter.organizationId);
|
|
373
|
-
}
|
|
374
332
|
return query;
|
|
375
333
|
}
|
|
376
334
|
|
|
377
335
|
_mapEntity(doc, credential) {
|
|
378
336
|
const dynamicData = doc?.data || {};
|
|
379
337
|
return {
|
|
380
|
-
// First, so no stored provider field can replace a column below
|
|
381
|
-
// (identity, owner or organization decide who sees the entity).
|
|
382
|
-
...dynamicData,
|
|
383
338
|
id: fromObjectId(doc?._id),
|
|
384
339
|
credential,
|
|
385
340
|
userId: fromObjectId(doc?.userId),
|
|
386
341
|
name: doc?.name ?? null,
|
|
387
342
|
externalId: doc?.externalId ?? null,
|
|
388
343
|
moduleName: doc?.moduleName ?? null,
|
|
389
|
-
|
|
344
|
+
...dynamicData,
|
|
390
345
|
};
|
|
391
346
|
}
|
|
392
347
|
}
|
|
@@ -67,21 +67,6 @@ class ModuleRepositoryInterface {
|
|
|
67
67
|
);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/**
|
|
71
|
-
* Find entities matching any of the owner/organization pairs, in one query
|
|
72
|
-
* (ADR-056 §3.6). `organizationId: null` matches rows with no organization.
|
|
73
|
-
* Which pairs is decided by the caller (modules/entity-scope.js), not here.
|
|
74
|
-
*
|
|
75
|
-
* @param {Array<{userId: string|number, organizationId: (string|number|null)}>} clauses
|
|
76
|
-
* @returns {Promise<Array>} Array of entity objects
|
|
77
|
-
* @abstract
|
|
78
|
-
*/
|
|
79
|
-
async findEntitiesByOwnership(clauses) {
|
|
80
|
-
throw new Error(
|
|
81
|
-
'Method findEntitiesByOwnership must be implemented by subclass'
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
70
|
/**
|
|
86
71
|
* Unset credential from entity
|
|
87
72
|
*
|
|
@@ -125,13 +110,11 @@ class ModuleRepositoryInterface {
|
|
|
125
110
|
/**
|
|
126
111
|
* Create a new entity
|
|
127
112
|
*
|
|
128
|
-
* @param {Object} entityData - Entity data
|
|
129
|
-
* @param {{organizationId?: (string|number|null)}} [tenancy] - the organization
|
|
130
|
-
* the entity belongs to (ADR-056 §3.6), kept apart from `entityData`
|
|
113
|
+
* @param {Object} entityData - Entity data
|
|
131
114
|
* @returns {Promise<Object>} Created entity object
|
|
132
115
|
* @abstract
|
|
133
116
|
*/
|
|
134
|
-
async createEntity(entityData
|
|
117
|
+
async createEntity(entityData) {
|
|
135
118
|
throw new Error('Method createEntity must be implemented by subclass');
|
|
136
119
|
}
|
|
137
120
|
|