@learncard/network-brain-service 3.19.1 → 4.0.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/.env.example +44 -111
- package/AGENTS.md +52 -0
- package/CHANGELOG.md +109 -0
- package/README.md +9 -2
- package/bootstrapLambda.test.ts +72 -0
- package/config/config.dev.json +29 -0
- package/config/config.example.json +29 -0
- package/config/config.json +1 -0
- package/config/config.production.json +29 -0
- package/config/config.scouts.dev.json +24 -0
- package/config/config.scouts.production.json +24 -0
- package/contractEventsLambda.ts +18 -0
- package/contractEventsLambdaApp.ts +19 -0
- package/didWebLambda.ts +13 -18
- package/didWebLambdaApp.ts +22 -0
- package/dist/accesslayer/app-manifest-version/create.d.ts +10 -0
- package/dist/accesslayer/app-manifest-version/create.d.ts.map +1 -0
- package/dist/accesslayer/app-manifest-version/read.d.ts +11 -0
- package/dist/accesslayer/app-manifest-version/read.d.ts.map +1 -0
- package/dist/accesslayer/app-manifest-version/update.d.ts +7 -0
- package/dist/accesslayer/app-manifest-version/update.d.ts.map +1 -0
- package/dist/accesslayer/app-store-listing/read.d.ts +2 -1
- package/dist/accesslayer/app-store-listing/read.d.ts.map +1 -1
- package/dist/accesslayer/app-store-listing/relationships/create.d.ts +1 -0
- package/dist/accesslayer/app-store-listing/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/app-store-listing/relationships/read.d.ts +2 -0
- package/dist/accesslayer/app-store-listing/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/app-store-listing/update.d.ts +1 -0
- package/dist/accesslayer/app-store-listing/update.d.ts.map +1 -1
- package/dist/accesslayer/boost/relationships/create.d.ts +2 -1
- package/dist/accesslayer/boost/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/create.d.ts +9 -1
- package/dist/accesslayer/consentflowcontract/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/read.d.ts +34 -3
- package/dist/accesslayer/consentflowcontract/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts +4 -1
- package/dist/accesslayer/consentflowcontract/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/delete.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts +3 -2
- package/dist/accesslayer/consentflowcontract/relationships/read.d.ts.map +1 -1
- package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts +12 -0
- package/dist/accesslayer/consentflowcontract/relationships/recipients.d.ts.map +1 -0
- package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts +12 -0
- package/dist/accesslayer/consentflowcontract/relationships/smartResume.d.ts.map +1 -0
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts +8 -5
- package/dist/accesslayer/consentflowcontract/relationships/update.d.ts.map +1 -1
- package/dist/accesslayer/credential/create.d.ts +2 -1
- package/dist/accesslayer/credential/create.d.ts.map +1 -1
- package/dist/accesslayer/credential/relationships/create.d.ts +4 -3
- package/dist/accesslayer/credential/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/relationships/create.d.ts +2 -1
- package/dist/accesslayer/inbox-credential/relationships/create.d.ts.map +1 -1
- package/dist/accesslayer/inbox-credential/update.d.ts +2 -1
- package/dist/accesslayer/inbox-credential/update.d.ts.map +1 -1
- package/dist/accesslayer/share-link/abandon.d.ts.map +1 -1
- package/dist/accesslayer/share-link/attachment.d.ts +37 -0
- package/dist/accesslayer/share-link/attachment.d.ts.map +1 -0
- package/dist/accesslayer/share-link/finalize.d.ts.map +1 -1
- package/dist/accesslayer/share-link/helpers.d.ts +3 -0
- package/dist/accesslayer/share-link/helpers.d.ts.map +1 -1
- package/dist/accesslayer/share-link/recovery.d.ts.map +1 -1
- package/dist/accesslayer/share-link/reserve.d.ts.map +1 -1
- package/dist/accesslayer/share-link/revoke.d.ts.map +1 -1
- package/dist/accesslayer/share-link/types.d.ts +8 -3
- package/dist/accesslayer/share-link/types.d.ts.map +1 -1
- package/dist/cache/app-store.caches.d.ts +49 -0
- package/dist/cache/app-store.caches.d.ts.map +1 -1
- package/dist/config/applyDockerStageConfig.d.ts +2 -0
- package/dist/config/applyDockerStageConfig.d.ts.map +1 -0
- package/dist/config/environment.d.ts +5 -0
- package/dist/config/environment.d.ts.map +1 -1
- package/dist/config/serverlessFunctionEnv.test.d.ts +2 -0
- package/dist/config/serverlessFunctionEnv.test.d.ts.map +1 -0
- package/dist/config/stageConfig.d.ts +112 -0
- package/dist/config/stageConfig.d.ts.map +1 -0
- package/dist/config/stageConfig.test.d.ts +2 -0
- package/dist/config/stageConfig.test.d.ts.map +1 -0
- package/dist/didkit_wasm_bg.wasm +0 -0
- package/dist/docker-entry.d.ts +1 -1
- package/dist/docker-entry.d.ts.map +1 -1
- package/dist/helpers/app-manifest.helpers.d.ts +5 -0
- package/dist/helpers/app-manifest.helpers.d.ts.map +1 -0
- package/dist/helpers/claim-hooks.helpers.d.ts +2 -1
- package/dist/helpers/claim-hooks.helpers.d.ts.map +1 -1
- package/dist/helpers/consent-referral.helpers.d.ts +2 -0
- package/dist/helpers/consent-referral.helpers.d.ts.map +1 -0
- package/dist/helpers/consent-scopes.helpers.d.ts +7 -0
- package/dist/helpers/consent-scopes.helpers.d.ts.map +1 -0
- package/dist/helpers/contract-event-maintenance.helpers.d.ts +3 -0
- package/dist/helpers/contract-event-maintenance.helpers.d.ts.map +1 -0
- package/dist/helpers/contract-events.helpers.d.ts +25 -0
- package/dist/helpers/contract-events.helpers.d.ts.map +1 -0
- package/dist/helpers/contract-requests.helpers.d.ts +19 -0
- package/dist/helpers/contract-requests.helpers.d.ts.map +1 -0
- package/dist/helpers/finalize-inbox.helpers.d.ts +3 -3
- package/dist/helpers/finalize-inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/inbox.helpers.d.ts.map +1 -1
- package/dist/helpers/inline-template.helpers.d.ts +45 -0
- package/dist/helpers/inline-template.helpers.d.ts.map +1 -0
- package/dist/helpers/notificationMessages.d.ts +1 -1
- package/dist/helpers/notificationMessages.d.ts.map +1 -1
- package/dist/helpers/notificationQueue.helpers.d.ts.map +1 -1
- package/dist/helpers/notifications.helpers.d.ts +6 -0
- package/dist/helpers/notifications.helpers.d.ts.map +1 -1
- package/dist/helpers/profile.helpers.d.ts +1 -0
- package/dist/helpers/profile.helpers.d.ts.map +1 -1
- package/dist/helpers/share-link-attachment-access.d.ts +4 -0
- package/dist/helpers/share-link-attachment-access.d.ts.map +1 -0
- package/dist/helpers/share-link-attachment.d.ts +18 -0
- package/dist/helpers/share-link-attachment.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/attachment-stat.d.ts +5 -0
- package/dist/helpers/share-link-coordinator/attachment-stat.d.ts.map +1 -0
- package/dist/helpers/share-link-coordinator/coordinator.d.ts.map +1 -1
- package/dist/helpers/share-link-coordinator/recovery-runner.d.ts.map +1 -1
- package/dist/helpers/share-link-maintenance/runner.d.ts +1 -0
- package/dist/helpers/share-link-maintenance/runner.d.ts.map +1 -1
- package/dist/helpers/share-link-maintenance/runtime.d.ts +2 -0
- package/dist/helpers/share-link-maintenance/runtime.d.ts.map +1 -1
- package/dist/helpers/share-link-maintenance/telemetry.d.ts.map +1 -1
- package/dist/helpers/share-link-maintenance/types.d.ts +4 -1
- package/dist/helpers/share-link-maintenance/types.d.ts.map +1 -1
- package/dist/helpers/share-link-owner-projection.d.ts.map +1 -1
- package/dist/helpers/share-link-policy/production.d.ts +3 -5
- package/dist/helpers/share-link-policy/production.d.ts.map +1 -1
- package/dist/helpers/share-link-policy/resolver.d.ts +1 -1
- package/dist/helpers/share-link-policy/resolver.d.ts.map +1 -1
- package/dist/helpers/share-link-policy/types.d.ts +8 -2
- package/dist/helpers/share-link-policy/types.d.ts.map +1 -1
- package/dist/helpers/signingAuthority.helpers.d.ts.map +1 -1
- package/dist/helpers/smartResume.helpers.d.ts +3 -0
- package/dist/helpers/smartResume.helpers.d.ts.map +1 -0
- package/dist/index.js +2 -2
- package/dist/models/AppManifestVersion.d.ts +20 -0
- package/dist/models/AppManifestVersion.d.ts.map +1 -0
- package/dist/models/AppStoreListing.d.ts +13 -0
- package/dist/models/AppStoreListing.d.ts.map +1 -1
- package/dist/models/ConsentFlowContract.d.ts +13 -2
- package/dist/models/ConsentFlowContract.d.ts.map +1 -1
- package/dist/models/ConsentFlowTerms.d.ts.map +1 -1
- package/dist/models/ConsentFlowTransaction.d.ts.map +1 -1
- package/dist/models/Integration.d.ts +2 -0
- package/dist/models/Integration.d.ts.map +1 -1
- package/dist/models/ShareLink.d.ts +2 -0
- package/dist/models/ShareLink.d.ts.map +1 -1
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/share-link-constraints.d.ts.map +1 -1
- package/dist/openapi.d.ts +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/routes/app-store.d.ts +619 -0
- package/dist/routes/app-store.d.ts.map +1 -1
- package/dist/routes/boosts.d.ts +2 -2
- package/dist/routes/boosts.d.ts.map +1 -1
- package/dist/routes/contracts.d.ts +146 -3
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/credentials.d.ts +34 -0
- package/dist/routes/credentials.d.ts.map +1 -1
- package/dist/routes/inbox.d.ts +52 -2
- package/dist/routes/inbox.d.ts.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/profile-manager.d.ts.map +1 -1
- package/dist/routes/profiles.d.ts.map +1 -1
- package/dist/routes/public-share-links.d.ts +55 -0
- package/dist/routes/public-share-links.d.ts.map +1 -1
- package/dist/routes/share-links.d.ts +175 -14
- package/dist/routes/share-links.d.ts.map +1 -1
- package/dist/routes/test.d.ts +5 -0
- package/dist/routes/test.d.ts.map +1 -1
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/types/app-manifest.d.ts +339 -0
- package/dist/types/app-manifest.d.ts.map +1 -0
- package/dist/types/app-store-listing.d.ts +34 -0
- package/dist/types/app-store-listing.d.ts.map +1 -1
- package/dist/types/consentflowcontract.d.ts +30 -3
- package/dist/types/consentflowcontract.d.ts.map +1 -1
- package/dist/types/credential-refresh.d.ts +1 -1
- package/esbuildPlugins.cjs +1 -0
- package/lambda.ts +49 -195
- package/lambdaApp.ts +203 -0
- package/package.json +20 -18
- package/scripts/export-openapi.ts +20 -0
- package/scripts/offline-schema-environment.ts +18 -0
- package/scripts/verify-python-contracts.ts +72 -0
- package/serverless-local.yml +3 -6
- package/serverless.function-env.cjs +50 -0
- package/serverless.local.yml +0 -1
- package/serverless.yml +27 -74
- package/shareLinkMaintenanceLambda.ts +13 -44
- package/shareLinkMaintenanceLambdaApp.ts +50 -0
- package/src/accesslayer/app-manifest-version/create.ts +57 -0
- package/src/accesslayer/app-manifest-version/read.ts +129 -0
- package/src/accesslayer/app-manifest-version/update.ts +61 -0
- package/src/accesslayer/app-store-listing/read.ts +6 -0
- package/src/accesslayer/app-store-listing/relationships/create.ts +20 -0
- package/src/accesslayer/app-store-listing/relationships/read.ts +24 -1
- package/src/accesslayer/app-store-listing/update.ts +15 -0
- package/src/accesslayer/boost/relationships/create.ts +8 -2
- package/src/accesslayer/consentflowcontract/create.ts +81 -8
- package/src/accesslayer/consentflowcontract/read.ts +84 -10
- package/src/accesslayer/consentflowcontract/relationships/create.ts +116 -93
- package/src/accesslayer/consentflowcontract/relationships/delete.ts +1 -0
- package/src/accesslayer/consentflowcontract/relationships/read.ts +184 -108
- package/src/accesslayer/consentflowcontract/relationships/recipients.ts +125 -0
- package/src/accesslayer/consentflowcontract/relationships/smartResume.ts +125 -0
- package/src/accesslayer/consentflowcontract/relationships/update.ts +240 -189
- package/src/accesslayer/credential/create.test.ts +2 -115
- package/src/accesslayer/credential/create.ts +11 -6
- package/src/accesslayer/credential/relationships/create.ts +17 -10
- package/src/accesslayer/inbox-credential/relationships/create.ts +70 -48
- package/src/accesslayer/inbox-credential/update.ts +4 -2
- package/src/accesslayer/share-link/abandon.ts +20 -0
- package/src/accesslayer/share-link/attachment.ts +360 -0
- package/src/accesslayer/share-link/finalize.ts +55 -7
- package/src/accesslayer/share-link/helpers.ts +33 -2
- package/src/accesslayer/share-link/recovery.ts +20 -0
- package/src/accesslayer/share-link/reserve.ts +85 -6
- package/src/accesslayer/share-link/revoke.ts +35 -0
- package/src/accesslayer/share-link/types.ts +9 -4
- package/src/cache/app-store.caches.ts +35 -0
- package/src/config/applyDockerStageConfig.ts +6 -0
- package/src/config/environment.test.ts +17 -12
- package/src/config/environment.ts +1 -0
- package/src/config/serverlessFunctionEnv.test.ts +105 -0
- package/src/config/stageConfig.test.ts +25 -0
- package/src/config/stageConfig.ts +24 -0
- package/src/docker-entry.ts +24 -0
- package/src/helpers/app-manifest.helpers.ts +185 -0
- package/src/helpers/claim-hooks.helpers.ts +20 -7
- package/src/helpers/consent-referral.helpers.ts +6 -0
- package/src/helpers/consent-scopes.helpers.ts +58 -0
- package/src/helpers/contract-event-maintenance.helpers.ts +43 -0
- package/src/helpers/contract-events.helpers.ts +540 -0
- package/src/helpers/contract-requests.helpers.ts +211 -0
- package/src/helpers/finalize-inbox.helpers.ts +163 -44
- package/src/helpers/inbox.helpers.ts +51 -66
- package/src/helpers/inline-template.helpers.ts +560 -0
- package/src/helpers/notificationMessages.ts +59 -0
- package/src/helpers/notificationQueue.helpers.ts +23 -5
- package/src/helpers/notifications.helpers.ts +25 -1
- package/src/helpers/profile.helpers.ts +6 -0
- package/src/helpers/share-link-attachment-access.ts +87 -0
- package/src/helpers/share-link-attachment.ts +115 -0
- package/src/helpers/share-link-coordinator/attachment-stat.ts +41 -0
- package/src/helpers/share-link-coordinator/coordinator.ts +15 -2
- package/src/helpers/share-link-coordinator/recovery-runner.ts +15 -0
- package/src/helpers/share-link-maintenance/runner.ts +28 -0
- package/src/helpers/share-link-maintenance/runtime.ts +6 -0
- package/src/helpers/share-link-maintenance/telemetry.ts +3 -0
- package/src/helpers/share-link-maintenance/types.ts +2 -0
- package/src/helpers/share-link-owner-projection.ts +2 -0
- package/src/helpers/share-link-policy/production.ts +37 -17
- package/src/helpers/share-link-policy/resolver.ts +16 -11
- package/src/helpers/share-link-policy/types.ts +15 -7
- package/src/helpers/signingAuthority.helpers.ts +23 -14
- package/src/helpers/smartResume.helpers.ts +123 -0
- package/src/models/AppManifestVersion.ts +43 -0
- package/src/models/AppStoreListing.ts +32 -0
- package/src/models/ConsentFlowContract.ts +34 -3
- package/src/models/ConsentFlowTerms.ts +10 -0
- package/src/models/ConsentFlowTransaction.ts +3 -0
- package/src/models/Integration.ts +2 -0
- package/src/models/ShareLink.ts +4 -0
- package/src/models/index.ts +23 -0
- package/src/models/share-link-constraints.ts +5 -0
- package/src/openapi.ts +44 -8
- package/src/routes/app-store.ts +1054 -27
- package/src/routes/boosts.ts +12 -14
- package/src/routes/contracts.ts +382 -262
- package/src/routes/inbox.ts +4 -2
- package/src/routes/index.ts +4 -1
- package/src/routes/profile-manager.ts +19 -0
- package/src/routes/profiles.ts +12 -0
- package/src/routes/public-share-links.ts +169 -0
- package/src/routes/share-links.ts +69 -0
- package/src/routes/workflows.ts +3 -2
- package/src/types/app-manifest.ts +28 -0
- package/src/types/app-store-listing.ts +13 -0
- package/src/types/consentflowcontract.ts +25 -4
- package/test/app-manifest.helpers.spec.ts +170 -0
- package/test/app-manifest.routes.spec.ts +299 -0
- package/test/app-store-listing-updates.spec.ts +236 -0
- package/test/app-store-listing.spec.ts +65 -3
- package/test/consent-data-boundaries.spec.ts +352 -0
- package/test/consent-recipients.spec.ts +513 -0
- package/test/consent-scopes.helpers.spec.ts +67 -0
- package/test/consentflow.spec.ts +63 -2
- package/test/contract-requests.spec.ts +1588 -0
- package/test/credential-storage.neo4j.spec.ts +122 -0
- package/test/guardian-gated-routes.spec.ts +36 -0
- package/test/inbox-security.e2e.ts +26 -10
- package/test/inbox.spec.ts +184 -8
- package/test/inline-template.helpers.spec.ts +49 -0
- package/test/managed-recipient-inbox.spec.ts +24 -2
- package/test/openapi.spec.ts +14 -73
- package/test/profiles.spec.ts +30 -0
- package/test/share-link-attachment.neo4j.spec.ts +262 -0
- package/test/share-link-attachment.unit.spec.ts +244 -0
- package/test/share-link-lifecycle.neo4j.spec.ts +145 -2
- package/test/share-link-maintenance.unit.spec.ts +32 -5
- package/test/share-link-owner-route.unit.spec.ts +70 -2
- package/test/share-link-policy.unit.spec.ts +18 -4
- package/test/share-link-production-policy.unit.spec.ts +58 -9
- package/test/share-link-public-route.unit.spec.ts +279 -0
- package/test/share-link-service-policy.neo4j.spec.ts +403 -0
- package/test/signing-authority-encryption.spec.ts +75 -0
- package/test/smart-resume-consent.spec.ts +233 -0
- package/test/smart-resume-upload.spec.ts +65 -0
- package/tsconfig.build.json +1 -1
- package/tsconfig.json +1 -0
- package/vitest.config.ts +13 -0
- package/vitest.inbox-e2e.config.ts +2 -0
- package/workerLambda.test.ts +100 -0
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import type { JWE
|
|
2
|
+
import type { JWE } from '@learncard/types';
|
|
3
3
|
import type { IssuedCredential } from '../../types/credential';
|
|
4
4
|
|
|
5
|
-
const mocks = vi.hoisted(() => ({ createOne: vi.fn(
|
|
5
|
+
const mocks = vi.hoisted(() => ({ createOne: vi.fn() }));
|
|
6
6
|
vi.mock('@models', () => ({ Credential: { createOne: mocks.createOne } }));
|
|
7
7
|
|
|
8
8
|
import { storeCredential } from './create';
|
|
9
9
|
|
|
10
|
-
const statusEntry = {
|
|
11
|
-
id: 'https://network.example/status/1#42',
|
|
12
|
-
type: 'BitstringStatusListEntry' as const,
|
|
13
|
-
statusPurpose: 'revocation' as const,
|
|
14
|
-
statusListIndex: '42',
|
|
15
|
-
statusListCredential: 'https://network.example/status/1',
|
|
16
|
-
};
|
|
17
10
|
const createJwe = (): JWE => ({
|
|
18
11
|
protected: 'header',
|
|
19
12
|
iv: 'iv',
|
|
@@ -21,121 +14,15 @@ const createJwe = (): JWE => ({
|
|
|
21
14
|
tag: 'tag',
|
|
22
15
|
recipients: [],
|
|
23
16
|
});
|
|
24
|
-
const createVc = (): VC => ({
|
|
25
|
-
'@context': ['https://www.w3.org/2018/credentials/v1'],
|
|
26
|
-
type: ['VerifiableCredential'],
|
|
27
|
-
issuer: 'did:example:issuer',
|
|
28
|
-
issuanceDate: '2026-01-01T00:00:00Z',
|
|
29
|
-
credentialSubject: { id: 'did:example:subject' },
|
|
30
|
-
proof: {
|
|
31
|
-
type: 'Ed25519Signature2020',
|
|
32
|
-
created: '2026-01-01T00:00:00Z',
|
|
33
|
-
proofPurpose: 'assertionMethod',
|
|
34
|
-
verificationMethod: 'did:example:issuer#key-1',
|
|
35
|
-
proofValue: 'existing-signature',
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
17
|
|
|
39
18
|
describe('credential status storage', () => {
|
|
40
19
|
beforeEach(() => {
|
|
41
|
-
vi.restoreAllMocks();
|
|
42
20
|
vi.clearAllMocks();
|
|
43
21
|
});
|
|
44
22
|
|
|
45
|
-
it('persists public status coordinates after serializing an issuance result', async () => {
|
|
46
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
47
|
-
const jwe = createJwe();
|
|
48
|
-
const issued: IssuedCredential = {
|
|
49
|
-
kind: 'issued-credential',
|
|
50
|
-
credential: jwe,
|
|
51
|
-
statusEntries: [statusEntry],
|
|
52
|
-
};
|
|
53
|
-
await storeCredential(JSON.parse(JSON.stringify(issued)));
|
|
54
|
-
expect(mocks.createOne).toHaveBeenCalledWith(
|
|
55
|
-
expect.objectContaining({
|
|
56
|
-
credential: JSON.stringify(jwe),
|
|
57
|
-
statusEntries: JSON.stringify([statusEntry]),
|
|
58
|
-
})
|
|
59
|
-
);
|
|
60
|
-
expect(warn).not.toHaveBeenCalled();
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('warns when a client-encrypted JWE has no metadata, without logging its payload', async () => {
|
|
64
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
65
|
-
const original = createJwe();
|
|
66
|
-
const jwe = { ...original };
|
|
67
|
-
await storeCredential(jwe);
|
|
68
|
-
expect(warn).toHaveBeenCalledExactlyOnceWith(
|
|
69
|
-
expect.stringContaining('Encrypted credential has no status metadata'),
|
|
70
|
-
{ credentialId: expect.any(String) }
|
|
71
|
-
);
|
|
72
|
-
expect(mocks.createOne.mock.calls[0]![0].statusEntries).toBeUndefined();
|
|
73
|
-
expect(mocks.createOne.mock.calls[0]![0].credential).toBe(JSON.stringify(jwe));
|
|
74
|
-
});
|
|
75
|
-
|
|
76
23
|
it('rejects internal issuance results that lost their status metadata before writing', async () => {
|
|
77
24
|
const issued = { kind: 'issued-credential', credential: createJwe() } as IssuedCredential;
|
|
78
25
|
await expect(storeCredential(issued)).rejects.toThrow();
|
|
79
26
|
expect(mocks.createOne).not.toHaveBeenCalled();
|
|
80
27
|
});
|
|
81
|
-
|
|
82
|
-
it.each([undefined, []])(
|
|
83
|
-
'warns for a pre-signed plaintext VC with missing or empty status (%j) and preserves its signature',
|
|
84
|
-
async credentialStatus => {
|
|
85
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
86
|
-
const credential = { ...createVc(), credentialStatus };
|
|
87
|
-
const stored = await storeCredential(credential);
|
|
88
|
-
expect(warn).toHaveBeenCalledExactlyOnceWith(
|
|
89
|
-
expect.stringContaining('Plaintext credential has no status metadata'),
|
|
90
|
-
{ credentialId: stored.id }
|
|
91
|
-
);
|
|
92
|
-
expect(stored.credential).toBe(JSON.stringify(credential));
|
|
93
|
-
expect(stored.statusEntries).toBeUndefined();
|
|
94
|
-
}
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
it.each([statusEntry, [statusEntry]])(
|
|
98
|
-
'does not warn for a plaintext VC with embedded status entries (%j)',
|
|
99
|
-
async credentialStatus => {
|
|
100
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
101
|
-
const credential = { ...createVc(), credentialStatus };
|
|
102
|
-
const stored = await storeCredential(credential);
|
|
103
|
-
expect(warn).not.toHaveBeenCalled();
|
|
104
|
-
expect(stored.credential).toBe(JSON.stringify(credential));
|
|
105
|
-
}
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
it.each([
|
|
109
|
-
['Encrypted', createJwe()],
|
|
110
|
-
['Plaintext', createVc()],
|
|
111
|
-
] as const)(
|
|
112
|
-
'warns for %s VC v1 issuance with empty status metadata',
|
|
113
|
-
async (kind, credential) => {
|
|
114
|
-
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
115
|
-
const stored = await storeCredential({
|
|
116
|
-
kind: 'issued-credential',
|
|
117
|
-
credential,
|
|
118
|
-
statusEntries: [],
|
|
119
|
-
});
|
|
120
|
-
expect(warn).toHaveBeenCalledExactlyOnceWith(
|
|
121
|
-
expect.stringContaining(`${kind} credential has no status metadata`),
|
|
122
|
-
{ credentialId: stored.id }
|
|
123
|
-
);
|
|
124
|
-
expect(stored.credential).toBe(JSON.stringify(credential));
|
|
125
|
-
expect(stored.statusEntries).toBeUndefined();
|
|
126
|
-
}
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
it('does not treat a wire JWE with extra envelope-like fields as trusted issuance metadata', async () => {
|
|
130
|
-
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
131
|
-
const jwe = {
|
|
132
|
-
...createJwe(),
|
|
133
|
-
kind: 'issued-credential',
|
|
134
|
-
credential: createJwe(),
|
|
135
|
-
statusEntries: [statusEntry],
|
|
136
|
-
};
|
|
137
|
-
await storeCredential(jwe);
|
|
138
|
-
expect(mocks.createOne.mock.calls[0]![0].statusEntries).toBeUndefined();
|
|
139
|
-
expect(mocks.createOne.mock.calls[0]![0].credential).toBe(JSON.stringify(jwe));
|
|
140
|
-
});
|
|
141
28
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BitstringStatusListEntryValidator, UnsignedVC, VC, JWE } from '@learncard/types';
|
|
2
|
+
import type { Transaction } from 'neo4j-driver';
|
|
2
3
|
import { v4 as uuid } from 'uuid';
|
|
3
4
|
import { getBitstringStatusListEntries, isEncrypted } from '@learncard/helpers';
|
|
4
5
|
import { isIssuedCredential } from '@helpers/issuedCredentialStatus.helpers';
|
|
@@ -7,7 +8,8 @@ import type { IssuedCredential } from 'types/credential';
|
|
|
7
8
|
import { Credential, CredentialInstance } from '@models';
|
|
8
9
|
|
|
9
10
|
export const storeCredential = async (
|
|
10
|
-
input: UnsignedVC | VC | JWE | IssuedCredential
|
|
11
|
+
input: UnsignedVC | VC | JWE | IssuedCredential,
|
|
12
|
+
transaction?: Transaction
|
|
11
13
|
): Promise<CredentialInstance> => {
|
|
12
14
|
const id = uuid();
|
|
13
15
|
|
|
@@ -28,9 +30,12 @@ export const storeCredential = async (
|
|
|
28
30
|
{ credentialId: id }
|
|
29
31
|
);
|
|
30
32
|
}
|
|
31
|
-
return Credential.createOne(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
return Credential.createOne(
|
|
34
|
+
{
|
|
35
|
+
id,
|
|
36
|
+
credential: JSON.stringify(credential),
|
|
37
|
+
...(statusEntries?.length ? { statusEntries: JSON.stringify(statusEntries) } : {}),
|
|
38
|
+
},
|
|
39
|
+
transaction ? { session: transaction } : undefined
|
|
40
|
+
);
|
|
36
41
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { termsReferralSnapshotCypher } from '@helpers/consent-referral.helpers';
|
|
1
2
|
import { QueryBuilder, BindParam } from 'neogma';
|
|
3
|
+
import type { Transaction } from 'neo4j-driver';
|
|
2
4
|
import { v4 as uuid } from 'uuid';
|
|
3
5
|
|
|
4
6
|
import {
|
|
@@ -24,7 +26,8 @@ export const createSentCredentialRelationship = async (
|
|
|
24
26
|
credential: CredentialInstance,
|
|
25
27
|
metadata?: Record<string, unknown>,
|
|
26
28
|
activityId?: string,
|
|
27
|
-
integrationId?: string
|
|
29
|
+
integrationId?: string,
|
|
30
|
+
transaction?: Transaction
|
|
28
31
|
): Promise<void> => {
|
|
29
32
|
const properties = flattenObject({
|
|
30
33
|
to: to.profileId,
|
|
@@ -57,12 +60,10 @@ export const createSentCredentialRelationship = async (
|
|
|
57
60
|
],
|
|
58
61
|
})
|
|
59
62
|
.create(
|
|
60
|
-
`(profile)-[r:${
|
|
61
|
-
Profile.getRelationshipByAlias('credentialSent').name
|
|
62
|
-
}]->(credential)`
|
|
63
|
+
`(profile)-[r:${Profile.getRelationshipByAlias('credentialSent').name}]->(credential)`
|
|
63
64
|
)
|
|
64
65
|
.set('r = $params')
|
|
65
|
-
.run();
|
|
66
|
+
.run(transaction);
|
|
66
67
|
|
|
67
68
|
// If the issuer is an AppStoreListing, additionally write the
|
|
68
69
|
// AppStoreListing -> CREDENTIAL_SENT -> Credential edge so that
|
|
@@ -91,7 +92,7 @@ export const createSentCredentialRelationship = async (
|
|
|
91
92
|
}]->(credential)`
|
|
92
93
|
)
|
|
93
94
|
.set('r = $params')
|
|
94
|
-
.run();
|
|
95
|
+
.run(transaction);
|
|
95
96
|
}
|
|
96
97
|
};
|
|
97
98
|
|
|
@@ -139,7 +140,8 @@ export const createReceivedCredentialRelationship = async (
|
|
|
139
140
|
to: ProfileType,
|
|
140
141
|
from: ProfileType | { listing_id: string },
|
|
141
142
|
credential: CredentialInstance,
|
|
142
|
-
metadata?: Record<string, unknown
|
|
143
|
+
metadata?: Record<string, unknown>,
|
|
144
|
+
transaction?: Transaction
|
|
143
145
|
): Promise<void> => {
|
|
144
146
|
const fromId = 'profileId' in from ? from.profileId : from.listing_id;
|
|
145
147
|
const properties = flattenObject({
|
|
@@ -165,12 +167,13 @@ export const createReceivedCredentialRelationship = async (
|
|
|
165
167
|
}]->(profile)`
|
|
166
168
|
)
|
|
167
169
|
.set('r = $params')
|
|
168
|
-
.run();
|
|
170
|
+
.run(transaction);
|
|
169
171
|
};
|
|
170
172
|
|
|
171
173
|
export const setDefaultClaimedRole = async (
|
|
172
174
|
profile: ProfileType,
|
|
173
|
-
credential: CredentialInstance
|
|
175
|
+
credential: CredentialInstance,
|
|
176
|
+
transaction?: Transaction
|
|
174
177
|
): Promise<void> => {
|
|
175
178
|
await new QueryBuilder()
|
|
176
179
|
.match({
|
|
@@ -200,7 +203,10 @@ export const setDefaultClaimedRole = async (
|
|
|
200
203
|
{ identifier: 'boost' },
|
|
201
204
|
],
|
|
202
205
|
})
|
|
203
|
-
.run();
|
|
206
|
+
.run(transaction);
|
|
207
|
+
|
|
208
|
+
// An enclosing transaction invalidates caches only after its writes commit.
|
|
209
|
+
if (transaction) return;
|
|
204
210
|
|
|
205
211
|
try {
|
|
206
212
|
const boostId = await getBoostIdForCredentialInstance(credential);
|
|
@@ -239,6 +245,7 @@ export const createCredentialIssuedViaContractRelationship = async (
|
|
|
239
245
|
{ identifier: 'terms' },
|
|
240
246
|
],
|
|
241
247
|
})
|
|
248
|
+
.raw(termsReferralSnapshotCypher())
|
|
242
249
|
.with('transaction')
|
|
243
250
|
.match({
|
|
244
251
|
model: Credential,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { QueryBuilder, BindParam } from 'neogma';
|
|
2
|
+
import type { Transaction } from 'neo4j-driver';
|
|
2
3
|
import { Profile, InboxCredential } from '@models';
|
|
3
4
|
|
|
4
5
|
export const createDeliveredRelationship = async (
|
|
@@ -8,19 +9,23 @@ export const createDeliveredRelationship = async (
|
|
|
8
9
|
deliveryMethod: string = 'direct'
|
|
9
10
|
): Promise<boolean> => {
|
|
10
11
|
const timestamp = new Date().toISOString();
|
|
11
|
-
|
|
12
|
-
const result = await new QueryBuilder(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
|
|
13
|
+
const result = await new QueryBuilder(
|
|
14
|
+
new BindParam({
|
|
15
|
+
profileId,
|
|
16
|
+
inboxCredentialId,
|
|
17
|
+
timestamp,
|
|
18
|
+
recipientDid,
|
|
19
|
+
deliveryMethod,
|
|
20
|
+
})
|
|
21
|
+
)
|
|
19
22
|
.match({ model: Profile, identifier: 'profile' })
|
|
20
23
|
.where('profile.profileId = $profileId')
|
|
21
24
|
.match({ model: InboxCredential, identifier: 'inboxCredential' })
|
|
22
25
|
.where('inboxCredential.id = $inboxCredentialId')
|
|
23
|
-
.create(
|
|
26
|
+
.create(
|
|
27
|
+
'(profile)-[:DELIVERED_INBOX_CREDENTIAL { timestamp: $timestamp, recipientDid: $recipientDid, deliveryMethod: $deliveryMethod }]->(inboxCredential)'
|
|
28
|
+
)
|
|
24
29
|
.return('profile')
|
|
25
30
|
.run();
|
|
26
31
|
|
|
@@ -30,23 +35,28 @@ export const createDeliveredRelationship = async (
|
|
|
30
35
|
export const createClaimedRelationship = async (
|
|
31
36
|
profileId: string,
|
|
32
37
|
inboxCredentialId: string,
|
|
33
|
-
claimToken: string
|
|
38
|
+
claimToken: string,
|
|
39
|
+
transaction?: Transaction
|
|
34
40
|
): Promise<boolean> => {
|
|
35
41
|
const timestamp = new Date().toISOString();
|
|
36
|
-
|
|
37
|
-
const result = await new QueryBuilder(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
|
|
43
|
+
const result = await new QueryBuilder(
|
|
44
|
+
new BindParam({
|
|
45
|
+
profileId,
|
|
46
|
+
inboxCredentialId,
|
|
47
|
+
timestamp,
|
|
48
|
+
claimToken,
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
.match({ model: Profile, identifier: 'profile' })
|
|
44
52
|
.where('profile.profileId = $profileId')
|
|
45
|
-
.match({ model: InboxCredential, identifier: 'inboxCredential'})
|
|
53
|
+
.match({ model: InboxCredential, identifier: 'inboxCredential' })
|
|
46
54
|
.where('inboxCredential.id = $inboxCredentialId')
|
|
47
|
-
.create(
|
|
55
|
+
.create(
|
|
56
|
+
'(profile)-[:CLAIMED_INBOX_CREDENTIAL { timestamp: $timestamp, claimToken: $claimToken }]->(inboxCredential)'
|
|
57
|
+
)
|
|
48
58
|
.return('profile')
|
|
49
|
-
.run();
|
|
59
|
+
.run(transaction);
|
|
50
60
|
|
|
51
61
|
return result.records.length > 0;
|
|
52
62
|
};
|
|
@@ -58,19 +68,23 @@ export const createEmailSentRelationship = async (
|
|
|
58
68
|
token: string
|
|
59
69
|
): Promise<boolean> => {
|
|
60
70
|
const timestamp = new Date().toISOString();
|
|
61
|
-
|
|
62
|
-
const result = await new QueryBuilder(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
|
|
72
|
+
const result = await new QueryBuilder(
|
|
73
|
+
new BindParam({
|
|
74
|
+
profileDid,
|
|
75
|
+
inboxCredentialId,
|
|
76
|
+
timestamp,
|
|
77
|
+
emailAddress,
|
|
78
|
+
token,
|
|
79
|
+
})
|
|
80
|
+
)
|
|
69
81
|
.match({ model: Profile, identifier: 'profile' })
|
|
70
82
|
.where('profile.did = $profileDid')
|
|
71
83
|
.match({ model: InboxCredential, identifier: 'inboxCredential' })
|
|
72
84
|
.where('inboxCredential.id = $inboxCredentialId')
|
|
73
|
-
.create(
|
|
85
|
+
.create(
|
|
86
|
+
'(profile)-[:SENT_EMAIL { timestamp: $timestamp, emailAddress: $emailAddress, token: $token }]->(inboxCredential)'
|
|
87
|
+
)
|
|
74
88
|
.return('profile')
|
|
75
89
|
.run();
|
|
76
90
|
|
|
@@ -85,20 +99,24 @@ export const createWebhookSentRelationship = async (
|
|
|
85
99
|
response?: string
|
|
86
100
|
): Promise<boolean> => {
|
|
87
101
|
const timestamp = new Date().toISOString();
|
|
88
|
-
|
|
89
|
-
const result = await new QueryBuilder(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
102
|
+
|
|
103
|
+
const result = await new QueryBuilder(
|
|
104
|
+
new BindParam({
|
|
105
|
+
profileDid,
|
|
106
|
+
inboxCredentialId,
|
|
107
|
+
timestamp,
|
|
108
|
+
url,
|
|
109
|
+
status,
|
|
110
|
+
response: response || null,
|
|
111
|
+
})
|
|
112
|
+
)
|
|
97
113
|
.match({ model: Profile, identifier: 'profile' })
|
|
98
114
|
.where('profile.did = $profileDid')
|
|
99
115
|
.match({ model: InboxCredential, identifier: 'inboxCredential' })
|
|
100
116
|
.where('inboxCredential.id = $inboxCredentialId')
|
|
101
|
-
.create(
|
|
117
|
+
.create(
|
|
118
|
+
'(profile)-[:SENT_WEBHOOK { timestamp: $timestamp, url: $url, status: $status, response: $response }]->(inboxCredential)'
|
|
119
|
+
)
|
|
102
120
|
.return('profile')
|
|
103
121
|
.run();
|
|
104
122
|
|
|
@@ -110,19 +128,23 @@ export const createExpiredRelationship = async (
|
|
|
110
128
|
inboxCredentialId: string
|
|
111
129
|
): Promise<boolean> => {
|
|
112
130
|
const timestamp = new Date().toISOString();
|
|
113
|
-
|
|
114
|
-
const result = await new QueryBuilder(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
|
|
132
|
+
const result = await new QueryBuilder(
|
|
133
|
+
new BindParam({
|
|
134
|
+
profileDid,
|
|
135
|
+
inboxCredentialId,
|
|
136
|
+
timestamp,
|
|
137
|
+
})
|
|
138
|
+
)
|
|
119
139
|
.match({ model: Profile, identifier: 'profile' })
|
|
120
140
|
.where('profile.did = $profileDid')
|
|
121
141
|
.match({ model: InboxCredential, identifier: 'inboxCredential' })
|
|
122
142
|
.where('inboxCredential.id = $inboxCredentialId')
|
|
123
|
-
.create(
|
|
143
|
+
.create(
|
|
144
|
+
'(profile)-[:EXPIRED_INBOX_CREDENTIAL { timestamp: $timestamp }]->(inboxCredential)'
|
|
145
|
+
)
|
|
124
146
|
.return('profile')
|
|
125
147
|
.run();
|
|
126
148
|
|
|
127
149
|
return result.records.length > 0;
|
|
128
|
-
};
|
|
150
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { QueryBuilder, BindParam } from 'neogma';
|
|
2
|
+
import type { Transaction } from 'neo4j-driver';
|
|
2
3
|
import { InboxCredential } from '@models';
|
|
3
4
|
import { InboxCredentialType } from '@learncard/types';
|
|
4
5
|
import { flattenObject, inflateObject } from '@helpers/objects.helpers';
|
|
@@ -31,7 +32,8 @@ export const updateInboxCredential = async (
|
|
|
31
32
|
export const finalizeAndWipeInboxCredential = async (
|
|
32
33
|
id: string,
|
|
33
34
|
delivery: InboxDelivery,
|
|
34
|
-
{ isAccepted = true }: { isAccepted?: boolean } = {}
|
|
35
|
+
{ isAccepted = true }: { isAccepted?: boolean } = {},
|
|
36
|
+
transaction?: Transaction
|
|
35
37
|
): Promise<InboxCredentialType | null> => {
|
|
36
38
|
const result = await new QueryBuilder(
|
|
37
39
|
new BindParam({
|
|
@@ -64,7 +66,7 @@ export const finalizeAndWipeInboxCredential = async (
|
|
|
64
66
|
)
|
|
65
67
|
.return('inboxCredential')
|
|
66
68
|
.limit(1)
|
|
67
|
-
.run();
|
|
69
|
+
.run(transaction);
|
|
68
70
|
|
|
69
71
|
const inboxCredential = result.records[0]?.get('inboxCredential')?.properties;
|
|
70
72
|
return inboxCredential
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ensureShareLinkConstraints } from '../../models/share-link-constraints';
|
|
2
2
|
import { failShareLink } from './errors';
|
|
3
|
+
import { queueShareAttachmentCleanup } from './attachment';
|
|
3
4
|
import {
|
|
4
5
|
deleteReservation,
|
|
5
6
|
enqueueCleanupJob,
|
|
@@ -70,6 +71,25 @@ export const abandonReservation = async (
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
let cleanupQueuedFor: string | null = null;
|
|
74
|
+
if (
|
|
75
|
+
reservation.attachmentId &&
|
|
76
|
+
reservation.attachmentChunkCount &&
|
|
77
|
+
reservation.contentVersion
|
|
78
|
+
) {
|
|
79
|
+
await queueShareAttachmentCleanup(
|
|
80
|
+
tx,
|
|
81
|
+
{
|
|
82
|
+
namespace: reservation.namespace,
|
|
83
|
+
ownerProfileId: reservation.ownerProfileId,
|
|
84
|
+
shareId: reservation.shareId,
|
|
85
|
+
contentVersion: reservation.contentVersion,
|
|
86
|
+
attachmentId: reservation.attachmentId,
|
|
87
|
+
chunkCount: reservation.attachmentChunkCount,
|
|
88
|
+
},
|
|
89
|
+
'abandoned',
|
|
90
|
+
nowIso
|
|
91
|
+
);
|
|
92
|
+
}
|
|
73
93
|
|
|
74
94
|
if (reservation.objectRef) {
|
|
75
95
|
await enqueueCleanupJob(tx, {
|