@openhi/constructs 0.0.104 → 0.0.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/lib/chunk-2PM2NGXI.mjs +31 -0
- package/lib/chunk-2PM2NGXI.mjs.map +1 -0
- package/lib/chunk-AGF3RAAZ.mjs +20 -0
- package/lib/chunk-AGF3RAAZ.mjs.map +1 -0
- package/lib/chunk-AO3E22CS.mjs +108 -0
- package/lib/chunk-AO3E22CS.mjs.map +1 -0
- package/lib/chunk-CHPEQRXU.mjs +45 -0
- package/lib/chunk-CHPEQRXU.mjs.map +1 -0
- package/lib/chunk-JUNL76HF.mjs +428 -0
- package/lib/chunk-JUNL76HF.mjs.map +1 -0
- package/lib/chunk-L6UAP4KP.mjs +27 -0
- package/lib/chunk-L6UAP4KP.mjs.map +1 -0
- package/lib/{chunk-3QS3WKRC.mjs → chunk-LZOMFHX3.mjs} +9 -2
- package/lib/chunk-QMIOLLAS.mjs +531 -0
- package/lib/chunk-QMIOLLAS.mjs.map +1 -0
- package/lib/chunk-SYBADQXI.mjs +607 -0
- package/lib/chunk-SYBADQXI.mjs.map +1 -0
- package/lib/chunk-VXX4I3EF.mjs +19 -0
- package/lib/chunk-VXX4I3EF.mjs.map +1 -0
- package/lib/{chunk-MLTYFMSE.mjs → chunk-VYDIGFIX.mjs} +74 -29
- package/lib/chunk-VYDIGFIX.mjs.map +1 -0
- package/lib/chunk-YU2HRNUP.mjs +33 -0
- package/lib/chunk-YU2HRNUP.mjs.map +1 -0
- package/lib/chunk-YZZDUJHI.mjs +37 -0
- package/lib/chunk-YZZDUJHI.mjs.map +1 -0
- package/lib/cors-options-lambda.handler.mjs +1 -1
- package/lib/data-store-postgres-replication.handler.mjs +1 -1
- package/lib/events-BfrkMoBD.d.mts +44 -0
- package/lib/events-BfrkMoBD.d.ts +44 -0
- package/lib/events-CVA3_eEB.d.mts +23 -0
- package/lib/events-CVA3_eEB.d.ts +23 -0
- package/lib/events-DGep6C7w.d.mts +207 -0
- package/lib/events-DGep6C7w.d.ts +207 -0
- package/lib/firehose-archive-transform.handler.mjs +1 -1
- package/lib/index.d.mts +508 -29
- package/lib/index.d.ts +773 -30
- package/lib/index.js +2536 -105
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +899 -106
- package/lib/index.mjs.map +1 -1
- package/lib/openhi-context-CaBH8SFo.d.mts +39 -0
- package/lib/openhi-context-CaBH8SFo.d.ts +39 -0
- package/lib/platform-deploy-bridge.handler.d.mts +14 -0
- package/lib/platform-deploy-bridge.handler.d.ts +14 -0
- package/lib/platform-deploy-bridge.handler.js +762 -0
- package/lib/platform-deploy-bridge.handler.js.map +1 -0
- package/lib/platform-deploy-bridge.handler.mjs +134 -0
- package/lib/platform-deploy-bridge.handler.mjs.map +1 -0
- package/lib/post-authentication.handler.mjs +1 -1
- package/lib/post-confirmation.handler.js +50 -904
- package/lib/post-confirmation.handler.js.map +1 -1
- package/lib/post-confirmation.handler.mjs +37 -112
- package/lib/post-confirmation.handler.mjs.map +1 -1
- package/lib/pre-token-generation.handler.js +135 -55
- package/lib/pre-token-generation.handler.js.map +1 -1
- package/lib/pre-token-generation.handler.mjs +25 -32
- package/lib/pre-token-generation.handler.mjs.map +1 -1
- package/lib/provision-default-workspace.handler.d.mts +13 -0
- package/lib/provision-default-workspace.handler.d.ts +13 -0
- package/lib/provision-default-workspace.handler.js +1172 -0
- package/lib/provision-default-workspace.handler.js.map +1 -0
- package/lib/provision-default-workspace.handler.mjs +175 -0
- package/lib/provision-default-workspace.handler.mjs.map +1 -0
- package/lib/rest-api-lambda.handler.js +114 -59
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +60 -587
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/lib/seed-demo-data.handler.d.mts +107 -0
- package/lib/seed-demo-data.handler.d.ts +107 -0
- package/lib/seed-demo-data.handler.js +2037 -0
- package/lib/seed-demo-data.handler.js.map +1 -0
- package/lib/seed-demo-data.handler.mjs +23 -0
- package/lib/seed-demo-data.handler.mjs.map +1 -0
- package/lib/seed-system-data.handler.d.mts +64 -0
- package/lib/seed-system-data.handler.d.ts +64 -0
- package/lib/seed-system-data.handler.js +1631 -0
- package/lib/seed-system-data.handler.js.map +1 -0
- package/lib/seed-system-data.handler.mjs +135 -0
- package/lib/seed-system-data.handler.mjs.map +1 -0
- package/package.json +4 -2
- package/lib/chunk-MLTYFMSE.mjs.map +0 -1
- /package/lib/{chunk-3QS3WKRC.mjs.map → chunk-LZOMFHX3.mjs.map} +0 -0
package/README.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
1
|
# @openhi/constructs
|
|
2
2
|
|
|
3
3
|
AWS CDK constructs for deploying OpenHI infrastructure. The library provides reusable constructs for applications, shared infrastructure (auth and core resources), data services, and common components used across OpenHI stacks.
|
|
4
|
+
|
|
5
|
+
## Source Boundaries
|
|
6
|
+
|
|
7
|
+
- `src/components`: reusable infrastructure building blocks and thin service integrations around AWS primitives.
|
|
8
|
+
- `src/data`: persistence models, data access operations, API routes, and storage-specific handlers.
|
|
9
|
+
- `src/workflows`: deployed async business processes, their EventBridge event contracts, workflow Lambda constructs, handlers, and tests.
|
|
10
|
+
|
|
11
|
+
Workflow Lambdas should live under a plane namespace such as `src/workflows/control-plane` or `src/workflows/data-plane`. Trigger-specific handlers in `components` or API routes should stay thin and publish workflow events when the business process can run asynchronously.
|
|
12
|
+
|
|
13
|
+
Use `events.ts` for EventBridge contracts, `<workflow>.handler.ts` for runtime handlers, `<workflow>-lambda.ts` for Lambda constructs, `<workflow>.handler.test.ts` for handler tests, and a `<domain>-workflow.ts` construct when EventBridge rules or multiple workflow Lambdas are deployed together.
|
|
14
|
+
|
|
15
|
+
## Strategy
|
|
16
|
+
|
|
17
|
+
Workflow placement (bus selection, Lambda vs. Step Functions, standard event envelope, idempotency contract, cross-boundary rules) is governed by [ADR-016 — Workflow Boundary Strategy](https://github.com/codedrifters/openhi-planning/blob/main/docs/src/content/docs/requirements/architectural-decisions/ADR-016-workflow-boundary-strategy.md) in the planning repo.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/workflows/control-plane/user-onboarding/events.ts
|
|
2
|
+
var USER_ONBOARDING_EVENT_SOURCE = "openhi.control.user-onboarding";
|
|
3
|
+
var PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE = "ProvisionDefaultWorkspaceRequested";
|
|
4
|
+
var buildProvisionDefaultWorkspaceRequestedDetail = (event) => {
|
|
5
|
+
const attrs = event.request?.userAttributes ?? {};
|
|
6
|
+
const cognitoSub = attrs.sub?.trim();
|
|
7
|
+
if (!cognitoSub) {
|
|
8
|
+
return void 0;
|
|
9
|
+
}
|
|
10
|
+
const email = attrs.email?.trim();
|
|
11
|
+
const displayName = email || event.userName || cognitoSub;
|
|
12
|
+
return {
|
|
13
|
+
cognitoSub,
|
|
14
|
+
...email ? { email } : {},
|
|
15
|
+
displayName,
|
|
16
|
+
trigger: {
|
|
17
|
+
source: "cognito.post-confirmation",
|
|
18
|
+
triggerSource: event.triggerSource,
|
|
19
|
+
userPoolId: event.userPoolId,
|
|
20
|
+
userName: event.userName,
|
|
21
|
+
clientId: event.callerContext?.clientId
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
USER_ONBOARDING_EVENT_SOURCE,
|
|
28
|
+
PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE,
|
|
29
|
+
buildProvisionDefaultWorkspaceRequestedDetail
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=chunk-2PM2NGXI.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/workflows/control-plane/user-onboarding/events.ts"],"sourcesContent":["import type { PostConfirmationTriggerEvent } from \"aws-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/events.md\n */\n\n// EventBridge routing values shared by the publisher and workflow rule.\nexport const USER_ONBOARDING_EVENT_SOURCE = \"openhi.control.user-onboarding\";\nexport const PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE =\n \"ProvisionDefaultWorkspaceRequested\";\n\n// Minimal workflow payload needed to provision and diagnose onboarding.\nexport interface ProvisionDefaultWorkspaceRequestedDetail {\n readonly cognitoSub: string;\n readonly userId?: string;\n readonly email?: string;\n readonly displayName?: string;\n readonly trigger: {\n readonly source: \"cognito.post-confirmation\";\n readonly triggerSource?: string;\n readonly userPoolId?: string;\n readonly userName?: string;\n readonly clientId?: string;\n };\n}\n\n// Convert Cognito's Post Confirmation trigger event into the workflow event.\nexport const buildProvisionDefaultWorkspaceRequestedDetail = (\n event: PostConfirmationTriggerEvent,\n): ProvisionDefaultWorkspaceRequestedDetail | undefined => {\n // Cognito sub is the required stable identity key for onboarding records.\n const attrs = event.request?.userAttributes ?? {};\n const cognitoSub = attrs.sub?.trim();\n if (!cognitoSub) {\n return undefined;\n }\n\n // Prefer email for display where available; fall back to Cognito metadata.\n const email = attrs.email?.trim();\n const displayName = email || event.userName || cognitoSub;\n\n // Include trigger metadata so failed or duplicate events can be traced.\n return {\n cognitoSub,\n ...(email ? { email } : {}),\n displayName,\n trigger: {\n source: \"cognito.post-confirmation\",\n triggerSource: event.triggerSource,\n userPoolId: event.userPoolId,\n userName: event.userName,\n clientId: event.callerContext?.clientId,\n },\n };\n};\n"],"mappings":";AAOO,IAAM,+BAA+B;AACrC,IAAM,0CACX;AAkBK,IAAM,gDAAgD,CAC3D,UACyD;AAEzD,QAAM,QAAQ,MAAM,SAAS,kBAAkB,CAAC;AAChD,QAAM,aAAa,MAAM,KAAK,KAAK;AACnC,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,MAAM,OAAO,KAAK;AAChC,QAAM,cAAc,SAAS,MAAM,YAAY;AAG/C,SAAO;AAAA,IACL;AAAA,IACA,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IACzB;AAAA,IACA,SAAS;AAAA,MACP,QAAQ;AAAA,MACR,eAAe,MAAM;AAAA,MACrB,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,UAAU,MAAM,eAAe;AAAA,IACjC;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
require_lib
|
|
3
|
+
} from "./chunk-SYBADQXI.mjs";
|
|
4
|
+
import {
|
|
5
|
+
__toESM
|
|
6
|
+
} from "./chunk-LZOMFHX3.mjs";
|
|
7
|
+
|
|
8
|
+
// src/workflows/control-plane/seed-system-data/events.ts
|
|
9
|
+
var import_workflows = __toESM(require_lib());
|
|
10
|
+
var SEED_SYSTEM_DATA_CONSUMER_NAME = "seed-system-data";
|
|
11
|
+
var SEED_SYSTEM_DATA_ACTOR_SYSTEM = "seed-system-data";
|
|
12
|
+
var SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR = "CONTROL_EVENT_BUS_NAME";
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
import_workflows,
|
|
16
|
+
SEED_SYSTEM_DATA_CONSUMER_NAME,
|
|
17
|
+
SEED_SYSTEM_DATA_ACTOR_SYSTEM,
|
|
18
|
+
SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=chunk-AGF3RAAZ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/workflows/control-plane/seed-system-data/events.ts"],"sourcesContent":["import {\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n} from \"@openhi/workflows\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/events.md\n */\n\n/**\n * Stable logical name this workflow registers with the shared\n * `WorkflowDedupTable` (TR-015). Used in both the construct grant\n * (`workflowDedupTable.grantConsumer(lambda, SEED_SYSTEM_DATA_CONSUMER_NAME)`)\n * and the handler's runtime `recordIfAbsent` call — keep them aligned by\n * importing this constant in both places.\n */\nexport const SEED_SYSTEM_DATA_CONSUMER_NAME = \"seed-system-data\";\n\n/**\n * Free-form `actor.system` value the handler stamps on the\n * `platform.system-data-seeded.v1` event it publishes when seeding\n * completes. Pinned here so the test can assert the wire value without\n * importing private handler internals.\n */\nexport const SEED_SYSTEM_DATA_ACTOR_SYSTEM = \"seed-system-data\";\n\n/**\n * Env var the Lambda construct injects with the control event bus\n * name. The handler reads it to build the publisher target when\n * emitting `platform.system-data-seeded.v1` after a successful seed.\n */\nexport const SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR = \"CONTROL_EVENT_BUS_NAME\";\n\n/**\n * The trigger this workflow subscribes to on the control event bus. The\n * `platform-deploy-bridge` workflow (#960) republishes terminal CloudFormation\n * stack-success events as `platform.deployment-completed.v1`; on every fire\n * this workflow re-asserts the platform-singleton control-plane records\n * (today: the three canonical Roles; future: additional system data).\n *\n * Re-exported from `@openhi/workflows` for symmetry with the handler's\n * import — keep the construct + handler reading from the same registry\n * entry so a rename upstream surfaces at both call sites.\n */\nexport { PlatformDeploymentCompletedV1, PlatformSystemDataSeededV1 };\n"],"mappings":";;;;;;;;AAAA,uBAGO;AAaA,IAAM,iCAAiC;AAQvC,IAAM,gCAAgC;AAOtC,IAAM,uCAAuC;","names":[]}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDynamoControlService
|
|
3
|
+
} from "./chunk-VYDIGFIX.mjs";
|
|
4
|
+
|
|
5
|
+
// src/data/operations/control/membership/membership-create-operation.ts
|
|
6
|
+
import { extractSummary } from "@openhi/types";
|
|
7
|
+
async function createMembershipOperation(params) {
|
|
8
|
+
const { context, body, tableName } = params;
|
|
9
|
+
const service = getDynamoControlService(tableName);
|
|
10
|
+
const id = body.id ?? `membership-${Date.now()}`;
|
|
11
|
+
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
12
|
+
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
13
|
+
const vid = `1`;
|
|
14
|
+
const resource = { resourceType: "Membership", id, ...parsedResource };
|
|
15
|
+
const summary = JSON.stringify(extractSummary(resource));
|
|
16
|
+
await service.entities.membership.put({
|
|
17
|
+
tenantId: context.tenantId,
|
|
18
|
+
id,
|
|
19
|
+
resource: JSON.stringify(resource),
|
|
20
|
+
summary,
|
|
21
|
+
vid,
|
|
22
|
+
lastUpdated
|
|
23
|
+
}).go();
|
|
24
|
+
return {
|
|
25
|
+
id,
|
|
26
|
+
resource,
|
|
27
|
+
meta: { lastUpdated, versionId: vid }
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/data/operations/control/roleassignment/roleassignment-create-operation.ts
|
|
32
|
+
import { extractSummary as extractSummary2 } from "@openhi/types";
|
|
33
|
+
async function createRoleAssignmentOperation(params) {
|
|
34
|
+
const { context, body, tableName } = params;
|
|
35
|
+
const service = getDynamoControlService(tableName);
|
|
36
|
+
const id = body.id ?? `roleassignment-${Date.now()}`;
|
|
37
|
+
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
38
|
+
const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
39
|
+
const vid = `1`;
|
|
40
|
+
const resource = { resourceType: "RoleAssignment", id, ...parsedResource };
|
|
41
|
+
const summary = JSON.stringify(extractSummary2(resource));
|
|
42
|
+
await service.entities.roleAssignment.put({
|
|
43
|
+
tenantId: context.tenantId,
|
|
44
|
+
id,
|
|
45
|
+
resource: JSON.stringify(resource),
|
|
46
|
+
summary,
|
|
47
|
+
vid,
|
|
48
|
+
lastUpdated
|
|
49
|
+
}).go();
|
|
50
|
+
return {
|
|
51
|
+
id,
|
|
52
|
+
resource,
|
|
53
|
+
meta: { lastUpdated, versionId: vid }
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/data/operations/control/tenant/tenant-create-operation.ts
|
|
58
|
+
import { extractSummary as extractSummary3 } from "@openhi/types";
|
|
59
|
+
async function createTenantOperation(params) {
|
|
60
|
+
const { context, body, tableName } = params;
|
|
61
|
+
const service = getDynamoControlService(tableName);
|
|
62
|
+
const id = body.id ?? `tenant-${Date.now()}`;
|
|
63
|
+
const lastUpdated = context.date;
|
|
64
|
+
const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
|
|
65
|
+
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
66
|
+
const resource = { resourceType: "Tenant", id, ...parsedResource };
|
|
67
|
+
const summary = JSON.stringify(extractSummary3(resource));
|
|
68
|
+
await service.entities.tenant.put({
|
|
69
|
+
tenantId: id,
|
|
70
|
+
id,
|
|
71
|
+
resource: JSON.stringify(resource),
|
|
72
|
+
summary,
|
|
73
|
+
vid,
|
|
74
|
+
lastUpdated
|
|
75
|
+
}).go();
|
|
76
|
+
return { id, resource, meta: { lastUpdated, versionId: vid } };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/data/operations/control/workspace/workspace-create-operation.ts
|
|
80
|
+
import { extractSummary as extractSummary4 } from "@openhi/types";
|
|
81
|
+
async function createWorkspaceOperation(params) {
|
|
82
|
+
const { context, body, tableName } = params;
|
|
83
|
+
const { tenantId } = context;
|
|
84
|
+
const service = getDynamoControlService(tableName);
|
|
85
|
+
const id = body.id ?? `workspace-${Date.now()}`;
|
|
86
|
+
const lastUpdated = context.date;
|
|
87
|
+
const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
|
|
88
|
+
const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
|
|
89
|
+
const resource = { resourceType: "Workspace", id, ...parsedResource };
|
|
90
|
+
const summary = JSON.stringify(extractSummary4(resource));
|
|
91
|
+
await service.entities.workspace.put({
|
|
92
|
+
tenantId,
|
|
93
|
+
id,
|
|
94
|
+
resource: JSON.stringify(resource),
|
|
95
|
+
summary,
|
|
96
|
+
vid,
|
|
97
|
+
lastUpdated
|
|
98
|
+
}).go();
|
|
99
|
+
return { id, resource, meta: { lastUpdated, versionId: vid } };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
createMembershipOperation,
|
|
104
|
+
createRoleAssignmentOperation,
|
|
105
|
+
createTenantOperation,
|
|
106
|
+
createWorkspaceOperation
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=chunk-AO3E22CS.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data/operations/control/membership/membership-create-operation.ts","../src/data/operations/control/roleassignment/roleassignment-create-operation.ts","../src/data/operations/control/tenant/tenant-create-operation.ts","../src/data/operations/control/workspace/workspace-create-operation.ts"],"sourcesContent":["import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface MembershipCreateParams {\n context: OpenHiContext;\n body: { id?: string; resource?: Record<string, unknown> | string };\n tableName?: string;\n}\n\nexport interface MembershipCreateResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n meta: { lastUpdated: string; versionId: string };\n}\n\nexport async function createMembershipOperation(\n params: MembershipCreateParams,\n): Promise<MembershipCreateResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `membership-${Date.now()}`;\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const lastUpdated = context.date ?? new Date().toISOString();\n const vid = `1`;\n\n const resource = { resourceType: \"Membership\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.membership\n .put({\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface RoleAssignmentCreateParams {\n context: OpenHiContext;\n body: { id?: string; resource?: Record<string, unknown> | string };\n tableName?: string;\n}\n\nexport interface RoleAssignmentCreateResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n meta: { lastUpdated: string; versionId: string };\n}\n\nexport async function createRoleAssignmentOperation(\n params: RoleAssignmentCreateParams,\n): Promise<RoleAssignmentCreateResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `roleassignment-${Date.now()}`;\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const lastUpdated = context.date ?? new Date().toISOString();\n const vid = `1`;\n\n const resource = { resourceType: \"RoleAssignment\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.roleAssignment\n .put({\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface CreateTenantParams {\n context: OpenHiContext;\n body: {\n id?: string;\n resource?: Record<string, unknown> | string;\n };\n tableName?: string;\n}\n\nexport interface CreateTenantResult {\n id: string;\n resource: Record<string, unknown>;\n meta: { lastUpdated: string; versionId: string };\n}\n\n/**\n * Creates a Tenant. Generates an id if not provided.\n */\nexport async function createTenantOperation(\n params: CreateTenantParams,\n): Promise<CreateTenantResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `tenant-${Date.now()}`;\n const lastUpdated = context.date;\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const resource = { resourceType: \"Tenant\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.tenant\n .put({\n tenantId: id,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return { id, resource, meta: { lastUpdated, versionId: vid } };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface CreateWorkspaceParams {\n context: OpenHiContext;\n body: {\n id?: string;\n resource?: Record<string, unknown> | string;\n };\n tableName?: string;\n}\n\nexport interface CreateWorkspaceResult {\n id: string;\n resource: Record<string, unknown>;\n meta: { lastUpdated: string; versionId: string };\n}\n\n/**\n * Creates a Workspace scoped to the context tenant. Generates an id if not provided.\n */\nexport async function createWorkspaceOperation(\n params: CreateWorkspaceParams,\n): Promise<CreateWorkspaceResult> {\n const { context, body, tableName } = params;\n const { tenantId } = context;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `workspace-${Date.now()}`;\n const lastUpdated = context.date;\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const resource = { resourceType: \"Workspace\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.workspace\n .put({\n tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return { id, resource, meta: { lastUpdated, versionId: vid } };\n}\n"],"mappings":"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","names":["extractSummary","extractSummary","extractSummary","extractSummary","extractSummary","extractSummary"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDynamoControlService
|
|
3
|
+
} from "./chunk-VYDIGFIX.mjs";
|
|
4
|
+
|
|
5
|
+
// src/data/operations/control/user/user-find-by-sub-operation.ts
|
|
6
|
+
async function findUserBySubOperation(params) {
|
|
7
|
+
const { cognitoSub, tableName } = params;
|
|
8
|
+
const service = getDynamoControlService(tableName);
|
|
9
|
+
const result = await service.entities.user.query.gsi2({ cognitoSub }).go({ limit: 1 });
|
|
10
|
+
const item = result.data?.[0];
|
|
11
|
+
if (!item) {
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
id: item.id,
|
|
16
|
+
cognitoSub: item.cognitoSub,
|
|
17
|
+
resource: item.resource,
|
|
18
|
+
vid: item.vid
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/data/operations/control/user/user-resource-helpers.ts
|
|
23
|
+
function parseUserResource(resource) {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(resource);
|
|
26
|
+
} catch {
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/data/operations/fhir-reference.ts
|
|
32
|
+
function idFromReference(reference, prefix) {
|
|
33
|
+
if (!reference || !reference.startsWith(prefix)) {
|
|
34
|
+
return void 0;
|
|
35
|
+
}
|
|
36
|
+
const id = reference.slice(prefix.length);
|
|
37
|
+
return id.length > 0 ? id : void 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
findUserBySubOperation,
|
|
42
|
+
parseUserResource,
|
|
43
|
+
idFromReference
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=chunk-CHPEQRXU.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data/operations/control/user/user-find-by-sub-operation.ts","../src/data/operations/control/user/user-resource-helpers.ts","../src/data/operations/fhir-reference.ts"],"sourcesContent":["import { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface FindUserBySubParams {\n context: OpenHiContext;\n cognitoSub: string;\n tableName?: string;\n}\n\nexport interface FindUserBySubResult {\n id: string;\n cognitoSub?: string;\n resource: string;\n vid: string;\n}\n\n/**\n * Look up a User by Cognito sub via GSI2, projecting the row to a stable\n * result shape. Returns `undefined` when no row matches.\n */\nexport async function findUserBySubOperation(\n params: FindUserBySubParams,\n): Promise<FindUserBySubResult | undefined> {\n const { cognitoSub, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const result = await service.entities.user.query\n .gsi2({ cognitoSub })\n .go({ limit: 1 });\n const item = result.data?.[0];\n if (!item) {\n return undefined;\n }\n return {\n id: item.id,\n cognitoSub: item.cognitoSub,\n resource: item.resource,\n vid: item.vid,\n };\n}\n","import type { User } from \"@openhi/types\";\n\n/**\n * Helpers for working with persisted OpenHI User resources. Co-located with\n * the User operations because both the Cognito triggers and the onboarding\n * workflow consume these alongside `findUserBySubOperation`.\n */\n\n// Defensive parse — JSON.parse may yield any shape, so every field is optional.\nexport type UserResource = Partial<User>;\n\n/**\n * Existing User resources are stored as JSON strings in the data store; parse\n * defensively so a malformed payload returns `undefined` rather than throwing.\n */\nexport function parseUserResource(resource: string): UserResource | undefined {\n try {\n return JSON.parse(resource) as UserResource;\n } catch {\n return undefined;\n }\n}\n","/**\n * Pure helpers for working with FHIR Reference fields. Shared across data-plane\n * and control-plane operations and the handlers that wrap them.\n */\n\n/**\n * Extract the id portion from a FHIR-style reference such as `Patient/<id>` or\n * `Tenant/<id>`. Returns `undefined` if the reference is missing, does not\n * match the prefix, or has an empty id after the prefix.\n */\nexport function idFromReference(\n reference: string | undefined,\n prefix: string,\n): string | undefined {\n if (!reference || !reference.startsWith(prefix)) {\n return undefined;\n }\n const id = reference.slice(prefix.length);\n return id.length > 0 ? id : undefined;\n}\n"],"mappings":";;;;;AAoBA,eAAsB,uBACpB,QAC0C;AAC1C,QAAM,EAAE,YAAY,UAAU,IAAI;AAClC,QAAM,UAAU,wBAAwB,SAAS;AAEjD,QAAM,SAAS,MAAM,QAAQ,SAAS,KAAK,MACxC,KAAK,EAAE,WAAW,CAAC,EACnB,GAAG,EAAE,OAAO,EAAE,CAAC;AAClB,QAAM,OAAO,OAAO,OAAO,CAAC;AAC5B,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,YAAY,KAAK;AAAA,IACjB,UAAU,KAAK;AAAA,IACf,KAAK,KAAK;AAAA,EACZ;AACF;;;ACxBO,SAAS,kBAAkB,UAA4C;AAC5E,MAAI;AACF,WAAO,KAAK,MAAM,QAAQ;AAAA,EAC5B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACXO,SAAS,gBACd,WACA,QACoB;AACpB,MAAI,CAAC,aAAa,CAAC,UAAU,WAAW,MAAM,GAAG;AAC/C,WAAO;AAAA,EACT;AACA,QAAM,KAAK,UAAU,MAAM,OAAO,MAAM;AACxC,SAAO,GAAG,SAAS,IAAI,KAAK;AAC9B;","names":[]}
|