@openhi/constructs 0.0.118 → 0.0.120

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.
@@ -3,7 +3,7 @@ import {
3
3
  createRoleAssignmentOperation,
4
4
  createTenantOperation,
5
5
  createWorkspaceOperation
6
- } from "./chunk-QWWLM452.mjs";
6
+ } from "./chunk-7WDX6GPO.mjs";
7
7
  import "./chunk-HQ67J7BP.mjs";
8
8
  import "./chunk-QJDHVMKT.mjs";
9
9
  import {
@@ -4946,6 +4946,19 @@ function extractDenormalizedReferenceDisplay(resource, fieldName) {
4946
4946
  return trimmed.length > 0 ? trimmed : void 0;
4947
4947
  }
4948
4948
 
4949
+ // src/data/operations/control/membership-constraints/assert-workspace-in-tenant-operation.ts
4950
+ async function assertWorkspaceInTenantOperation(params) {
4951
+ const { tenantId, workspaceId, tableName } = params;
4952
+ const service = getDynamoControlService(tableName);
4953
+ const { data: item } = await service.entities.workspace.get({ tenantId, id: workspaceId, sk: "CURRENT" }).go();
4954
+ if (!item) {
4955
+ throw new ConflictError(
4956
+ `Workspace ${workspaceId} does not belong to tenant ${tenantId}; the workspace must be created in the referenced tenant before this resource can reference it.`,
4957
+ { details: { tenantId, workspaceId } }
4958
+ );
4959
+ }
4960
+ }
4961
+
4949
4962
  // src/data/operations/control/membership/membership-create-operation.ts
4950
4963
  async function createMembershipOperation(params) {
4951
4964
  const { context, body, tableName } = params;
@@ -4986,6 +4999,15 @@ async function createMembershipOperation(params) {
4986
4999
  resourceRecord,
4987
5000
  "workspace"
4988
5001
  );
5002
+ if (workspaceIdFromResource !== void 0) {
5003
+ const tenantIdFromResource = extractReferenceSlug(resourceRecord, "tenant");
5004
+ const referencedTenantId = tenantIdFromResource ?? context.tenantId;
5005
+ await assertWorkspaceInTenantOperation({
5006
+ tenantId: referencedTenantId,
5007
+ workspaceId: workspaceIdFromResource,
5008
+ tableName
5009
+ });
5010
+ }
4989
5011
  const userProjectionItem = userIdFromResource !== void 0 ? buildMembershipUserProjectionItem({
4990
5012
  tenantId: context.tenantId,
4991
5013
  userId: userIdFromResource,
@@ -5061,6 +5083,22 @@ async function createMembershipRoute(req, res) {
5061
5083
  });
5062
5084
  return res.status(201).location(`${BASE_PATH.MEMBERSHIP}/${result.id}`).json({ ...result.resource, meta: result.meta });
5063
5085
  } catch (err) {
5086
+ if (err instanceof ConflictError) {
5087
+ return res.status(409).json({
5088
+ resourceType: "OperationOutcome",
5089
+ issue: [
5090
+ { severity: "error", code: "conflict", diagnostics: err.message }
5091
+ ]
5092
+ });
5093
+ }
5094
+ if (err instanceof ValidationError) {
5095
+ return res.status(400).json({
5096
+ resourceType: "OperationOutcome",
5097
+ issue: [
5098
+ { severity: "error", code: "invalid", diagnostics: err.message }
5099
+ ]
5100
+ });
5101
+ }
5064
5102
  console.error("POST Membership error:", err);
5065
5103
  return res.status(500).json({
5066
5104
  resourceType: "OperationOutcome",
@@ -5743,6 +5781,21 @@ function buildRoleAssignmentWorkspaceProjectionItem(input) {
5743
5781
  };
5744
5782
  }
5745
5783
 
5784
+ // src/data/operations/control/membership-constraints/assert-user-has-tenant-membership-operation.ts
5785
+ var TENANT_LANE_SK_PREFIX = "MEMBERSHIP#TENANT#";
5786
+ async function assertUserHasTenantMembershipOperation(params) {
5787
+ const { userId, tenantId, tableName } = params;
5788
+ const service = getDynamoControlService(tableName);
5789
+ const result = await service.entities.membershipUserProjection.query.record({ userId }).begins({ sk: TENANT_LANE_SK_PREFIX }).go();
5790
+ const matched = (result.data ?? []).some((row) => row.tenantId === tenantId);
5791
+ if (!matched) {
5792
+ throw new ConflictError(
5793
+ `User ${userId} has no tenant-level Membership in tenant ${tenantId}; a Membership must exist before a RoleAssignment can be created.`,
5794
+ { details: { userId, tenantId } }
5795
+ );
5796
+ }
5797
+ }
5798
+
5746
5799
  // src/data/operations/control/roleassignment/roleassignment-create-operation.ts
5747
5800
  async function createRoleAssignmentOperation(params) {
5748
5801
  const { context, body, tableName } = params;
@@ -5772,6 +5825,22 @@ async function createRoleAssignmentOperation(params) {
5772
5825
  resourceRecord,
5773
5826
  "workspace"
5774
5827
  );
5828
+ if (userIdFromResource !== void 0) {
5829
+ const tenantIdFromResource = extractReferenceSlug2(resourceRecord, "tenant");
5830
+ const referencedTenantId = tenantIdFromResource ?? context.tenantId;
5831
+ await assertUserHasTenantMembershipOperation({
5832
+ userId: userIdFromResource,
5833
+ tenantId: referencedTenantId,
5834
+ tableName
5835
+ });
5836
+ if (workspaceIdFromResource !== void 0) {
5837
+ await assertWorkspaceInTenantOperation({
5838
+ tenantId: referencedTenantId,
5839
+ workspaceId: workspaceIdFromResource,
5840
+ tableName
5841
+ });
5842
+ }
5843
+ }
5775
5844
  const userProjectionItem = userIdFromResource !== void 0 && roleIdFromResource !== void 0 ? buildRoleAssignmentUserProjectionItem({
5776
5845
  tenantId: context.tenantId,
5777
5846
  userId: userIdFromResource,
@@ -5850,6 +5919,22 @@ async function createRoleAssignmentRoute(req, res) {
5850
5919
  });
5851
5920
  return res.status(201).location(`${BASE_PATH.ROLEASSIGNMENT}/${result.id}`).json({ ...result.resource, meta: result.meta });
5852
5921
  } catch (err) {
5922
+ if (err instanceof ConflictError) {
5923
+ return res.status(409).json({
5924
+ resourceType: "OperationOutcome",
5925
+ issue: [
5926
+ { severity: "error", code: "conflict", diagnostics: err.message }
5927
+ ]
5928
+ });
5929
+ }
5930
+ if (err instanceof ValidationError) {
5931
+ return res.status(400).json({
5932
+ resourceType: "OperationOutcome",
5933
+ issue: [
5934
+ { severity: "error", code: "invalid", diagnostics: err.message }
5935
+ ]
5936
+ });
5937
+ }
5853
5938
  console.error("POST RoleAssignment error:", err);
5854
5939
  return res.status(500).json({
5855
5940
  resourceType: "OperationOutcome",