@learncard/network-brain-service 3.19.1 → 4.0.1
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 +120 -0
- package/README.md +9 -2
- package/bootstrapLambda.test.ts +72 -0
- package/bundle.mjs +9 -1
- 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/publish-metafile.json +1 -0
- 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 -19
- package/project.json +8 -1
- 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 +29 -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
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const { functionEnvironment, pickNonEmpty } = require('@learncard/service-config/function-env.cjs');
|
|
2
|
+
|
|
3
|
+
// User-approved brain-service deployment credentials. In bundle mode these are delivered
|
|
4
|
+
// through the runtime-secrets bundle and omitted here; in fallback mode (no bundle) the
|
|
5
|
+
// existing per-value GitHub environment secrets are retained. Only genuine credentials
|
|
6
|
+
// belong in this list — non-secret allowlists, tuning flags and per-stage values are
|
|
7
|
+
// delivered through checked-in stage config at cold start, never forwarded here.
|
|
8
|
+
const PROVIDER_SECRETS = [
|
|
9
|
+
'SEED',
|
|
10
|
+
'NEO4J_URI',
|
|
11
|
+
'NEO4J_USERNAME',
|
|
12
|
+
'NEO4J_PASSWORD',
|
|
13
|
+
'POSTMARK_API_KEY',
|
|
14
|
+
'MESSAGEBIRD_AUTH_TOKEN',
|
|
15
|
+
'SENTRY_DSN',
|
|
16
|
+
'POSTHOG_API_KEY',
|
|
17
|
+
'SKILL_EMBEDDING_GOOGLE_API_KEY',
|
|
18
|
+
'SKILLS_PROVIDER_API_KEY',
|
|
19
|
+
'TWILIO_ACCOUNT_SID',
|
|
20
|
+
'TWILIO_AUTH_TOKEN',
|
|
21
|
+
'SMART_RESUME_CLIENT_ID',
|
|
22
|
+
'SMART_RESUME_ACCESS_KEY',
|
|
23
|
+
'SMART_RESUME_CONTRACT_URI',
|
|
24
|
+
'CREDENTIAL_REFRESH_DIGEST_SECRET',
|
|
25
|
+
'SHARE_LINK_REQUEST_HASH_SECRET',
|
|
26
|
+
'APP_STORE_ADMIN_PROFILE_IDS',
|
|
27
|
+
'LOGIN_PROVIDER_DID',
|
|
28
|
+
'NOTIFICATIONS_SERVICE_WEBHOOK_URL',
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
// Serverless v3 supplies CLI options to file functions and recursively resolves the strings
|
|
32
|
+
// it returns, so CloudFormation intrinsics (Fn::GetAtt) and construct references survive.
|
|
33
|
+
// Every brain-service function shares the one generated execution role, so the bundle pointer
|
|
34
|
+
// and the credential fallbacks sit together on the provider. Bundle mode emits only infra plus
|
|
35
|
+
// the pointer; fallback mode retains the per-value GitHub secrets. Non-secret per-stage tuning
|
|
36
|
+
// and allowlists are applied from checked-in stage config at cold start, never forwarded here.
|
|
37
|
+
exports.provider = ({ options = {}, env = process.env } = {}) => ({
|
|
38
|
+
LAMBDA_STAGE: options.stage || 'dev',
|
|
39
|
+
// Non-secret product selector for the shared stage files (config.<tenant>.<stage>.json).
|
|
40
|
+
CONFIG_TENANT: env.CONFIG_TENANT || 'learncard',
|
|
41
|
+
PORT: String(options.httpPort || '3000'),
|
|
42
|
+
REDIS_HOST: { 'Fn::GetAtt': ['ElasticCacheCluster', 'RedisEndpoint.Address'] },
|
|
43
|
+
REDIS_PORT: { 'Fn::GetAtt': ['ElasticCacheCluster', 'RedisEndpoint.Port'] },
|
|
44
|
+
NOTIFICATIONS_QUEUE_URL: '${construct:notifications-queue.queueUrl}',
|
|
45
|
+
INBOX_QUEUE_URL: '${construct:inbox-queue.queueUrl}',
|
|
46
|
+
// The deploy commit, emitted only when set so an empty string never counts toward the
|
|
47
|
+
// 4KB per-function environment limit.
|
|
48
|
+
...pickNonEmpty(['GIT_SHA'], env),
|
|
49
|
+
...functionEnvironment({ always: ['RUNTIME_SECRETS_ID'], fallback: PROVIDER_SECRETS, env }),
|
|
50
|
+
});
|
package/serverless.local.yml
CHANGED
package/serverless.yml
CHANGED
|
@@ -46,6 +46,8 @@ custom:
|
|
|
46
46
|
packager: pnpm
|
|
47
47
|
plugins: esbuildPlugins.cjs
|
|
48
48
|
bundle: true
|
|
49
|
+
# Deployment builds run once; avoid leaking rebuild contexts when plugins fail.
|
|
50
|
+
skipRebuild: true
|
|
49
51
|
minify: true
|
|
50
52
|
external: ['@learncard/didkit-plugin/dist/didkit_wasm_bg.wasm', '*.node', 'p-limit']
|
|
51
53
|
# didkit-plugin-node ships via the didkitNative Lambda Layer. `exclude` both
|
|
@@ -138,80 +140,20 @@ provider:
|
|
|
138
140
|
timeout: 29
|
|
139
141
|
stage: ${opt:stage, "dev"}
|
|
140
142
|
region: ${opt:region, "us-east-1"}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
MESSAGEBIRD_AUTH_TOKEN: ${env:MESSAGEBIRD_AUTH_TOKEN}
|
|
156
|
-
MESSAGEBIRD_ORIGINATOR: ${env:MESSAGEBIRD_ORIGINATOR}
|
|
157
|
-
TRUSTED_ISSUERS_WHITELIST: ${env:TRUSTED_ISSUERS_WHITELIST, ""}
|
|
158
|
-
TRUSTED_BRAIN_SERVICES: ${env:TRUSTED_BRAIN_SERVICES, ""}
|
|
159
|
-
BRAIN_SERVICE_REGISTRY_URL: ${env:BRAIN_SERVICE_REGISTRY_URL, ""}
|
|
160
|
-
DCC_KNOWN_REGISTRIES_URL: ${env:DCC_KNOWN_REGISTRIES_URL, ""}
|
|
161
|
-
SENTRY_DSN: ${env:SENTRY_DSN}
|
|
162
|
-
SENTRY_ENV: ${env:SENTRY_ENV}
|
|
163
|
-
SENTRY_AUTH_TOKEN: ${env:SENTRY_AUTH_TOKEN}
|
|
164
|
-
SENTRY_ORG: ${env:SENTRY_ORG}
|
|
165
|
-
SENTRY_PROJECT: ${env:SENTRY_PROJECT}
|
|
166
|
-
REDIS_HOST:
|
|
167
|
-
'Fn::GetAtt': [ElasticCacheCluster, RedisEndpoint.Address]
|
|
168
|
-
REDIS_PORT:
|
|
169
|
-
'Fn::GetAtt': [ElasticCacheCluster, RedisEndpoint.Port]
|
|
170
|
-
NOTIFICATIONS_QUEUE_URL: ${construct:notifications-queue.queueUrl}
|
|
171
|
-
INBOX_QUEUE_URL: ${construct:inbox-queue.queueUrl}
|
|
172
|
-
SKILL_EMBEDDING_GOOGLE_API_KEY: ${env:SKILL_EMBEDDING_GOOGLE_API_KEY, ""}
|
|
173
|
-
SKILL_EMBEDDING_GOOGLE_MODEL: ${env:SKILL_EMBEDDING_GOOGLE_MODEL, "gemini-embedding-001"}
|
|
174
|
-
SKILL_EMBEDDING_BATCH_SIZE: ${env:SKILL_EMBEDDING_BATCH_SIZE, "25"}
|
|
175
|
-
SKILL_EMBEDDING_BACKFILL_ON_STARTUP: ${env:SKILL_EMBEDDING_BACKFILL_ON_STARTUP, "false"}
|
|
176
|
-
SKILL_EMBEDDING_BACKFILL_LOCK_TTL_SECONDS: ${env:SKILL_EMBEDDING_BACKFILL_LOCK_TTL_SECONDS, "900"}
|
|
177
|
-
SKILL_EMBEDDING_BACKFILL_PAGE_SIZE: ${env:SKILL_EMBEDDING_BACKFILL_PAGE_SIZE, "100"}
|
|
178
|
-
SKILL_EMBEDDING_BACKFILL_BATCH_SIZE: ${env:SKILL_EMBEDDING_BACKFILL_BATCH_SIZE, "50"}
|
|
179
|
-
SMART_RESUME_CLIENT_ID: ${env:SMART_RESUME_CLIENT_ID, ""}
|
|
180
|
-
SMART_RESUME_ACCESS_KEY: ${env:SMART_RESUME_ACCESS_KEY, ""}
|
|
181
|
-
SMART_RESUME_CONTRACT_URI: ${env:SMART_RESUME_CONTRACT_URI, ""}
|
|
182
|
-
LOGIN_PROVIDER_DID: ${env:LOGIN_PROVIDER_DID, ""}
|
|
183
|
-
OIDC_TRUSTED_ISSUERS: ${env:OIDC_TRUSTED_ISSUERS, ""}
|
|
184
|
-
OIDC_EXPECTED_AUDIENCE: ${env:OIDC_EXPECTED_AUDIENCE, ""}
|
|
185
|
-
APP_STORE_ADMIN_PROFILE_IDS: ${env:APP_STORE_ADMIN_PROFILE_IDS, ""}
|
|
186
|
-
APP_BRAND: ${env:APP_BRAND, 'scouts'}
|
|
187
|
-
# LC-1644 perf bench (admin-only). Optional — if unset, the bench still runs
|
|
188
|
-
# but emits no PostHog events. POSTHOG_HOST falls back to the helper's default
|
|
189
|
-
# (https://us.i.posthog.com).
|
|
190
|
-
POSTHOG_API_KEY: ${env:POSTHOG_API_KEY, ""}
|
|
191
|
-
POSTHOG_HOST: ${env:POSTHOG_HOST, ""}
|
|
192
|
-
GIT_SHA: ${env:GIT_SHA, ""}
|
|
193
|
-
CREDENTIAL_REFRESH_ENABLED: ${env:CREDENTIAL_REFRESH_ENABLED, "false"}
|
|
194
|
-
CREDENTIAL_REFRESH_DIGEST_SECRET: ${env:CREDENTIAL_REFRESH_DIGEST_SECRET, ""}
|
|
195
|
-
CREDENTIAL_REFRESH_NOTIFICATION_WINDOW_HOURS: ${env:CREDENTIAL_REFRESH_NOTIFICATION_WINDOW_HOURS, "24"}
|
|
196
|
-
# Stable HMAC key for passcode-bearing share-link request fingerprints.
|
|
197
|
-
SHARE_LINK_REQUEST_HASH_SECRET: ${env:SHARE_LINK_REQUEST_HASH_SECRET, ""}
|
|
198
|
-
# LC-2187 share-link service wiring and automatic maintenance. Every value is
|
|
199
|
-
# bounded and re-validated fail-closed at runtime. Never read from the
|
|
200
|
-
# scheduled event payload.
|
|
201
|
-
SHARE_LINK_MAINTENANCE_NAMESPACE: ${env:SHARE_LINK_MAINTENANCE_NAMESPACE, ""}
|
|
202
|
-
SHARE_LINK_MAINTENANCE_ORIGIN: ${env:SHARE_LINK_MAINTENANCE_ORIGIN, ""}
|
|
203
|
-
SHARE_LINK_MAINTENANCE_AUDIENCE: ${env:SHARE_LINK_MAINTENANCE_AUDIENCE, ""}
|
|
204
|
-
SHARE_LINK_MAINTENANCE_ALLOW_INSECURE_LOOPBACK: ${env:SHARE_LINK_MAINTENANCE_ALLOW_INSECURE_LOOPBACK, "false"}
|
|
205
|
-
SHARE_LINK_MAINTENANCE_CLAIMANT: ${env:SHARE_LINK_MAINTENANCE_CLAIMANT, "brain-share-link-maintenance"}
|
|
206
|
-
SHARE_LINK_MAINTENANCE_RECOVERY_LIMIT: ${env:SHARE_LINK_MAINTENANCE_RECOVERY_LIMIT, "25"}
|
|
207
|
-
SHARE_LINK_MAINTENANCE_CLEANUP_LIMIT: ${env:SHARE_LINK_MAINTENANCE_CLEANUP_LIMIT, "25"}
|
|
208
|
-
SHARE_LINK_MAINTENANCE_LEASE_MS: ${env:SHARE_LINK_MAINTENANCE_LEASE_MS, "120000"}
|
|
209
|
-
SHARE_LINK_MAINTENANCE_CLAIM_MS: ${env:SHARE_LINK_MAINTENANCE_CLAIM_MS, "300000"}
|
|
210
|
-
SHARE_LINK_MAINTENANCE_BUDGET_MS: ${env:SHARE_LINK_MAINTENANCE_BUDGET_MS, "60000"}
|
|
211
|
-
SHARE_LINK_MAINTENANCE_INTERVAL_MS: ${env:SHARE_LINK_MAINTENANCE_INTERVAL_MS, "300000"}
|
|
212
|
-
SHARE_LINK_MAINTENANCE_REQUEST_TIMEOUT_MS: ${env:SHARE_LINK_MAINTENANCE_REQUEST_TIMEOUT_MS, "10000"}
|
|
213
|
-
SHARE_LINK_MAINTENANCE_FINALIZE_RESERVE_MS: ${env:SHARE_LINK_MAINTENANCE_FINALIZE_RESERVE_MS, "5000"}
|
|
214
|
-
SHARE_LINK_MAINTENANCE_GRAPH_TIMEOUT_MS: ${env:SHARE_LINK_MAINTENANCE_GRAPH_TIMEOUT_MS, "15000"}
|
|
143
|
+
# Every function shares the default execution role. Grant it read access to the stage's
|
|
144
|
+
# runtime-secrets bundle only; the id itself is passed through RUNTIME_SECRETS_ID.
|
|
145
|
+
iam:
|
|
146
|
+
role:
|
|
147
|
+
statements:
|
|
148
|
+
- Effect: Allow
|
|
149
|
+
Action: secretsmanager:GetSecretValue
|
|
150
|
+
Resource:
|
|
151
|
+
Fn::Sub: arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:brain-service/${sls:stage}/runtime-secrets-*
|
|
152
|
+
# Bundle mode emits only infra (Redis/queue CF intrinsics, the bundle pointer and the
|
|
153
|
+
# deploy commit); fallback mode retains the existing GitHub credential secrets. Non-secret
|
|
154
|
+
# per-stage tuning, allowlists and OIDC/share-link config are applied from checked-in stage
|
|
155
|
+
# config at cold start, never forwarded here. LAMBDA_STAGE comes from the CLI stage option.
|
|
156
|
+
environment: ${file(./serverless.function-env.cjs):provider}
|
|
215
157
|
tracing:
|
|
216
158
|
apiGateway: true
|
|
217
159
|
lambda: true
|
|
@@ -263,6 +205,19 @@ functions:
|
|
|
263
205
|
rate: rate(1 day)
|
|
264
206
|
enabled: true
|
|
265
207
|
|
|
208
|
+
# Retry durable consent notification intents once per minute.
|
|
209
|
+
contractEvents:
|
|
210
|
+
handler: contractEventsLambda.contractEventsHandler
|
|
211
|
+
timeout: 60
|
|
212
|
+
reservedConcurrency: 1
|
|
213
|
+
vpc:
|
|
214
|
+
securityGroupIds:
|
|
215
|
+
- 'Fn::GetAtt': [ServerlessSecurityGroup, GroupId]
|
|
216
|
+
subnetIds:
|
|
217
|
+
- Ref: PrivateSubnetA
|
|
218
|
+
events:
|
|
219
|
+
- schedule: rate(1 minute)
|
|
220
|
+
|
|
266
221
|
# LC-2187 share-link recovery + cleanup. Dedicated entrypoint (no lambda.ts
|
|
267
222
|
# import-time work). Runs automatically with valid service configuration;
|
|
268
223
|
# absent wiring remains inert and partial wiring fails closed. The cadence
|
|
@@ -1,49 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { bootstrapLambda } from '@learncard/service-config';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
createShareLinkMaintenanceRuntime,
|
|
5
|
-
type ShareLinkMaintenanceRuntime,
|
|
6
|
-
} from './src/helpers/share-link-maintenance';
|
|
3
|
+
import { base, stages } from './src/config/stageConfig';
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
* Dedicated LC-2187 share-link maintenance Lambda entrypoint.
|
|
10
|
-
*
|
|
11
|
-
* It deliberately does NOT import `lambda.ts`, so no unrelated embedding
|
|
12
|
-
* backfill, tRPC router, Sentry input capture or inbox handler runs at import
|
|
13
|
-
* time. The only side effect is resolving the service and maintenance
|
|
14
|
-
* configuration; when it is absent or malformed the runtime is inert and the
|
|
15
|
-
* handler performs no graph, remote or signing work.
|
|
16
|
-
*
|
|
17
|
-
* The scheduled event payload is ignored entirely: it can never override the
|
|
18
|
-
* namespace, origin, audience or any other configuration, and it is never
|
|
19
|
-
* logged.
|
|
20
|
-
*/
|
|
5
|
+
type ShareLinkMaintenanceApp = typeof import('./shareLinkMaintenanceLambdaApp');
|
|
21
6
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return runtime;
|
|
32
|
-
};
|
|
7
|
+
const getApplication = bootstrapLambda<ShareLinkMaintenanceApp>({
|
|
8
|
+
base,
|
|
9
|
+
stages,
|
|
10
|
+
stage: process.env.AWS_LAMBDA_FUNCTION_NAME
|
|
11
|
+
? process.env.LAMBDA_STAGE
|
|
12
|
+
: process.env.CONFIG_STAGE,
|
|
13
|
+
importApp: () => import('./shareLinkMaintenanceLambdaApp'),
|
|
14
|
+
});
|
|
33
15
|
|
|
34
16
|
export const shareLinkMaintenanceHandler = async (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
): Promise<{ status: string }> => {
|
|
38
|
-
const hasRemainingTime = typeof context?.getRemainingTimeInMillis === 'function';
|
|
39
|
-
|
|
40
|
-
const summary = await getRuntime().runOnce({
|
|
41
|
-
// Preserve the LIVE Lambda remaining-time source: the runtime samples it
|
|
42
|
-
// once, before dependency initialization, and anchors the invocation
|
|
43
|
-
// budget to that instant.
|
|
44
|
-
remainingTimeMs: hasRemainingTime ? () => context!.getRemainingTimeInMillis() : undefined,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// The runtime already emitted one aggregate-only allowlisted event.
|
|
48
|
-
return { status: summary.status };
|
|
49
|
-
};
|
|
17
|
+
...args: Parameters<ShareLinkMaintenanceApp['shareLinkMaintenanceHandler']>
|
|
18
|
+
) => (await getApplication()).shareLinkMaintenanceHandler(...args);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Context } from 'aws-lambda';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createShareLinkMaintenanceRuntime,
|
|
5
|
+
type ShareLinkMaintenanceRuntime,
|
|
6
|
+
} from './src/helpers/share-link-maintenance';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Dedicated LC-2187 share-link maintenance application module.
|
|
10
|
+
*
|
|
11
|
+
* It is imported lazily by the thin bootstrap entrypoint, after checked-in
|
|
12
|
+
* stage config has been applied, so no unrelated embedding backfill, tRPC
|
|
13
|
+
* router, Sentry input capture or inbox handler runs at import time. The only
|
|
14
|
+
* side effect is resolving the service and maintenance configuration; when it
|
|
15
|
+
* is absent or malformed the runtime is inert and the handler performs no
|
|
16
|
+
* graph, remote or signing work.
|
|
17
|
+
*
|
|
18
|
+
* The scheduled event payload is ignored entirely: it can never override the
|
|
19
|
+
* namespace, origin, audience or any other configuration, and it is never
|
|
20
|
+
* logged.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
let runtime: ShareLinkMaintenanceRuntime | null = null;
|
|
24
|
+
|
|
25
|
+
const getRuntime = (): ShareLinkMaintenanceRuntime => {
|
|
26
|
+
if (runtime === null) {
|
|
27
|
+
runtime = createShareLinkMaintenanceRuntime({
|
|
28
|
+
rawEnvironment: process.env as Record<string, unknown>,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return runtime;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const shareLinkMaintenanceHandler = async (
|
|
36
|
+
_event: unknown,
|
|
37
|
+
context?: Pick<Context, 'getRemainingTimeInMillis'>
|
|
38
|
+
): Promise<{ status: string }> => {
|
|
39
|
+
const hasRemainingTime = typeof context?.getRemainingTimeInMillis === 'function';
|
|
40
|
+
|
|
41
|
+
const summary = await getRuntime().runOnce({
|
|
42
|
+
// Preserve the LIVE Lambda remaining-time source: the runtime samples it
|
|
43
|
+
// once, before dependency initialization, and anchors the invocation
|
|
44
|
+
// budget to that instant.
|
|
45
|
+
remainingTimeMs: hasRemainingTime ? () => context!.getRemainingTimeInMillis() : undefined,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// The runtime already emitted one aggregate-only allowlisted event.
|
|
49
|
+
return { status: summary.status };
|
|
50
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { BindParam, QueryBuilder } from 'neogma';
|
|
2
|
+
import { v4 as uuid } from 'uuid';
|
|
3
|
+
|
|
4
|
+
import { Integration, AppManifestVersion } from '@models';
|
|
5
|
+
import type { AppManifest } from '@learncard/types';
|
|
6
|
+
|
|
7
|
+
import type { AppManifestVersionType } from 'types/app-manifest';
|
|
8
|
+
|
|
9
|
+
import { readAppManifestVersionById } from './read';
|
|
10
|
+
|
|
11
|
+
export const createAppManifestVersion = async ({
|
|
12
|
+
integrationId,
|
|
13
|
+
version,
|
|
14
|
+
manifestHash,
|
|
15
|
+
manifest,
|
|
16
|
+
status = 'draft',
|
|
17
|
+
}: {
|
|
18
|
+
integrationId: string;
|
|
19
|
+
version: number;
|
|
20
|
+
manifestHash: string;
|
|
21
|
+
manifest: AppManifest;
|
|
22
|
+
status?: 'draft' | 'active' | 'superseded';
|
|
23
|
+
}): Promise<AppManifestVersionType> => {
|
|
24
|
+
const id = uuid();
|
|
25
|
+
const createdAt = new Date().toISOString();
|
|
26
|
+
|
|
27
|
+
await new QueryBuilder(
|
|
28
|
+
new BindParam({
|
|
29
|
+
integrationId,
|
|
30
|
+
params: {
|
|
31
|
+
id,
|
|
32
|
+
version,
|
|
33
|
+
manifestHash,
|
|
34
|
+
manifestJson: JSON.stringify(manifest),
|
|
35
|
+
status,
|
|
36
|
+
createdAt,
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
)
|
|
40
|
+
.match({ model: Integration, identifier: 'integration', where: { id: integrationId } })
|
|
41
|
+
.create({ model: AppManifestVersion, identifier: 'manifestVersion' })
|
|
42
|
+
.set('manifestVersion += $params')
|
|
43
|
+
.create(
|
|
44
|
+
`(integration)-[:${
|
|
45
|
+
Integration.getRelationshipByAlias('hasManifestVersion').name
|
|
46
|
+
}]->(manifestVersion)`
|
|
47
|
+
)
|
|
48
|
+
.run();
|
|
49
|
+
|
|
50
|
+
const created = await readAppManifestVersionById(id);
|
|
51
|
+
|
|
52
|
+
if (!created) {
|
|
53
|
+
throw new Error('Failed to create app manifest version');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return created;
|
|
57
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { neogma } from '@instance';
|
|
2
|
+
|
|
3
|
+
import { AppManifestVersionValidator } from '@learncard/types';
|
|
4
|
+
|
|
5
|
+
import type { AppManifestVersionType } from 'types/app-manifest';
|
|
6
|
+
|
|
7
|
+
const parseManifestVersionRecord = (
|
|
8
|
+
properties: Record<string, unknown>
|
|
9
|
+
): AppManifestVersionType => {
|
|
10
|
+
const manifestJson = properties.manifestJson;
|
|
11
|
+
const rawVersion = properties.version as { toNumber?: () => number } | number;
|
|
12
|
+
|
|
13
|
+
if (typeof manifestJson !== 'string') {
|
|
14
|
+
throw new Error('AppManifestVersion.manifestJson is missing');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return AppManifestVersionValidator.parse({
|
|
18
|
+
id: properties.id,
|
|
19
|
+
version: typeof rawVersion === 'number' ? rawVersion : rawVersion?.toNumber?.(),
|
|
20
|
+
manifestHash: properties.manifestHash,
|
|
21
|
+
manifest: JSON.parse(manifestJson),
|
|
22
|
+
status: properties.status,
|
|
23
|
+
createdAt: properties.createdAt,
|
|
24
|
+
activatedAt: properties.activatedAt,
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const readAppManifestVersionById = async (
|
|
29
|
+
id: string
|
|
30
|
+
): Promise<AppManifestVersionType | null> => {
|
|
31
|
+
const result = await neogma.queryRunner.run(
|
|
32
|
+
`MATCH (manifestVersion:AppManifestVersion {id: $id})
|
|
33
|
+
RETURN manifestVersion
|
|
34
|
+
LIMIT 1`,
|
|
35
|
+
{ id }
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const properties = result.records[0]?.get('manifestVersion')?.properties;
|
|
39
|
+
|
|
40
|
+
if (!properties) return null;
|
|
41
|
+
|
|
42
|
+
return parseManifestVersionRecord(properties as Record<string, unknown>);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const getManifestVersionForIntegration = async (
|
|
46
|
+
integrationId: string,
|
|
47
|
+
version: number
|
|
48
|
+
): Promise<AppManifestVersionType | null> => {
|
|
49
|
+
const result = await neogma.queryRunner.run(
|
|
50
|
+
`MATCH (:Integration {id: $integrationId})-[:HAS_MANIFEST_VERSION]->(manifestVersion:AppManifestVersion {version: $version})
|
|
51
|
+
RETURN manifestVersion
|
|
52
|
+
LIMIT 1`,
|
|
53
|
+
{ integrationId, version }
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const properties = result.records[0]?.get('manifestVersion')?.properties;
|
|
57
|
+
|
|
58
|
+
if (!properties) return null;
|
|
59
|
+
|
|
60
|
+
return parseManifestVersionRecord(properties as Record<string, unknown>);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const getLatestManifestVersionForIntegration = async (
|
|
64
|
+
integrationId: string
|
|
65
|
+
): Promise<AppManifestVersionType | null> => {
|
|
66
|
+
const result = await neogma.queryRunner.run(
|
|
67
|
+
`MATCH (:Integration {id: $integrationId})-[:HAS_MANIFEST_VERSION]->(manifestVersion:AppManifestVersion)
|
|
68
|
+
RETURN manifestVersion
|
|
69
|
+
ORDER BY manifestVersion.version DESC
|
|
70
|
+
LIMIT 1`,
|
|
71
|
+
{ integrationId }
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const properties = result.records[0]?.get('manifestVersion')?.properties;
|
|
75
|
+
|
|
76
|
+
if (!properties) return null;
|
|
77
|
+
|
|
78
|
+
return parseManifestVersionRecord(properties as Record<string, unknown>);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const getActiveManifestVersionForIntegration = async (
|
|
82
|
+
integrationId: string
|
|
83
|
+
): Promise<AppManifestVersionType | null> => {
|
|
84
|
+
const result = await neogma.queryRunner.run(
|
|
85
|
+
`MATCH (:Integration {id: $integrationId})-[:HAS_MANIFEST_VERSION]->(manifestVersion:AppManifestVersion {status: 'active'})
|
|
86
|
+
RETURN manifestVersion
|
|
87
|
+
ORDER BY manifestVersion.activatedAt DESC, manifestVersion.version DESC
|
|
88
|
+
LIMIT 1`,
|
|
89
|
+
{ integrationId }
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const properties = result.records[0]?.get('manifestVersion')?.properties;
|
|
93
|
+
|
|
94
|
+
if (!properties) return null;
|
|
95
|
+
|
|
96
|
+
return parseManifestVersionRecord(properties as Record<string, unknown>);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const getManifestVersionsForIntegration = async (
|
|
100
|
+
integrationId: string,
|
|
101
|
+
{ limit, cursor }: { limit: number; cursor?: string }
|
|
102
|
+
): Promise<AppManifestVersionType[]> => {
|
|
103
|
+
const result = await neogma.queryRunner.run(
|
|
104
|
+
`MATCH (:Integration {id: $integrationId})-[:HAS_MANIFEST_VERSION]->(manifestVersion:AppManifestVersion)
|
|
105
|
+
${cursor ? 'WHERE manifestVersion.version < toInteger($cursor)' : ''}
|
|
106
|
+
RETURN manifestVersion
|
|
107
|
+
ORDER BY manifestVersion.version DESC
|
|
108
|
+
LIMIT toInteger($limit)`,
|
|
109
|
+
{ integrationId, cursor: cursor ?? null, limit }
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
return result.records.map(record => {
|
|
113
|
+
const properties = record.get('manifestVersion')?.properties as Record<string, unknown>;
|
|
114
|
+
return parseManifestVersionRecord(properties);
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const getListingIdForManifestVersion = async (id: string): Promise<string | null> => {
|
|
119
|
+
const result = await neogma.queryRunner.run(
|
|
120
|
+
`MATCH (listing:AppStoreListing)-[:USES_MANIFEST_VERSION]->(:AppManifestVersion {id: $id})
|
|
121
|
+
RETURN listing.listing_id AS listingId
|
|
122
|
+
LIMIT 1`,
|
|
123
|
+
{ id }
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const listingId = result.records[0]?.get('listingId');
|
|
127
|
+
|
|
128
|
+
return typeof listingId === 'string' ? listingId : null;
|
|
129
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { neogma } from '@instance';
|
|
2
|
+
|
|
3
|
+
export const updateAppManifestVersion = async (
|
|
4
|
+
id: string,
|
|
5
|
+
updates: Partial<{
|
|
6
|
+
status: 'draft' | 'active' | 'superseded';
|
|
7
|
+
activatedAt: string | null;
|
|
8
|
+
}>
|
|
9
|
+
): Promise<boolean> => {
|
|
10
|
+
const setClauses: string[] = [];
|
|
11
|
+
const params: Record<string, unknown> = { id };
|
|
12
|
+
|
|
13
|
+
if (typeof updates.status !== 'undefined') {
|
|
14
|
+
params.status = updates.status;
|
|
15
|
+
setClauses.push('manifestVersion.status = $status');
|
|
16
|
+
}
|
|
17
|
+
if (typeof updates.activatedAt !== 'undefined') {
|
|
18
|
+
params.activatedAt = updates.activatedAt;
|
|
19
|
+
setClauses.push('manifestVersion.activatedAt = $activatedAt');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (setClauses.length === 0) return true;
|
|
23
|
+
|
|
24
|
+
const result = await neogma.queryRunner.run(
|
|
25
|
+
`MATCH (manifestVersion:AppManifestVersion {id: $id})
|
|
26
|
+
SET ${setClauses.join(', ')}`,
|
|
27
|
+
params
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return result.summary.updateStatistics.containsUpdates();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const markManifestVersionsSuperseded = async (
|
|
34
|
+
integrationId: string,
|
|
35
|
+
exceptId: string
|
|
36
|
+
): Promise<boolean> => {
|
|
37
|
+
const result = await neogma.queryRunner.run(
|
|
38
|
+
`MATCH (:Integration {id: $integrationId})-[:HAS_MANIFEST_VERSION]->(manifestVersion:AppManifestVersion)
|
|
39
|
+
WHERE manifestVersion.id <> $exceptId AND manifestVersion.status = 'active'
|
|
40
|
+
SET manifestVersion.status = 'superseded'`,
|
|
41
|
+
{ integrationId, exceptId }
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return result.summary.updateStatistics.containsUpdates();
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const associateListingWithManifestVersion = async (
|
|
48
|
+
listingId: string,
|
|
49
|
+
manifestVersionId: string
|
|
50
|
+
): Promise<boolean> => {
|
|
51
|
+
await neogma.queryRunner.run(
|
|
52
|
+
`MATCH (listing:AppStoreListing {listing_id: $listingId})
|
|
53
|
+
MATCH (manifestVersion:AppManifestVersion {id: $manifestVersionId})
|
|
54
|
+
OPTIONAL MATCH (listing)-[existing:USES_MANIFEST_VERSION]->(:AppManifestVersion)
|
|
55
|
+
DELETE existing
|
|
56
|
+
MERGE (listing)-[:USES_MANIFEST_VERSION]->(manifestVersion)`,
|
|
57
|
+
{ listingId, manifestVersionId }
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return true;
|
|
61
|
+
};
|
|
@@ -244,16 +244,22 @@ export const getListedAppsWithSubmitter = async ({
|
|
|
244
244
|
status,
|
|
245
245
|
includeAllStatuses = false,
|
|
246
246
|
excludeDemoted = true,
|
|
247
|
+
pendingUpdatesOnly = false,
|
|
247
248
|
}: {
|
|
248
249
|
limit: number;
|
|
249
250
|
cursor?: string;
|
|
250
251
|
status?: string;
|
|
251
252
|
includeAllStatuses?: boolean;
|
|
252
253
|
excludeDemoted?: boolean;
|
|
254
|
+
pendingUpdatesOnly?: boolean;
|
|
253
255
|
}): Promise<ListingWithSubmitter[]> => {
|
|
254
256
|
const whereClauses: string[] = [];
|
|
255
257
|
const params: Record<string, any> = { limit: int(limit) };
|
|
256
258
|
|
|
259
|
+
if (pendingUpdatesOnly) {
|
|
260
|
+
whereClauses.push("listing.pending_update_status = 'PENDING_REVIEW'");
|
|
261
|
+
}
|
|
262
|
+
|
|
257
263
|
if (status) {
|
|
258
264
|
whereClauses.push('listing.app_listing_status = $status');
|
|
259
265
|
params.status = status;
|
|
@@ -96,6 +96,26 @@ export const associateBoostWithListing = async (
|
|
|
96
96
|
return true;
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
+
export const associateConsentContractWithListing = async (
|
|
100
|
+
listingId: string,
|
|
101
|
+
contractId: string,
|
|
102
|
+
scopeHash: string
|
|
103
|
+
): Promise<boolean> => {
|
|
104
|
+
await AppStoreListing.relateTo({
|
|
105
|
+
alias: 'hasConsentContract',
|
|
106
|
+
where: {
|
|
107
|
+
source: { listing_id: listingId },
|
|
108
|
+
target: { id: contractId },
|
|
109
|
+
},
|
|
110
|
+
properties: {
|
|
111
|
+
scopeHash,
|
|
112
|
+
createdAt: new Date().toISOString(),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return true;
|
|
117
|
+
};
|
|
118
|
+
|
|
99
119
|
export const associateListingWithSigningAuthority = async (
|
|
100
120
|
listingId: string,
|
|
101
121
|
signingAuthorityEndpoint: string,
|
|
@@ -6,6 +6,8 @@ import { Boost, BoostInstance } from '@models';
|
|
|
6
6
|
import { int } from 'neo4j-driver';
|
|
7
7
|
import { getBoostUri } from '@helpers/boost.helpers';
|
|
8
8
|
import { parseIntegration } from '@helpers/integrations.helpers';
|
|
9
|
+
import { getContractById } from '@accesslayer/consentflowcontract/read';
|
|
10
|
+
import type { DbContractType } from 'types/consentflowcontract';
|
|
9
11
|
|
|
10
12
|
export const getIntegrationForListing = async (
|
|
11
13
|
listingIdOrSlug: string
|
|
@@ -93,7 +95,9 @@ export const getBoostForListingByTemplateAlias = async (
|
|
|
93
95
|
): Promise<{ boost: BoostInstance; templateAlias: string; boostUri: string } | null> => {
|
|
94
96
|
const result = await neogma.queryRunner.run(
|
|
95
97
|
`MATCH (listing:AppStoreListing {listing_id: $listingId})-[r:HAS_BOOST {templateAlias: $templateAlias}]->(b:Boost)
|
|
96
|
-
RETURN b.id AS id, r.templateAlias AS templateAlias
|
|
98
|
+
RETURN b.id AS id, r.templateAlias AS templateAlias
|
|
99
|
+
ORDER BY r.createdAt DESC
|
|
100
|
+
LIMIT 1`,
|
|
97
101
|
{ listingId, templateAlias }
|
|
98
102
|
);
|
|
99
103
|
|
|
@@ -146,6 +150,25 @@ export const hasTemplateAliasForListing = async (
|
|
|
146
150
|
return result.records[0]?.get('exists') ?? false;
|
|
147
151
|
};
|
|
148
152
|
|
|
153
|
+
export const getConsentContractForListingByScopeHash = async (
|
|
154
|
+
listingId: string,
|
|
155
|
+
scopeHash: string
|
|
156
|
+
): Promise<DbContractType | null> => {
|
|
157
|
+
const result = await neogma.queryRunner.run(
|
|
158
|
+
`MATCH (listing:AppStoreListing {listing_id: $listingId})
|
|
159
|
+
-[:HAS_CONSENT_CONTRACT {scopeHash: $scopeHash}]->(contract:ConsentFlowContract)
|
|
160
|
+
RETURN contract.id AS id
|
|
161
|
+
ORDER BY contract.createdAt ASC
|
|
162
|
+
LIMIT 1`,
|
|
163
|
+
{ listingId, scopeHash }
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const id = result.records[0]?.get('id');
|
|
167
|
+
if (typeof id !== 'string') return null;
|
|
168
|
+
|
|
169
|
+
return getContractById(id);
|
|
170
|
+
};
|
|
171
|
+
|
|
149
172
|
export const getCredentialsSentByListingToProfile = async (
|
|
150
173
|
listingId: string,
|
|
151
174
|
profileId: string,
|