@friggframework/core 2.0.0--canary.685.a13fdab.0 → 2.0.0--canary.679.a2bcc2f.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 -25
- package/README.md +0 -105
- package/application/commands/integration-commands.js +2 -2
- package/application/commands/user-commands.js +5 -17
- package/credential/use-cases/credential-operations-for-user.js +25 -110
- package/credential/use-cases/reauthorize-credential-for-user.js +8 -15
- package/database/documentdb-utils.js +1 -6
- package/generated/prisma-mongodb/edge.js +4 -15
- package/generated/prisma-mongodb/index-browser.js +1 -12
- package/generated/prisma-mongodb/index.d.ts +86 -2027
- package/generated/prisma-mongodb/index.js +4 -15
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -47
- package/generated/prisma-mongodb/wasm.js +4 -15
- package/generated/prisma-postgresql/edge.js +4 -15
- package/generated/prisma-postgresql/index-browser.js +1 -12
- package/generated/prisma-postgresql/index.d.ts +63 -2162
- package/generated/prisma-postgresql/index.js +4 -15
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -44
- package/generated/prisma-postgresql/wasm.js +4 -15
- package/handlers/app-definition-loader.js +1 -24
- package/index.js +0 -22
- package/integrations/integration-router.js +73 -115
- 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 -5
- package/integrations/use-cases/integration-operations-for-user.js +24 -55
- package/management-api/management-api-config.js +2 -71
- package/management-api/openapi/components.js +0 -40
- package/management-api/openapi/v2-operations.js +4 -16
- package/management-api/v2/dependencies.js +15 -40
- package/management-api/v2/dto.js +0 -20
- package/management-api/v2/handlers/authorization.js +0 -1
- package/management-api/v2/handlers/credentials.js +2 -6
- package/management-api/v2/handlers/entities.js +7 -10
- package/management-api/v2/handlers/integrations.js +3 -6
- package/management-api/v2/request.js +1 -32
- package/modules/index.js +0 -8
- 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/use-cases/entity-operations-for-user.js +13 -50
- package/modules/use-cases/entity-types.js +7 -31
- package/modules/use-cases/execute-entity-proxy-request.js +3 -50
- package/modules/use-cases/get-entities-for-user.js +5 -16
- package/modules/use-cases/get-entity-module-for-user.js +5 -32
- package/modules/use-cases/get-entity-options-by-id.js +3 -9
- package/modules/use-cases/get-module.js +3 -9
- package/modules/use-cases/multi-step-authorization.js +12 -39
- package/modules/use-cases/process-authorization-callback.js +18 -52
- package/modules/use-cases/refresh-entity-options.js +3 -9
- package/modules/use-cases/test-module-auth.js +3 -9
- package/package.json +5 -6
- package/prisma-mongodb/schema.prisma +0 -47
- package/prisma-postgresql/schema.prisma +0 -44
- package/types/index.d.ts +0 -1
- package/types/modules/index.d.ts +0 -129
- 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/database/organization-where.js +0 -18
- package/database/unique-violation.js +0 -25
- package/integrations/repositories/integration-not-found.js +0 -14
- package/integrations/use-cases/resolve-integration-ownership.js +0 -177
- package/modules/entity-scope.js +0 -15
- package/modules/repositories/entity-not-found.js +0 -18
- 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/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/manage-access.js +0 -21
- 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/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
|
@@ -29,18 +29,13 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
29
29
|
getEntityTypeRegistry,
|
|
30
30
|
getEntityTypeDefinitions,
|
|
31
31
|
} = require('../../modules/entity-type-registry');
|
|
32
|
-
const {
|
|
33
|
-
const {
|
|
34
|
-
|
|
35
|
-
} = require('../../modules/use-cases/authorize-entity-for-user');
|
|
36
|
-
const {
|
|
37
|
-
ResolveIntegrationOwnership,
|
|
38
|
-
} = require('../../integrations/use-cases/resolve-integration-ownership');
|
|
32
|
+
const { GetUserFromBearerToken } = require('../../user/use-cases/get-user-from-bearer-token');
|
|
33
|
+
const { GetUserFromXFriggHeaders } = require('../../user/use-cases/get-user-from-x-frigg-headers');
|
|
34
|
+
const { GetUserFromAdopterJwt } = require('../../user/use-cases/get-user-from-adopter-jwt');
|
|
39
35
|
const {
|
|
40
36
|
AuthenticateWithSharedSecret,
|
|
41
37
|
} = require('../../user/use-cases/authenticate-with-shared-secret');
|
|
42
38
|
const { AuthenticateUser } = require('../../user/use-cases/authenticate-user');
|
|
43
|
-
const { buildUserAuthentication } = require('../../user/create-user-authentication');
|
|
44
39
|
const { CreateIntegration } = require('../../integrations/use-cases/create-integration');
|
|
45
40
|
const { UpdateIntegration } = require('../../integrations/use-cases/update-integration');
|
|
46
41
|
const {
|
|
@@ -92,11 +87,6 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
92
87
|
appDefinition.entityTypeRegistry ??
|
|
93
88
|
getEntityTypeRegistry(appDefinition);
|
|
94
89
|
const moduleDefinitions = getEntityTypeDefinitions(registry);
|
|
95
|
-
// Ownership by scope (ADR-056 §3): one object every use case asks.
|
|
96
|
-
const scopePolicy = createScopePolicy({
|
|
97
|
-
ownership: appDefinition.ownership,
|
|
98
|
-
entityTypeRegistry: registry,
|
|
99
|
-
});
|
|
100
90
|
|
|
101
91
|
const moduleRepository = createModuleRepository();
|
|
102
92
|
const integrationRepository = createIntegrationRepository();
|
|
@@ -105,7 +95,9 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
105
95
|
const authorizationSessionRepository = createAuthorizationSessionRepository();
|
|
106
96
|
|
|
107
97
|
const authenticateUser = new AuthenticateUser({
|
|
108
|
-
|
|
98
|
+
getUserFromBearerToken: new GetUserFromBearerToken({ userRepository, userConfig }),
|
|
99
|
+
getUserFromXFriggHeaders: new GetUserFromXFriggHeaders({ userRepository, userConfig }),
|
|
100
|
+
getUserFromAdopterJwt: new GetUserFromAdopterJwt({ userRepository, userConfig }),
|
|
109
101
|
authenticateWithSharedSecret: new AuthenticateWithSharedSecret(),
|
|
110
102
|
userConfig,
|
|
111
103
|
});
|
|
@@ -119,12 +111,10 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
119
111
|
const getEntityModuleForUser = new GetEntityModuleForUser({
|
|
120
112
|
moduleRepository,
|
|
121
113
|
moduleDefinitions,
|
|
122
|
-
scopePolicy,
|
|
123
114
|
});
|
|
124
115
|
const getCredentialForUser = new credentialOps.GetCredentialForUserV2({
|
|
125
116
|
credentialRepository,
|
|
126
117
|
moduleRepository,
|
|
127
|
-
scopePolicy,
|
|
128
118
|
});
|
|
129
119
|
const getAuthorizationRequirements = new GetAuthorizationRequirements({
|
|
130
120
|
moduleDefinitions,
|
|
@@ -138,22 +128,17 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
138
128
|
moduleDefinitions,
|
|
139
129
|
getAuthorizationRequirements,
|
|
140
130
|
authorizationSessionRepository,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
integrationRepository,
|
|
147
|
-
moduleDefinitions,
|
|
148
|
-
}),
|
|
131
|
+
processAuthorizationCallback: new ProcessAuthorizationCallback({
|
|
132
|
+
moduleRepository,
|
|
133
|
+
credentialRepository,
|
|
134
|
+
integrationRepository,
|
|
135
|
+
moduleDefinitions,
|
|
149
136
|
}),
|
|
150
137
|
moduleRepository,
|
|
151
138
|
credentialRepository,
|
|
152
|
-
scopePolicy,
|
|
153
139
|
});
|
|
154
140
|
const getOwnedIntegrationRecord = new integrationOps.GetOwnedIntegrationRecord({
|
|
155
141
|
integrationRepository,
|
|
156
|
-
scopePolicy,
|
|
157
142
|
});
|
|
158
143
|
const getIntegrationInstanceForUser = new integrationOps.GetIntegrationInstanceForUser({
|
|
159
144
|
getOwnedIntegrationRecord,
|
|
@@ -163,7 +148,6 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
163
148
|
return {
|
|
164
149
|
config,
|
|
165
150
|
moduleDefinitions,
|
|
166
|
-
scopePolicy,
|
|
167
151
|
repositories: {
|
|
168
152
|
moduleRepository,
|
|
169
153
|
integrationRepository,
|
|
@@ -179,7 +163,6 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
179
163
|
integrationClasses,
|
|
180
164
|
moduleFactory,
|
|
181
165
|
moduleRepository,
|
|
182
|
-
scopePolicy,
|
|
183
166
|
}),
|
|
184
167
|
getPossibleIntegrations: new GetPossibleIntegrations({ integrationClasses }),
|
|
185
168
|
createIntegrationForUser: new integrationOps.CreateIntegrationForUser({
|
|
@@ -188,11 +171,7 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
188
171
|
integrationClasses,
|
|
189
172
|
moduleFactory,
|
|
190
173
|
}),
|
|
191
|
-
|
|
192
|
-
moduleRepository,
|
|
193
|
-
integrationClasses,
|
|
194
|
-
scopePolicy,
|
|
195
|
-
}),
|
|
174
|
+
getEntityModuleForUser,
|
|
196
175
|
integrationClasses,
|
|
197
176
|
}),
|
|
198
177
|
getIntegrationInstanceForUser,
|
|
@@ -224,10 +203,7 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
224
203
|
|
|
225
204
|
// Entities
|
|
226
205
|
getEntityModuleForUser,
|
|
227
|
-
listEntitiesForUser: new entityOps.ListEntitiesForUser({
|
|
228
|
-
moduleRepository,
|
|
229
|
-
scopePolicy,
|
|
230
|
-
}),
|
|
206
|
+
listEntitiesForUser: new entityOps.ListEntitiesForUser({ moduleRepository }),
|
|
231
207
|
getEntityForUser: new entityOps.GetEntityForUser({ getEntityModuleForUser }),
|
|
232
208
|
deleteEntityForUser: new entityOps.DeleteEntityForUser({
|
|
233
209
|
getEntityModuleForUser,
|
|
@@ -239,15 +215,14 @@ function buildV2Dependencies({ appDefinition, config, entityTypeRegistry }) {
|
|
|
239
215
|
refreshEntityOptionsForUser: new entityOps.RefreshEntityOptionsForUser({
|
|
240
216
|
getEntityModuleForUser,
|
|
241
217
|
}),
|
|
242
|
-
listEntityTypes: new ListEntityTypes({ entityTypeRegistry: registry
|
|
243
|
-
getEntityType: new GetEntityType({ entityTypeRegistry: registry
|
|
218
|
+
listEntityTypes: new ListEntityTypes({ entityTypeRegistry: registry }),
|
|
219
|
+
getEntityType: new GetEntityType({ entityTypeRegistry: registry }),
|
|
244
220
|
getAuthorizationRequirements,
|
|
245
221
|
|
|
246
222
|
// Credentials
|
|
247
223
|
listCredentialsForUser: new credentialOps.ListCredentialsForUser({
|
|
248
224
|
credentialRepository,
|
|
249
225
|
moduleRepository,
|
|
250
|
-
scopePolicy,
|
|
251
226
|
}),
|
|
252
227
|
getCredentialForUser,
|
|
253
228
|
deleteCredentialForUser: new credentialOps.DeleteCredentialForUser({
|
package/management-api/v2/dto.js
CHANGED
|
@@ -13,20 +13,6 @@ const idOf = (value) => {
|
|
|
13
13
|
return String(value);
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
* `{ scope, owner, organizationId, access }` (ADR-056 §3.4): whose a record is
|
|
18
|
-
* and what the caller may do with it. Empty when the use case did not say.
|
|
19
|
-
*/
|
|
20
|
-
function tenancyFields(tenancy) {
|
|
21
|
-
if (!tenancy) return {};
|
|
22
|
-
return {
|
|
23
|
-
scope: tenancy.scope,
|
|
24
|
-
owner: tenancy.owner,
|
|
25
|
-
organizationId: tenancy.organizationId ?? null,
|
|
26
|
-
access: tenancy.access,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
16
|
/** An entity (connected account). */
|
|
31
17
|
function toEntityDto(entity) {
|
|
32
18
|
const credential =
|
|
@@ -44,7 +30,6 @@ function toEntityDto(entity) {
|
|
|
44
30
|
typeof credential?.authIsValid === 'boolean'
|
|
45
31
|
? credential.authIsValid
|
|
46
32
|
: null,
|
|
47
|
-
...tenancyFields(entity.tenancy),
|
|
48
33
|
};
|
|
49
34
|
}
|
|
50
35
|
|
|
@@ -57,7 +42,6 @@ function toIntegrationDto(integration) {
|
|
|
57
42
|
config: integration.config ?? {},
|
|
58
43
|
version: integration.version ?? null,
|
|
59
44
|
messages: integration.messages ?? {},
|
|
60
|
-
...tenancyFields(integration.tenancy),
|
|
61
45
|
};
|
|
62
46
|
}
|
|
63
47
|
|
|
@@ -75,8 +59,6 @@ const CREDENTIAL_METADATA = new Set([
|
|
|
75
59
|
'type',
|
|
76
60
|
'entityIds',
|
|
77
61
|
'status',
|
|
78
|
-
// Added by the credential use cases (ADR-056 §3.4); never provider data.
|
|
79
|
-
'tenancy',
|
|
80
62
|
]);
|
|
81
63
|
|
|
82
64
|
/**
|
|
@@ -128,13 +110,11 @@ function maskCredential(credential) {
|
|
|
128
110
|
entityCount: entityIds.length,
|
|
129
111
|
createdAt: toIso(credential.createdAt),
|
|
130
112
|
updatedAt: toIso(credential.updatedAt),
|
|
131
|
-
...tenancyFields(credential.tenancy),
|
|
132
113
|
data,
|
|
133
114
|
};
|
|
134
115
|
}
|
|
135
116
|
|
|
136
117
|
module.exports = {
|
|
137
|
-
tenancyFields,
|
|
138
118
|
toEntityDto,
|
|
139
119
|
toIntegrationDto,
|
|
140
120
|
maskCredential,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const { maskCredential } = require('../dto');
|
|
2
|
-
const { parseListFilters } = require('../request');
|
|
3
2
|
|
|
4
3
|
/** Management API v2 credential handlers, keyed by operationId. */
|
|
5
4
|
function createCredentialHandlers(useCases) {
|
|
@@ -7,11 +6,8 @@ function createCredentialHandlers(useCases) {
|
|
|
7
6
|
useCases;
|
|
8
7
|
|
|
9
8
|
return {
|
|
10
|
-
async listCredentials(
|
|
11
|
-
const credentials = await listCredentialsForUser.execute(
|
|
12
|
-
user,
|
|
13
|
-
parseListFilters(req.query)
|
|
14
|
-
);
|
|
9
|
+
async listCredentials(_req, res, user) {
|
|
10
|
+
const credentials = await listCredentialsForUser.execute(user);
|
|
15
11
|
res.json({ credentials: credentials.map(maskCredential) });
|
|
16
12
|
},
|
|
17
13
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { toEntityDto } = require('../dto');
|
|
2
|
-
const { objectBody, parseStep
|
|
2
|
+
const { objectBody, parseStep } = require('../request');
|
|
3
3
|
|
|
4
4
|
/** Management API v2 entity and entity-type handlers, keyed by operationId. */
|
|
5
5
|
function createEntityHandlers(useCases) {
|
|
@@ -16,20 +16,17 @@ function createEntityHandlers(useCases) {
|
|
|
16
16
|
} = useCases;
|
|
17
17
|
|
|
18
18
|
return {
|
|
19
|
-
async listEntities(
|
|
20
|
-
const entities = await listEntitiesForUser.execute(
|
|
21
|
-
user,
|
|
22
|
-
parseListFilters(req.query)
|
|
23
|
-
);
|
|
19
|
+
async listEntities(_req, res, user) {
|
|
20
|
+
const entities = await listEntitiesForUser.execute(user);
|
|
24
21
|
res.json({ entities: entities.map(toEntityDto) });
|
|
25
22
|
},
|
|
26
23
|
|
|
27
|
-
async listEntityTypes(_req, res
|
|
28
|
-
res.json({ types: await listEntityTypes.execute(
|
|
24
|
+
async listEntityTypes(_req, res) {
|
|
25
|
+
res.json({ types: await listEntityTypes.execute() });
|
|
29
26
|
},
|
|
30
27
|
|
|
31
|
-
async getEntityType(req, res
|
|
32
|
-
res.json(await getEntityType.execute(req.params.entityType
|
|
28
|
+
async getEntityType(req, res) {
|
|
29
|
+
res.json(await getEntityType.execute(req.params.entityType));
|
|
33
30
|
},
|
|
34
31
|
|
|
35
32
|
// Describes a step's requirements without starting an authorization
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { toIntegrationDto } = require('../dto');
|
|
2
|
-
const { objectBody, optionalString
|
|
2
|
+
const { objectBody, optionalString } = require('../request');
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Management API v2 integration handlers, keyed by registry operationId.
|
|
@@ -27,11 +27,8 @@ function createIntegrationHandlers(useCases) {
|
|
|
27
27
|
);
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
|
-
async listIntegrations(
|
|
31
|
-
const integrations = await getIntegrationsForUser.execute(
|
|
32
|
-
user,
|
|
33
|
-
parseListFilters(req.query)
|
|
34
|
-
);
|
|
30
|
+
async listIntegrations(_req, res, user) {
|
|
31
|
+
const integrations = await getIntegrationsForUser.execute(user.getId());
|
|
35
32
|
res.json({ integrations: integrations.map(toIntegrationDto) });
|
|
36
33
|
},
|
|
37
34
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const Boom = require('@hapi/boom');
|
|
2
|
-
const { LIST_OWNERS } = require('../../modules/scope-visibility');
|
|
3
2
|
|
|
4
3
|
/** The JSON body as an object, or a 400 VALIDATION_ERROR. */
|
|
5
4
|
function objectBody(req) {
|
|
@@ -52,34 +51,4 @@ function optionalString(value, name) {
|
|
|
52
51
|
return value;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* `?scope=individual|organization` and `?owner=me|org` on a list (ADR-056
|
|
59
|
-
* §3.4). Anything else is a 400.
|
|
60
|
-
*/
|
|
61
|
-
function parseListFilters(query = {}) {
|
|
62
|
-
const scope = optionalString(query.scope, 'scope');
|
|
63
|
-
const owner = optionalString(query.owner, 'owner');
|
|
64
|
-
if (scope !== undefined && !LIST_SCOPES.includes(scope)) {
|
|
65
|
-
throw Boom.badRequest(`scope must be one of ${LIST_SCOPES.join(', ')}`, {
|
|
66
|
-
code: 'VALIDATION_ERROR',
|
|
67
|
-
details: { field: 'scope' },
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
if (owner !== undefined && !Object.hasOwn(LIST_OWNERS, owner)) {
|
|
71
|
-
throw Boom.badRequest('owner must be me or org', {
|
|
72
|
-
code: 'VALIDATION_ERROR',
|
|
73
|
-
details: { field: 'owner' },
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
return { scope, owner };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
module.exports = {
|
|
80
|
-
objectBody,
|
|
81
|
-
requireFields,
|
|
82
|
-
parseStep,
|
|
83
|
-
optionalString,
|
|
84
|
-
parseListFilters,
|
|
85
|
-
};
|
|
54
|
+
module.exports = { objectBody, requireFields, parseStep, optionalString };
|
package/modules/index.js
CHANGED
|
@@ -8,11 +8,6 @@ const {
|
|
|
8
8
|
getEntityTypeRegistry,
|
|
9
9
|
EntityRegistryError,
|
|
10
10
|
} = require('./entity-type-registry');
|
|
11
|
-
const {
|
|
12
|
-
resolveScope,
|
|
13
|
-
listBindingScopes,
|
|
14
|
-
OwnershipConfigError,
|
|
15
|
-
} = require('./entity-scope');
|
|
16
11
|
|
|
17
12
|
module.exports = {
|
|
18
13
|
ApiKeyRequester,
|
|
@@ -23,7 +18,4 @@ module.exports = {
|
|
|
23
18
|
ModuleFactory,
|
|
24
19
|
getEntityTypeRegistry,
|
|
25
20
|
EntityRegistryError,
|
|
26
|
-
resolveScope,
|
|
27
|
-
listBindingScopes,
|
|
28
|
-
OwnershipConfigError,
|
|
29
21
|
};
|
|
@@ -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
|
|