quidproquo-actionprocessor-awslambda 0.1.19 → 0.1.20
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/lib/commonjs/awsNamingUtils.d.ts +2 -0
- package/lib/commonjs/awsNamingUtils.js +4 -1
- package/lib/commonjs/awsNamingUtils.js.map +1 -1
- package/lib/commonjs/getActionProcessor/core/event/apiGatwayEvent/api/getEventTransformResponseResultActionProcessor.js +4 -3
- package/lib/commonjs/getActionProcessor/core/event/apiGatwayEvent/api/getEventTransformResponseResultActionProcessor.js.map +1 -1
- package/lib/commonjs/getActionProcessor/core/event/cloudFrontOriginRequest/getEventGetRecordsActionProcessor.js +7 -3
- package/lib/commonjs/getActionProcessor/core/event/cloudFrontOriginRequest/getEventGetRecordsActionProcessor.js.map +1 -1
- package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js +1 -0
- package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js.map +1 -1
- package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js +15 -5
- package/lib/commonjs/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js.map +1 -1
- package/lib/commonjs/lambdaHandlers/getApiGatewayEventHandler_redirect.js +7 -19
- package/lib/commonjs/lambdaHandlers/getApiGatewayEventHandler_redirect.js.map +1 -1
- package/lib/commonjs/logic/dynamo/index.d.ts +1 -0
- package/lib/commonjs/logic/dynamo/index.js +1 -0
- package/lib/commonjs/logic/dynamo/index.js.map +1 -1
- package/lib/commonjs/logic/dynamo/transactPutItems.d.ts +10 -0
- package/lib/commonjs/logic/dynamo/transactPutItems.js +90 -0
- package/lib/commonjs/logic/dynamo/transactPutItems.js.map +1 -0
- package/lib/esm/awsNamingUtils.d.ts +2 -0
- package/lib/esm/awsNamingUtils.js +2 -0
- package/lib/esm/awsNamingUtils.js.map +1 -1
- package/lib/esm/getActionProcessor/core/event/apiGatwayEvent/api/getEventTransformResponseResultActionProcessor.js +3 -2
- package/lib/esm/getActionProcessor/core/event/apiGatwayEvent/api/getEventTransformResponseResultActionProcessor.js.map +1 -1
- package/lib/esm/getActionProcessor/core/event/cloudFrontOriginRequest/getEventGetRecordsActionProcessor.js +6 -3
- package/lib/esm/getActionProcessor/core/event/cloudFrontOriginRequest/getEventGetRecordsActionProcessor.js.map +1 -1
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js +1 -0
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js.map +1 -1
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js +16 -6
- package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js.map +1 -1
- package/lib/esm/lambdaHandlers/getApiGatewayEventHandler_redirect.js +7 -19
- package/lib/esm/lambdaHandlers/getApiGatewayEventHandler_redirect.js.map +1 -1
- package/lib/esm/logic/dynamo/index.d.ts +1 -0
- package/lib/esm/logic/dynamo/index.js +1 -0
- package/lib/esm/logic/dynamo/index.js.map +1 -1
- package/lib/esm/logic/dynamo/transactPutItems.d.ts +10 -0
- package/lib/esm/logic/dynamo/transactPutItems.js +72 -0
- package/lib/esm/logic/dynamo/transactPutItems.js.map +1 -0
- package/lib/extension-layer/qpq-log-extension/index.js +33 -33
- package/package.json +7 -7
|
@@ -51,6 +51,8 @@ export const getConfigRuntimeBootstrapResourceNameFromConfig = (resourceName, qp
|
|
|
51
51
|
const feature = qpqCoreUtils.getApplicationModuleFeature(qpqConfig);
|
|
52
52
|
return getConfigRuntimeBootstrapResourceName(resourceName, application, environment, feature);
|
|
53
53
|
};
|
|
54
|
+
/** The GitHub Actions deploy role for an app environment: `github-actions-deploy-<app>-<env>[-<feature>]`. */
|
|
55
|
+
export const getGithubDeployRoleNameFromConfig = (qpqConfig) => getConfigRuntimeBootstrapResourceNameFromConfig('github-actions-deploy', qpqConfig);
|
|
54
56
|
export const getConfigRuntimeResourceNameFromConfigWithServiceOverride = (resourceName, qpqConfig, serviceOverride) => resolveConfigRuntimeResourceNameFromConfig(resourceName, qpqConfig, { module: serviceOverride });
|
|
55
57
|
export const getQpqRuntimeResourceName = (resourceName, application, service, environment, feature, resourceType = '') => {
|
|
56
58
|
const name = getConfigRuntimeResourceName(resourceName, application, service, environment, feature);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"awsNamingUtils.js","sourceRoot":"","sources":["../../src/awsNamingUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAwsServiceAccountInfoByDeploymentInfo, qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport { CrossModuleOwner, QPQConfig, qpqCoreUtils, ResourceName } from 'quidproquo-core';\nimport { qpqWebServerUtils } from 'quidproquo-webserver';\n\ntype AwsDeploymentContext = {\n application: string;\n service: string;\n environment: string;\n feature?: string;\n};\n\n// A resource declared with a CrossModuleOwner belongs to another service. Every owner field\n// falls back to the executing service's own deployment context, so all consumers resolve the\n// exact name the owning service deployed under.\nconst resolveOwnerDeploymentContext = (qpqConfig: QPQConfig, owner?: CrossModuleOwner): AwsDeploymentContext => ({\n application: owner?.application || qpqCoreUtils.getApplicationName(qpqConfig),\n service: owner?.module || qpqCoreUtils.getApplicationModuleName(qpqConfig),\n environment: owner?.environment || qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig),\n feature: owner?.feature || qpqCoreUtils.getApplicationModuleFeature(qpqConfig),\n});\n\nexport const getGlobalConfigRuntimeResourceName = (resourceName: string, application: string, environment: string, feature?: string) => {\n const baseName = `${resourceName}-${application}-${environment}`;\n\n if (feature) {\n return `${baseName}-${feature}`;\n }\n\n return baseName;\n};\n\nexport const getGlobalQpqRuntimeResourceName = (\n resourceName: string,\n application: string,\n environment: string,\n feature?: string,\n resourceType: string = '',\n) => {\n const name = getGlobalConfigRuntimeResourceName(resourceName, application, environment, feature);\n return `${name}-qpq${resourceType}`;\n};\n\nexport const getConfigRuntimeResourceName = (resourceName: string, application: string, service: string, environment: string, feature?: string) => {\n const baseName = `${resourceName}-${application}-${service}-${environment}`;\n\n if (feature) {\n return `${baseName}-${feature}`;\n }\n\n return baseName;\n};\n\n// Bootstrap resources are shared per app+environment, so like global names they carry no\n// service segment.\nexport const getConfigRuntimeBootstrapResourceName = (resourceName: string, application: string, environment: string, feature?: string) =>\n getGlobalConfigRuntimeResourceName(resourceName, application, environment, feature);\n\nexport const resolveConfigRuntimeResourceNameFromConfig = (resourceName: string, qpqConfig: QPQConfig, owner?: CrossModuleOwner) => {\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, owner);\n\n return getConfigRuntimeResourceName(resourceName, application, service, environment, feature);\n};\n\nexport const getConfigRuntimeResourceNameFromConfig = (resourceName: string, qpqConfig: QPQConfig) =>\n resolveConfigRuntimeResourceNameFromConfig(resourceName, qpqConfig);\n\n// AWS FIFO resources (SQS queues, SNS topics) must be named with a trailing .fifo - it has\n// to be the final characters, so it goes after the app-service-env decoration. Every FIFO\n// resource name (runtime send, CDK resource names, cross-stack ARNs) resolves through here\n// so they all stay in sync.\nexport const withFifoSuffix = (resourceName: string, isFifo?: boolean) => (isFifo ? `${resourceName}.fifo` : resourceName);\n\nexport const getQueueRuntimeResourceNameFromConfig = (queueName: string, qpqConfig: QPQConfig) => {\n const baseName = getConfigRuntimeResourceNameFromConfig(queueName, qpqConfig);\n\n return withFifoSuffix(baseName, qpqCoreUtils.getQueueByName(qpqConfig, queueName)?.isFifo);\n};\n\nexport const getConfigRuntimeBootstrapResourceNameFromConfig = (resourceName: string, qpqConfig: QPQConfig) => {\n const application = qpqCoreUtils.getApplicationName(qpqConfig);\n const environment = qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);\n const feature = qpqCoreUtils.getApplicationModuleFeature(qpqConfig);\n\n return getConfigRuntimeBootstrapResourceName(resourceName, application, environment, feature);\n};\n\nexport const getConfigRuntimeResourceNameFromConfigWithServiceOverride = (resourceName: string, qpqConfig: QPQConfig, serviceOverride?: string) =>\n resolveConfigRuntimeResourceNameFromConfig(resourceName, qpqConfig, { module: serviceOverride });\n\nexport const getQpqRuntimeResourceName = (\n resourceName: string,\n application: string,\n service: string,\n environment: string,\n feature?: string,\n resourceType: string = '',\n) => {\n const name = getConfigRuntimeResourceName(resourceName, application, service, environment, feature);\n return `${name}-qpq${resourceType}`;\n};\n\nexport const getQpqRuntimeResourceNameFromConfig = (resourceName: ResourceName, qpqConfig: QPQConfig, resourceType: string = '') => {\n const crossServiceResourceName = qpqCoreUtils.resolveCrossServiceResourceName(resourceName);\n\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, { module: crossServiceResourceName.service });\n\n return getQpqRuntimeResourceName(crossServiceResourceName.name, application, service, environment, feature, resourceType);\n};\n\n// Note: only the owner's module is honoured here; an owner application/environment/feature on\n// a kvs config does not change the table name. Changing that now would orphan deployed tables.\nexport const getKvsDynamoTableNameFromConfig = (resourceName: string, qpqConfig: QPQConfig, resourceType: string = '') => {\n const tableNameOverride = qpqConfigAwsUtils.getDynamoTableNameOverrride(resourceName, qpqConfig);\n if (tableNameOverride) {\n return tableNameOverride;\n }\n\n const storeConfig = qpqCoreUtils.getKeyValueStoreByName(qpqConfig, resourceName);\n\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, { module: storeConfig?.owner?.module });\n\n return getQpqRuntimeResourceName(resourceName, application, service, environment, feature, resourceType);\n};\n\nconst getUserPoolCFExportName = (userDirectoryName: string, qpqConfig: QPQConfig, resourceType: string) => {\n const userDirectoryConfig = qpqCoreUtils.getUserDirectoryByName(userDirectoryName, qpqConfig);\n\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, userDirectoryConfig.owner);\n const resourceName = userDirectoryConfig.owner?.resourceNameOverride || userDirectoryName;\n\n return getQpqRuntimeResourceName(resourceName, application, service, environment, feature, resourceType);\n};\n\nexport const getCFExportNameUserPoolIdFromConfig = (userDirectoryName: string, qpqConfig: QPQConfig) =>\n getUserPoolCFExportName(userDirectoryName, qpqConfig, 'user-pool-id-export');\n\nexport const getCFExportNameUserPoolClientIdFromConfig = (userDirectoryName: string, qpqConfig: QPQConfig) =>\n getUserPoolCFExportName(userDirectoryName, qpqConfig, 'user-pool-client-id-export');\n\nconst getOverridableQpqResourceExportName = (\n resourceName: string,\n qpqConfig: QPQConfig,\n resourceType: string,\n serviceOverride?: string,\n applicationOverride?: string,\n) => {\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, {\n application: applicationOverride,\n module: serviceOverride,\n });\n\n return getQpqRuntimeResourceName(resourceName, application, service, environment, feature, resourceType);\n};\n\nexport const getCFExportNameCachePolicyIdFromConfig = (\n cacheConfigName: string,\n qpqConfig: QPQConfig,\n\n serviceOverride?: string,\n applicationOverride?: string,\n) => getOverridableQpqResourceExportName(cacheConfigName, qpqConfig, 'cache-policy-name-export', serviceOverride, applicationOverride);\n\nexport const getCFExportNameApiKeyIdFromConfig = (\n apiKeyName: string,\n qpqConfig: QPQConfig,\n\n serviceOverride?: string,\n applicationOverride?: string,\n) => getOverridableQpqResourceExportName(apiKeyName, qpqConfig, 'api-key-id-export', serviceOverride, applicationOverride);\n\nexport const getCFExportNameSnsTopicArnFromConfig = (\n eventBusName: string,\n qpqConfig: QPQConfig,\n\n applicationOverride?: string,\n) => {\n const application = applicationOverride || qpqCoreUtils.getApplicationName(qpqConfig);\n const environment = qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);\n const feature = qpqCoreUtils.getApplicationModuleFeature(qpqConfig);\n\n return getGlobalQpqRuntimeResourceName(eventBusName, application, environment, feature, 'sns-topic-arn-export');\n};\n\nexport const getCFExportNameDistributionIdArnFromConfig = (\n webEntryName: string,\n qpqConfig: QPQConfig,\n\n serviceOverride?: string,\n applicationOverride?: string,\n) => getOverridableQpqResourceExportName(webEntryName, qpqConfig, 'distribution-id-export', serviceOverride, applicationOverride);\n\nconst resolveWebsocketApiNaming = (websocketApiName: string, qpqConfig: QPQConfig) => {\n const websocketApiConfig = qpqWebServerUtils.getWebsocketEntryByApiName(websocketApiName, qpqConfig);\n\n return {\n context: resolveOwnerDeploymentContext(qpqConfig, websocketApiConfig.owner),\n resourceName: websocketApiConfig.owner?.resourceNameOverride || websocketApiName,\n };\n};\n\nexport const getCFExportNameWebsocketApiIdFromConfig = (websocketApiName: string, qpqConfig: QPQConfig) => {\n const { context, resourceName } = resolveWebsocketApiNaming(websocketApiName, qpqConfig);\n\n return getQpqRuntimeResourceName(\n resourceName,\n context.application,\n context.service,\n context.environment,\n context.feature,\n 'websocket-api-id-export',\n );\n};\n\n// SSM parameter holding a websocket api's AWS-generated id, written by the owning service's\n// inf stack. Referencing services resolve it at deploy time to build exact execute-api\n// grants (deploy the owning inf stack first - same owner-publishes/others-read pattern as\n// the domain certificate arn parameter).\nexport const getWebsocketApiIdSsmParameterName = (websocketApiName: string, qpqConfig: QPQConfig) => {\n const { context, resourceName } = resolveWebsocketApiNaming(websocketApiName, qpqConfig);\n\n return `/qpq/websocket/api-id/${getConfigRuntimeResourceName(resourceName, context.application, context.service, context.environment, context.feature)}`;\n};\n\nexport const getEventBusSnsTopicArn = (\n eventBusName: string,\n qpqConfig: QPQConfig,\n\n module: string,\n environment: string,\n application: string,\n feature?: string,\n\n isFifo?: boolean,\n) => {\n const topicName = withFifoSuffix(getConfigRuntimeResourceName(eventBusName, application, module, environment, feature), isFifo);\n\n const accountInfo = getAwsServiceAccountInfoByDeploymentInfo(qpqConfig, module, environment, feature, application);\n\n const awsAccountId = accountInfo.awsAccountId;\n const region = accountInfo.awsRegion;\n\n return `arn:aws:sns:${region}:${awsAccountId}:${topicName}`;\n};\n\n// The account stack owns account-level resources (cloud trail, budgets, security services)\n// so they never share an app's lifecycle. The name is deliberately static: stack names are\n// already namespaced per account+region, and the resources it holds meter/audit the whole\n// account - an app/environment segment would only fake a separation that doesn't exist.\n// Everything in one account converges on this one stack, so exactly ONE repo/config must\n// own it, and actor (feature) deploys must not deploy it.\nexport const getAccountStackName = () => 'qpq-account';\n\nexport const getBaseStackName = (qpqConfig: QPQConfig) => {\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig);\n\n const baseName = `${application}-${service}-${environment}`;\n\n if (feature) {\n return `${baseName}-${feature}`;\n }\n\n return baseName;\n};\n\nexport const getInfStackName = (qpqConfig: QPQConfig) => {\n return `${getBaseStackName(qpqConfig)}-inf`;\n};\n\nexport const getWebStackName = (qpqConfig: QPQConfig) => {\n return `${getBaseStackName(qpqConfig)}-web`;\n};\n\nexport const getApiStackName = (qpqConfig: QPQConfig) => {\n return `${getBaseStackName(qpqConfig)}-api`;\n};\n\n// App-level stacks (bootstrap, domain) are shared by every module of the app, so their\n// names carry no module segment.\nconst getAppStackName = (qpqConfig: QPQConfig, suffix: string) => {\n const appName = qpqCoreUtils.getApplicationName(qpqConfig);\n const environment = qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);\n const feature = qpqCoreUtils.getApplicationModuleFeature(qpqConfig);\n\n const baseName = `${appName}-${environment}`;\n\n if (feature) {\n return `${baseName}-${feature}-${suffix}`;\n }\n\n return `${baseName}-${suffix}`;\n};\n\nexport const getBootstrapStackName = (qpqConfig: QPQConfig) => getAppStackName(qpqConfig, 'bs');\n\nexport const getDomainStackName = (qpqConfig: QPQConfig) => getAppStackName(qpqConfig, 'domain');\n"]}
|
|
1
|
+
{"version":3,"file":"awsNamingUtils.js","sourceRoot":"","sources":["../../src/awsNamingUtils.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAwsServiceAccountInfoByDeploymentInfo, qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport { CrossModuleOwner, QPQConfig, qpqCoreUtils, ResourceName } from 'quidproquo-core';\nimport { qpqWebServerUtils } from 'quidproquo-webserver';\n\ntype AwsDeploymentContext = {\n application: string;\n service: string;\n environment: string;\n feature?: string;\n};\n\n// A resource declared with a CrossModuleOwner belongs to another service. Every owner field\n// falls back to the executing service's own deployment context, so all consumers resolve the\n// exact name the owning service deployed under.\nconst resolveOwnerDeploymentContext = (qpqConfig: QPQConfig, owner?: CrossModuleOwner): AwsDeploymentContext => ({\n application: owner?.application || qpqCoreUtils.getApplicationName(qpqConfig),\n service: owner?.module || qpqCoreUtils.getApplicationModuleName(qpqConfig),\n environment: owner?.environment || qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig),\n feature: owner?.feature || qpqCoreUtils.getApplicationModuleFeature(qpqConfig),\n});\n\nexport const getGlobalConfigRuntimeResourceName = (resourceName: string, application: string, environment: string, feature?: string) => {\n const baseName = `${resourceName}-${application}-${environment}`;\n\n if (feature) {\n return `${baseName}-${feature}`;\n }\n\n return baseName;\n};\n\nexport const getGlobalQpqRuntimeResourceName = (\n resourceName: string,\n application: string,\n environment: string,\n feature?: string,\n resourceType: string = '',\n) => {\n const name = getGlobalConfigRuntimeResourceName(resourceName, application, environment, feature);\n return `${name}-qpq${resourceType}`;\n};\n\nexport const getConfigRuntimeResourceName = (resourceName: string, application: string, service: string, environment: string, feature?: string) => {\n const baseName = `${resourceName}-${application}-${service}-${environment}`;\n\n if (feature) {\n return `${baseName}-${feature}`;\n }\n\n return baseName;\n};\n\n// Bootstrap resources are shared per app+environment, so like global names they carry no\n// service segment.\nexport const getConfigRuntimeBootstrapResourceName = (resourceName: string, application: string, environment: string, feature?: string) =>\n getGlobalConfigRuntimeResourceName(resourceName, application, environment, feature);\n\nexport const resolveConfigRuntimeResourceNameFromConfig = (resourceName: string, qpqConfig: QPQConfig, owner?: CrossModuleOwner) => {\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, owner);\n\n return getConfigRuntimeResourceName(resourceName, application, service, environment, feature);\n};\n\nexport const getConfigRuntimeResourceNameFromConfig = (resourceName: string, qpqConfig: QPQConfig) =>\n resolveConfigRuntimeResourceNameFromConfig(resourceName, qpqConfig);\n\n// AWS FIFO resources (SQS queues, SNS topics) must be named with a trailing .fifo - it has\n// to be the final characters, so it goes after the app-service-env decoration. Every FIFO\n// resource name (runtime send, CDK resource names, cross-stack ARNs) resolves through here\n// so they all stay in sync.\nexport const withFifoSuffix = (resourceName: string, isFifo?: boolean) => (isFifo ? `${resourceName}.fifo` : resourceName);\n\nexport const getQueueRuntimeResourceNameFromConfig = (queueName: string, qpqConfig: QPQConfig) => {\n const baseName = getConfigRuntimeResourceNameFromConfig(queueName, qpqConfig);\n\n return withFifoSuffix(baseName, qpqCoreUtils.getQueueByName(qpqConfig, queueName)?.isFifo);\n};\n\nexport const getConfigRuntimeBootstrapResourceNameFromConfig = (resourceName: string, qpqConfig: QPQConfig) => {\n const application = qpqCoreUtils.getApplicationName(qpqConfig);\n const environment = qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);\n const feature = qpqCoreUtils.getApplicationModuleFeature(qpqConfig);\n\n return getConfigRuntimeBootstrapResourceName(resourceName, application, environment, feature);\n};\n\n/** The GitHub Actions deploy role for an app environment: `github-actions-deploy-<app>-<env>[-<feature>]`. */\nexport const getGithubDeployRoleNameFromConfig = (qpqConfig: QPQConfig): string =>\n getConfigRuntimeBootstrapResourceNameFromConfig('github-actions-deploy', qpqConfig);\n\nexport const getConfigRuntimeResourceNameFromConfigWithServiceOverride = (resourceName: string, qpqConfig: QPQConfig, serviceOverride?: string) =>\n resolveConfigRuntimeResourceNameFromConfig(resourceName, qpqConfig, { module: serviceOverride });\n\nexport const getQpqRuntimeResourceName = (\n resourceName: string,\n application: string,\n service: string,\n environment: string,\n feature?: string,\n resourceType: string = '',\n) => {\n const name = getConfigRuntimeResourceName(resourceName, application, service, environment, feature);\n return `${name}-qpq${resourceType}`;\n};\n\nexport const getQpqRuntimeResourceNameFromConfig = (resourceName: ResourceName, qpqConfig: QPQConfig, resourceType: string = '') => {\n const crossServiceResourceName = qpqCoreUtils.resolveCrossServiceResourceName(resourceName);\n\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, { module: crossServiceResourceName.service });\n\n return getQpqRuntimeResourceName(crossServiceResourceName.name, application, service, environment, feature, resourceType);\n};\n\n// Note: only the owner's module is honoured here; an owner application/environment/feature on\n// a kvs config does not change the table name. Changing that now would orphan deployed tables.\nexport const getKvsDynamoTableNameFromConfig = (resourceName: string, qpqConfig: QPQConfig, resourceType: string = '') => {\n const tableNameOverride = qpqConfigAwsUtils.getDynamoTableNameOverrride(resourceName, qpqConfig);\n if (tableNameOverride) {\n return tableNameOverride;\n }\n\n const storeConfig = qpqCoreUtils.getKeyValueStoreByName(qpqConfig, resourceName);\n\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, { module: storeConfig?.owner?.module });\n\n return getQpqRuntimeResourceName(resourceName, application, service, environment, feature, resourceType);\n};\n\nconst getUserPoolCFExportName = (userDirectoryName: string, qpqConfig: QPQConfig, resourceType: string) => {\n const userDirectoryConfig = qpqCoreUtils.getUserDirectoryByName(userDirectoryName, qpqConfig);\n\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, userDirectoryConfig.owner);\n const resourceName = userDirectoryConfig.owner?.resourceNameOverride || userDirectoryName;\n\n return getQpqRuntimeResourceName(resourceName, application, service, environment, feature, resourceType);\n};\n\nexport const getCFExportNameUserPoolIdFromConfig = (userDirectoryName: string, qpqConfig: QPQConfig) =>\n getUserPoolCFExportName(userDirectoryName, qpqConfig, 'user-pool-id-export');\n\nexport const getCFExportNameUserPoolClientIdFromConfig = (userDirectoryName: string, qpqConfig: QPQConfig) =>\n getUserPoolCFExportName(userDirectoryName, qpqConfig, 'user-pool-client-id-export');\n\nconst getOverridableQpqResourceExportName = (\n resourceName: string,\n qpqConfig: QPQConfig,\n resourceType: string,\n serviceOverride?: string,\n applicationOverride?: string,\n) => {\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig, {\n application: applicationOverride,\n module: serviceOverride,\n });\n\n return getQpqRuntimeResourceName(resourceName, application, service, environment, feature, resourceType);\n};\n\nexport const getCFExportNameCachePolicyIdFromConfig = (\n cacheConfigName: string,\n qpqConfig: QPQConfig,\n\n serviceOverride?: string,\n applicationOverride?: string,\n) => getOverridableQpqResourceExportName(cacheConfigName, qpqConfig, 'cache-policy-name-export', serviceOverride, applicationOverride);\n\nexport const getCFExportNameApiKeyIdFromConfig = (\n apiKeyName: string,\n qpqConfig: QPQConfig,\n\n serviceOverride?: string,\n applicationOverride?: string,\n) => getOverridableQpqResourceExportName(apiKeyName, qpqConfig, 'api-key-id-export', serviceOverride, applicationOverride);\n\nexport const getCFExportNameSnsTopicArnFromConfig = (\n eventBusName: string,\n qpqConfig: QPQConfig,\n\n applicationOverride?: string,\n) => {\n const application = applicationOverride || qpqCoreUtils.getApplicationName(qpqConfig);\n const environment = qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);\n const feature = qpqCoreUtils.getApplicationModuleFeature(qpqConfig);\n\n return getGlobalQpqRuntimeResourceName(eventBusName, application, environment, feature, 'sns-topic-arn-export');\n};\n\nexport const getCFExportNameDistributionIdArnFromConfig = (\n webEntryName: string,\n qpqConfig: QPQConfig,\n\n serviceOverride?: string,\n applicationOverride?: string,\n) => getOverridableQpqResourceExportName(webEntryName, qpqConfig, 'distribution-id-export', serviceOverride, applicationOverride);\n\nconst resolveWebsocketApiNaming = (websocketApiName: string, qpqConfig: QPQConfig) => {\n const websocketApiConfig = qpqWebServerUtils.getWebsocketEntryByApiName(websocketApiName, qpqConfig);\n\n return {\n context: resolveOwnerDeploymentContext(qpqConfig, websocketApiConfig.owner),\n resourceName: websocketApiConfig.owner?.resourceNameOverride || websocketApiName,\n };\n};\n\nexport const getCFExportNameWebsocketApiIdFromConfig = (websocketApiName: string, qpqConfig: QPQConfig) => {\n const { context, resourceName } = resolveWebsocketApiNaming(websocketApiName, qpqConfig);\n\n return getQpqRuntimeResourceName(\n resourceName,\n context.application,\n context.service,\n context.environment,\n context.feature,\n 'websocket-api-id-export',\n );\n};\n\n// SSM parameter holding a websocket api's AWS-generated id, written by the owning service's\n// inf stack. Referencing services resolve it at deploy time to build exact execute-api\n// grants (deploy the owning inf stack first - same owner-publishes/others-read pattern as\n// the domain certificate arn parameter).\nexport const getWebsocketApiIdSsmParameterName = (websocketApiName: string, qpqConfig: QPQConfig) => {\n const { context, resourceName } = resolveWebsocketApiNaming(websocketApiName, qpqConfig);\n\n return `/qpq/websocket/api-id/${getConfigRuntimeResourceName(resourceName, context.application, context.service, context.environment, context.feature)}`;\n};\n\nexport const getEventBusSnsTopicArn = (\n eventBusName: string,\n qpqConfig: QPQConfig,\n\n module: string,\n environment: string,\n application: string,\n feature?: string,\n\n isFifo?: boolean,\n) => {\n const topicName = withFifoSuffix(getConfigRuntimeResourceName(eventBusName, application, module, environment, feature), isFifo);\n\n const accountInfo = getAwsServiceAccountInfoByDeploymentInfo(qpqConfig, module, environment, feature, application);\n\n const awsAccountId = accountInfo.awsAccountId;\n const region = accountInfo.awsRegion;\n\n return `arn:aws:sns:${region}:${awsAccountId}:${topicName}`;\n};\n\n// The account stack owns account-level resources (cloud trail, budgets, security services)\n// so they never share an app's lifecycle. The name is deliberately static: stack names are\n// already namespaced per account+region, and the resources it holds meter/audit the whole\n// account - an app/environment segment would only fake a separation that doesn't exist.\n// Everything in one account converges on this one stack, so exactly ONE repo/config must\n// own it, and actor (feature) deploys must not deploy it.\nexport const getAccountStackName = () => 'qpq-account';\n\nexport const getBaseStackName = (qpqConfig: QPQConfig) => {\n const { application, service, environment, feature } = resolveOwnerDeploymentContext(qpqConfig);\n\n const baseName = `${application}-${service}-${environment}`;\n\n if (feature) {\n return `${baseName}-${feature}`;\n }\n\n return baseName;\n};\n\nexport const getInfStackName = (qpqConfig: QPQConfig) => {\n return `${getBaseStackName(qpqConfig)}-inf`;\n};\n\nexport const getWebStackName = (qpqConfig: QPQConfig) => {\n return `${getBaseStackName(qpqConfig)}-web`;\n};\n\nexport const getApiStackName = (qpqConfig: QPQConfig) => {\n return `${getBaseStackName(qpqConfig)}-api`;\n};\n\n// App-level stacks (bootstrap, domain) are shared by every module of the app, so their\n// names carry no module segment.\nconst getAppStackName = (qpqConfig: QPQConfig, suffix: string) => {\n const appName = qpqCoreUtils.getApplicationName(qpqConfig);\n const environment = qpqCoreUtils.getApplicationModuleEnvironment(qpqConfig);\n const feature = qpqCoreUtils.getApplicationModuleFeature(qpqConfig);\n\n const baseName = `${appName}-${environment}`;\n\n if (feature) {\n return `${baseName}-${feature}-${suffix}`;\n }\n\n return `${baseName}-${suffix}`;\n};\n\nexport const getBootstrapStackName = (qpqConfig: QPQConfig) => getAppStackName(qpqConfig, 'bs');\n\nexport const getDomainStackName = (qpqConfig: QPQConfig) => getAppStackName(qpqConfig, 'domain');\n"]}
|
|
@@ -27,7 +27,8 @@ const getResponseFromErrorResult = (error) => {
|
|
|
27
27
|
errorText: error.errorText,
|
|
28
28
|
}, statusCode);
|
|
29
29
|
};
|
|
30
|
-
const getProcessTransformResponseResult = (qpqConfig) => {
|
|
30
|
+
const getProcessTransformResponseResult = async (qpqConfig, loader) => {
|
|
31
|
+
const domainResolver = await qpqWebServerUtils.loadDomainResolver(qpqConfig, loader);
|
|
31
32
|
// We might need to JSON.stringify the body.
|
|
32
33
|
return async ({ eventParams: rawEventParams, qpqEventRecordResponses: rawQpqEventRecordResponses }) => {
|
|
33
34
|
// Registered for one event source only, so the base requester's
|
|
@@ -48,7 +49,7 @@ const getProcessTransformResponseResult = (qpqConfig) => {
|
|
|
48
49
|
// Add the cors headers
|
|
49
50
|
const currentHeaders = successRecord.headers || {};
|
|
50
51
|
const headers = {
|
|
51
|
-
...qpqWebServerUtils.getCorsHeaders(qpqConfig, {}, apiGatewayEvent.headers),
|
|
52
|
+
...qpqWebServerUtils.getCorsHeaders(qpqConfig, {}, apiGatewayEvent.headers, domainResolver),
|
|
52
53
|
...currentHeaders,
|
|
53
54
|
};
|
|
54
55
|
// Transform back to api gateway
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getEventTransformResponseResultActionProcessor.js","sourceRoot":"","sources":["../../../../../../../src/getActionProcessor/core/event/apiGatwayEvent/api/getEventTransformResponseResultActionProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,mCAAmC,EACnC,qBAAqB,
|
|
1
|
+
{"version":3,"file":"getEventTransformResponseResultActionProcessor.js","sourceRoot":"","sources":["../../../../../../../src/getActionProcessor/core/event/apiGatwayEvent/api/getEventTransformResponseResultActionProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n actionResult,\n askEventTransformResponseResultBase,\n createActionProcessor,\n DynamicModuleLoader,\n EitherActionResult,\n ErrorTypeEnum,\n ProcessorFor,\n QPQConfig,\n QPQError,\n} from 'quidproquo-core';\nimport { HttpEventHeaders, qpqWebServerUtils } from 'quidproquo-webserver';\n\nimport { EventInput, EventOutput, InternalEventOutput, InternalEventRecord } from './types';\n\nconst ErrorTypeHttpResponseMap: Record<string, number> = {\n [ErrorTypeEnum.BadRequest]: 400,\n [ErrorTypeEnum.Unauthorized]: 401,\n [ErrorTypeEnum.PaymentRequired]: 402,\n [ErrorTypeEnum.Forbidden]: 403,\n [ErrorTypeEnum.NotFound]: 404,\n [ErrorTypeEnum.TimeOut]: 408,\n [ErrorTypeEnum.Conflict]: 409,\n [ErrorTypeEnum.UnsupportedMediaType]: 415,\n [ErrorTypeEnum.OutOfResources]: 500,\n [ErrorTypeEnum.GenericError]: 500,\n [ErrorTypeEnum.NotImplemented]: 501,\n [ErrorTypeEnum.NoContent]: 204,\n [ErrorTypeEnum.Invalid]: 422,\n};\n\nconst transformHttpEventHeadersToAPIGatewayProxyResultHeaders = (\n headers: HttpEventHeaders,\n): {\n [header: string]: boolean | number | string;\n} => {\n return Object.keys(headers)\n .filter((header) => !!headers[header])\n .reduce((acc, header) => ({ ...acc, [header]: headers[header] }), {});\n};\n\nconst getResponseFromErrorResult = (error: QPQError): InternalEventOutput => {\n const statusCode = ErrorTypeHttpResponseMap[error.errorType] || 500;\n return qpqWebServerUtils.toJsonEventResponse(\n {\n errorType: error.errorType,\n errorText: error.errorText,\n },\n statusCode,\n );\n};\n\nconst getProcessTransformResponseResult = async (\n qpqConfig: QPQConfig,\n loader: DynamicModuleLoader,\n): Promise<ProcessorFor<typeof askEventTransformResponseResultBase>> => {\n const domainResolver = await qpqWebServerUtils.loadDomainResolver(qpqConfig, loader);\n\n // We might need to JSON.stringify the body.\n return async ({ eventParams: rawEventParams, qpqEventRecordResponses: rawQpqEventRecordResponses }) => {\n // Registered for one event source only, so the base requester's\n // source-agnostic payload is narrowed to this source's types here.\n const eventParams = rawEventParams as EventInput;\n const qpqEventRecordResponses = rawQpqEventRecordResponses as EitherActionResult<InternalEventOutput>[];\n\n const [apiGatewayEvent] = eventParams;\n const [record] = qpqEventRecordResponses;\n\n // If we have an error, we need to transform it to a response, otherwise we can just use the record as is\n let successRecord = record.success ? record.result : getResponseFromErrorResult(record.error);\n\n // Return an error if the body is the wrong type.\n if (successRecord.body && typeof successRecord.body !== 'string') {\n successRecord = getResponseFromErrorResult({\n errorText: 'Response body must be a string',\n errorType: ErrorTypeEnum.GenericError,\n });\n }\n\n // Add the cors headers\n const currentHeaders = successRecord.headers || {};\n const headers: HttpEventHeaders = {\n ...qpqWebServerUtils.getCorsHeaders(qpqConfig, {}, apiGatewayEvent.headers, domainResolver),\n ...currentHeaders,\n };\n\n // Transform back to api gateway\n return actionResult<EventOutput>({\n statusCode: successRecord.status,\n body: apiGatewayEvent.httpMethod === 'HEAD' || !successRecord.body ? '' : successRecord.body,\n isBase64Encoded: successRecord.isBase64Encoded,\n headers: transformHttpEventHeadersToAPIGatewayProxyResultHeaders(headers),\n });\n };\n};\n\nexport const getEventTransformResponseResultActionProcessor = createActionProcessor(\n askEventTransformResponseResultBase,\n getProcessTransformResponseResult,\n);\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { actionResult, askEventGetRecordsBase, createActionProcessor } from 'quidproquo-core';
|
|
1
|
+
import { actionResult, askEventGetRecordsBase, createActionProcessor, } from 'quidproquo-core';
|
|
2
2
|
import { qpqWebServerUtils } from 'quidproquo-webserver';
|
|
3
|
-
const getProcessGetRecords = (qpqConfig) => {
|
|
3
|
+
const getProcessGetRecords = async (qpqConfig, loader) => {
|
|
4
|
+
const domainResolver = await qpqWebServerUtils.loadDomainResolver(qpqConfig, loader);
|
|
4
5
|
return async ({ eventParams }) => {
|
|
5
6
|
// Registered for one event source only, so the base requester's
|
|
6
7
|
// source-agnostic payload is narrowed to this source's types here.
|
|
@@ -12,7 +13,9 @@ const getProcessGetRecords = (qpqConfig) => {
|
|
|
12
13
|
[header]: cfRecordRequest.headers[header][0].value,
|
|
13
14
|
}), {});
|
|
14
15
|
const internalRecord = {
|
|
15
|
-
domain
|
|
16
|
+
// Without a declared domain there is nothing to validate the Host header against,
|
|
17
|
+
// so it is the only value left; with one, a matched root wins over the header.
|
|
18
|
+
domain: qpqWebServerUtils.resolveHostForRequestHost(qpqConfig, headers.host, {}, domainResolver) ?? headers.host ?? '',
|
|
16
19
|
body: cfRecordRequest.body,
|
|
17
20
|
correlation: context.awsRequestId,
|
|
18
21
|
method: cfRecordRequest.method,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getEventGetRecordsActionProcessor.js","sourceRoot":"","sources":["../../../../../../src/getActionProcessor/core/event/cloudFrontOriginRequest/getEventGetRecordsActionProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"getEventGetRecordsActionProcessor.js","sourceRoot":"","sources":["../../../../../../src/getActionProcessor/core/event/cloudFrontOriginRequest/getEventGetRecordsActionProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,sBAAsB,EACtB,qBAAqB,GAMtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAIzD,MAAM,oBAAoB,GAAG,KAAK,EAAE,SAAoB,EAAE,MAA2B,EAAwD,EAAE;IAC7I,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAErF,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;QAC/B,gEAAgE;QAChE,mEAAmE;QACnE,MAAM,CAAC,sBAAsB,EAAE,OAAO,CAAC,GAAG,WAAyB,CAAC;QAEpE,MAAM,OAAO,GAAG,sBAAsB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YAC5D,MAAM,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC;YAE1C,MAAM,OAAO,GAA2B,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,MAAM,CACjF,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;gBAChB,GAAG,GAAG;gBACN,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK;aACnD,CAAC,EACF,EAAE,CACH,CAAC;YAEF,MAAM,cAAc,GAAwB;gBAC1C,kFAAkF;gBAClF,+EAA+E;gBAC/E,MAAM,EAAE,iBAAiB,CAAC,yBAAyB,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,cAAc,CAAC,IAAI,OAAO,CAAC,IAAI,IAAI,EAAE;gBACtH,IAAI,EAAE,eAAe,CAAC,IAAI;gBAC1B,WAAW,EAAE,OAAO,CAAC,YAAY;gBACjC,MAAM,EAAE,eAAe,CAAC,MAAoB;gBAC5C,IAAI,EAAE,eAAe,CAAC,GAAG;gBACzB,QAAQ,EAAE,eAAe,CAAC,QAAQ;gBAClC,OAAO,EAAE,OAAO;gBAChB,6BAA6B;gBAC7B,KAAK,EAAE,EAAE;aACV,CAAC;YAEF,OAAO,cAAc,CAAC;QACxB,CAAC,CAAC,CAAC;QAEH,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iCAAiC,GAAG,qBAAqB,CAAC,sBAAsB,EAAE,oBAAoB,CAAC,CAAC","sourcesContent":["import {\n actionResult,\n askEventGetRecordsBase,\n createActionProcessor,\n DynamicModuleLoader,\n EventActionType,\n HTTPMethod,\n ProcessorFor,\n QPQConfig,\n} from 'quidproquo-core';\nimport { qpqWebServerUtils } from 'quidproquo-webserver';\n\nimport { EventInput, InternalEventRecord } from './types';\n\nconst getProcessGetRecords = async (qpqConfig: QPQConfig, loader: DynamicModuleLoader): Promise<ProcessorFor<typeof askEventGetRecordsBase>> => {\n const domainResolver = await qpqWebServerUtils.loadDomainResolver(qpqConfig, loader);\n\n return async ({ eventParams }) => {\n // Registered for one event source only, so the base requester's\n // source-agnostic payload is narrowed to this source's types here.\n const [cloudFrontRequestEvent, context] = eventParams as EventInput;\n\n const records = cloudFrontRequestEvent.Records.map((record) => {\n const cfRecordRequest = record.cf.request;\n\n const headers: Record<string, string> = Object.keys(cfRecordRequest.headers).reduce(\n (acc, header) => ({\n ...acc,\n [header]: cfRecordRequest.headers[header][0].value,\n }),\n {},\n );\n\n const internalRecord: InternalEventRecord = {\n // Without a declared domain there is nothing to validate the Host header against,\n // so it is the only value left; with one, a matched root wins over the header.\n domain: qpqWebServerUtils.resolveHostForRequestHost(qpqConfig, headers.host, {}, domainResolver) ?? headers.host ?? '',\n body: cfRecordRequest.body,\n correlation: context.awsRequestId,\n method: cfRecordRequest.method as HTTPMethod,\n path: cfRecordRequest.uri,\n sourceIp: cfRecordRequest.clientIp,\n headers: headers,\n // TODO: query string support\n query: {},\n };\n\n return internalRecord;\n });\n\n return actionResult(records);\n };\n};\n\nexport const getEventGetRecordsActionProcessor = createActionProcessor(askEventGetRecordsBase, getProcessGetRecords);\n"]}
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js
CHANGED
|
@@ -28,6 +28,7 @@ const getProcessKeyValueStoreUpsert = (qpqConfig) => {
|
|
|
28
28
|
InternalServerError: () => actionResultError(askKeyValueStoreUpsertBase.errorType.ServiceUnavailable, 'KVS Service Unavailable'),
|
|
29
29
|
ResourceNotFoundException: () => actionResultError(askKeyValueStoreUpsertBase.errorType.ResourceNotFound, 'KVS Resource Not Found'),
|
|
30
30
|
ConditionalCheckFailedException: () => actionResultError(askKeyValueStoreUpsertBase.errorType.Conflict, 'KVS item already exists'),
|
|
31
|
+
TransactionConflictException: () => actionResultError(askKeyValueStoreUpsertBase.errorType.WriteContention, 'KVS item is being written by a transaction'),
|
|
31
32
|
InvalidScopeError: (error) => actionResultError(askKeyValueStoreUpsertBase.errorType.InvalidScope, error.message),
|
|
32
33
|
KvsStoreNotFoundError: (error) => actionResultError(askKeyValueStoreUpsertBase.errorType.StoreNotFound, error.message),
|
|
33
34
|
});
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getKeyValueStoreUpsertActionProcessor.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gCAAgC,EAChC,0BAA0B,EAC1B,qBAAqB,EACrB,6BAA6B,EAI7B,4BAA4B,GAC7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,+BAA+B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,MAAM,6BAA6B,GAAG,CAAC,SAAoB,EAAmD,EAAE;IAC9G,OAAO,KAAK,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;QACpD,MAAM,eAAe,GAAG,+BAA+B,CAAC,iBAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAC7F,MAAM,MAAM,GAAG,iBAAiB,CAAC,gCAAgC,CAAC,SAAS,CAAC,CAAC;QAE7E,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,4BAA4B,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;YAE/E,MAAM,IAAI,GAAG;gBACX,WAAW,CAAC,YAAY;gBACxB,GAAG,WAAW,CAAC,QAAQ;gBACvB,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC;gBACjD,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAQ,CAAC;aACzE,CAAC;YAEF,wEAAwE;YACxE,yDAAyD;YACzD,MAAM,MAAM,GAAG,6BAA6B,CAAC,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YAE3F,MAAM,OAAO,CACX,eAAe,EACf,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EACjB,IAAI,EACJ;gBACE,OAAO,EAAE,OAAO,EAAE,YAAY;gBAC9B,oBAAoB,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;aACtF,EACD,MAAM,CACP,CAAC;YAEF,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,OAAO,gCAAgC,CAAC,KAAK,EAAE;gBAC7C,mBAAmB,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,0BAA0B,CAAC,SAAS,CAAC,kBAAkB,EAAE,yBAAyB,CAAC;gBAChI,yBAAyB,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,0BAA0B,CAAC,SAAS,CAAC,gBAAgB,EAAE,wBAAwB,CAAC;gBACnI,+BAA+B,EAAE,GAAG,EAAE,CAAC,iBAAiB,CAAC,0BAA0B,CAAC,SAAS,CAAC,QAAQ,EAAE,yBAAyB,CAAC;gBAClI,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,0BAA0B,CAAC,SAAS,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,CAAC;gBACjH,qBAAqB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,iBAAiB,CAAC,0BAA0B,CAAC,SAAS,CAAC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC;aACvH,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qCAAqC,GAAG,qBAAqB,CAAC,0BAA0B,EAAE,6BAA6B,CAAC,CAAC","sourcesContent":["import { qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport {\n actionResult,\n actionResultError,\n actionResultErrorFromCaughtError,\n askKeyValueStoreUpsertBase,\n createActionProcessor,\n getScopedKvsTranslatorOrThrow,\n KeyValueStoreActionType,\n ProcessorFor,\n QPQConfig,\n resolveKvsStoreConfigOrThrow,\n} from 'quidproquo-core';\n\nimport { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';\nimport { putItem } from '../../../logic/dynamo';\n\nconst getProcessKeyValueStoreUpsert = (qpqConfig: QPQConfig): ProcessorFor<typeof askKeyValueStoreUpsertBase> => {\n return async ({ keyValueStoreName, item, options }) => {\n const dynamoTableName = getKvsDynamoTableNameFromConfig(keyValueStoreName, qpqConfig, 'kvs');\n const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);\n\n try {\n const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);\n\n const keys = [\n storeConfig.partitionKey,\n ...storeConfig.sortKeys,\n ...storeConfig.indexes.map((i) => i.partitionKey),\n ...storeConfig.indexes.filter((i) => !!i.sortKey).map((i) => i.sortKey!),\n ];\n\n // The scope lives inside the stored partition key value, so the item is\n // persisted with a composed pk; reads strip it back off.\n const scoped = getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, options?.scope);\n\n await putItem(\n dynamoTableName,\n scoped.item(item),\n keys,\n {\n expires: options?.ttlInSeconds,\n ifNotExistsAttribute: options?.ifNotExists ? storeConfig.partitionKey.key : undefined,\n },\n region,\n );\n\n return actionResult(void 0);\n } catch (error: unknown) {\n return actionResultErrorFromCaughtError(error, {\n InternalServerError: () => actionResultError(askKeyValueStoreUpsertBase.errorType.ServiceUnavailable, 'KVS Service Unavailable'),\n ResourceNotFoundException: () => actionResultError(askKeyValueStoreUpsertBase.errorType.ResourceNotFound, 'KVS Resource Not Found'),\n ConditionalCheckFailedException: () => actionResultError(askKeyValueStoreUpsertBase.errorType.Conflict, 'KVS item already exists'),\n InvalidScopeError: (error) => actionResultError(askKeyValueStoreUpsertBase.errorType.InvalidScope, error.message),\n KvsStoreNotFoundError: (error) => actionResultError(askKeyValueStoreUpsertBase.errorType.StoreNotFound, error.message),\n });\n }\n };\n};\n\nexport const getKeyValueStoreUpsertActionProcessor = createActionProcessor(askKeyValueStoreUpsertBase, getProcessKeyValueStoreUpsert);\n"]}
|
|
1
|
+
{"version":3,"file":"getKeyValueStoreUpsertActionProcessor.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertActionProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport {\n actionResult,\n actionResultError,\n actionResultErrorFromCaughtError,\n askKeyValueStoreUpsertBase,\n createActionProcessor,\n getScopedKvsTranslatorOrThrow,\n KeyValueStoreActionType,\n ProcessorFor,\n QPQConfig,\n resolveKvsStoreConfigOrThrow,\n} from 'quidproquo-core';\n\nimport { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';\nimport { putItem } from '../../../logic/dynamo';\n\nconst getProcessKeyValueStoreUpsert = (qpqConfig: QPQConfig): ProcessorFor<typeof askKeyValueStoreUpsertBase> => {\n return async ({ keyValueStoreName, item, options }) => {\n const dynamoTableName = getKvsDynamoTableNameFromConfig(keyValueStoreName, qpqConfig, 'kvs');\n const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);\n\n try {\n const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);\n\n const keys = [\n storeConfig.partitionKey,\n ...storeConfig.sortKeys,\n ...storeConfig.indexes.map((i) => i.partitionKey),\n ...storeConfig.indexes.filter((i) => !!i.sortKey).map((i) => i.sortKey!),\n ];\n\n // The scope lives inside the stored partition key value, so the item is\n // persisted with a composed pk; reads strip it back off.\n const scoped = getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, options?.scope);\n\n await putItem(\n dynamoTableName,\n scoped.item(item),\n keys,\n {\n expires: options?.ttlInSeconds,\n ifNotExistsAttribute: options?.ifNotExists ? storeConfig.partitionKey.key : undefined,\n },\n region,\n );\n\n return actionResult(void 0);\n } catch (error: unknown) {\n return actionResultErrorFromCaughtError(error, {\n InternalServerError: () => actionResultError(askKeyValueStoreUpsertBase.errorType.ServiceUnavailable, 'KVS Service Unavailable'),\n ResourceNotFoundException: () => actionResultError(askKeyValueStoreUpsertBase.errorType.ResourceNotFound, 'KVS Resource Not Found'),\n ConditionalCheckFailedException: () => actionResultError(askKeyValueStoreUpsertBase.errorType.Conflict, 'KVS item already exists'),\n TransactionConflictException: () =>\n actionResultError(askKeyValueStoreUpsertBase.errorType.WriteContention, 'KVS item is being written by a transaction'),\n InvalidScopeError: (error) => actionResultError(askKeyValueStoreUpsertBase.errorType.InvalidScope, error.message),\n KvsStoreNotFoundError: (error) => actionResultError(askKeyValueStoreUpsertBase.errorType.StoreNotFound, error.message),\n });\n }\n };\n};\n\nexport const getKeyValueStoreUpsertActionProcessor = createActionProcessor(askKeyValueStoreUpsertBase, getProcessKeyValueStoreUpsert);\n"]}
|
package/lib/esm/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { qpqConfigAwsUtils } from 'quidproquo-config-aws';
|
|
2
2
|
import { actionResult, actionResultError, actionResultErrorFromCaughtError, askKeyValueStoreUpsertManyBase, createActionProcessor, getScopedKvsTranslatorOrThrow, resolveKvsStoreConfigOrThrow, } from 'quidproquo-core';
|
|
3
3
|
import { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';
|
|
4
|
-
import { batchPutItems } from '../../../logic/dynamo';
|
|
5
|
-
// The batch sibling of Upsert: BatchWriteItem in chunks of 25,
|
|
6
|
-
// retry inside batchPutItems.
|
|
7
|
-
//
|
|
8
|
-
//
|
|
4
|
+
import { batchPutItems, transactPutItems } from '../../../logic/dynamo';
|
|
5
|
+
// The batch sibling of Upsert. Unconditional: BatchWriteItem in chunks of 25,
|
|
6
|
+
// partial-acceptance retry inside batchPutItems. With ifNotExists: TransactWriteItems
|
|
7
|
+
// in chunks of 100, every put conditional, each chunk all-or-nothing, a lost
|
|
8
|
+
// condition surfacing as the namespaced Conflict. Streams emit one record per
|
|
9
|
+
// item no matter how it was written, so downstream projectors see the same
|
|
10
|
+
// records as N single upserts.
|
|
9
11
|
const getProcessKeyValueStoreUpsertMany = (qpqConfig) => {
|
|
10
12
|
return async ({ keyValueStoreName, items, options }) => {
|
|
11
13
|
const dynamoTableName = getKvsDynamoTableNameFromConfig(keyValueStoreName, qpqConfig, 'kvs');
|
|
@@ -27,7 +29,13 @@ const getProcessKeyValueStoreUpsertMany = (qpqConfig) => {
|
|
|
27
29
|
// The scope lives inside the stored partition key value, so each item is
|
|
28
30
|
// persisted with a composed pk; reads strip it back off.
|
|
29
31
|
const scoped = getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, options?.scope);
|
|
30
|
-
|
|
32
|
+
const scopedItems = items.map((item) => scoped.item(item));
|
|
33
|
+
if (options?.ifNotExists) {
|
|
34
|
+
await transactPutItems(dynamoTableName, scopedItems, storeConfig.partitionKey.key, region);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
await batchPutItems(dynamoTableName, scopedItems, region);
|
|
38
|
+
}
|
|
31
39
|
return actionResult(void 0);
|
|
32
40
|
}
|
|
33
41
|
catch (error) {
|
|
@@ -37,6 +45,8 @@ const getProcessKeyValueStoreUpsertMany = (qpqConfig) => {
|
|
|
37
45
|
// Retries exhausted on UnprocessedItems — sustained throttle, a transient
|
|
38
46
|
// fault: callers must see ServiceUnavailable, not a permanent failure.
|
|
39
47
|
BatchWriteUnprocessedItemsError: () => actionResultError(askKeyValueStoreUpsertManyBase.errorType.ServiceUnavailable, 'KVS batch write throttled'),
|
|
48
|
+
ConditionalCheckFailedException: () => actionResultError(askKeyValueStoreUpsertManyBase.errorType.Conflict, 'KVS item already exists'),
|
|
49
|
+
TransactionConflictException: () => actionResultError(askKeyValueStoreUpsertManyBase.errorType.WriteContention, 'KVS item is being written by a transaction'),
|
|
40
50
|
InvalidScopeError: (error) => actionResultError(askKeyValueStoreUpsertManyBase.errorType.InvalidScope, error.message),
|
|
41
51
|
KvsStoreNotFoundError: (error) => actionResultError(askKeyValueStoreUpsertManyBase.errorType.StoreNotFound, error.message),
|
|
42
52
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getKeyValueStoreUpsertManyActionProcessor.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,gCAAgC,EAChC,8BAA8B,EAC9B,qBAAqB,EACrB,6BAA6B,EAI7B,4BAA4B,GAC7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,+BAA+B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"getKeyValueStoreUpsertManyActionProcessor.js","sourceRoot":"","sources":["../../../../../src/getActionProcessor/core/keyValueStore/getKeyValueStoreUpsertManyActionProcessor.ts"],"names":[],"mappings":"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","sourcesContent":["import { qpqConfigAwsUtils } from 'quidproquo-config-aws';\nimport {\n actionResult,\n actionResultError,\n actionResultErrorFromCaughtError,\n askKeyValueStoreUpsertManyBase,\n createActionProcessor,\n getScopedKvsTranslatorOrThrow,\n KeyValueStoreActionType,\n ProcessorFor,\n QPQConfig,\n resolveKvsStoreConfigOrThrow,\n} from 'quidproquo-core';\n\nimport { getKvsDynamoTableNameFromConfig } from '../../../awsNamingUtils';\nimport { batchPutItems, transactPutItems } from '../../../logic/dynamo';\n\n// The batch sibling of Upsert. Unconditional: BatchWriteItem in chunks of 25,\n// partial-acceptance retry inside batchPutItems. With ifNotExists: TransactWriteItems\n// in chunks of 100, every put conditional, each chunk all-or-nothing, a lost\n// condition surfacing as the namespaced Conflict. Streams emit one record per\n// item no matter how it was written, so downstream projectors see the same\n// records as N single upserts.\nconst getProcessKeyValueStoreUpsertMany = (qpqConfig: QPQConfig): ProcessorFor<typeof askKeyValueStoreUpsertManyBase> => {\n return async ({ keyValueStoreName, items, options }) => {\n const dynamoTableName = getKvsDynamoTableNameFromConfig(keyValueStoreName, qpqConfig, 'kvs');\n const region = qpqConfigAwsUtils.getApplicationModuleDeployRegion(qpqConfig);\n\n try {\n const storeConfig = resolveKvsStoreConfigOrThrow(qpqConfig, keyValueStoreName);\n\n // Same-key duplicates are rejected UP FRONT, before anything is written.\n // Dynamo itself only rejects them when both land in the same 25-item\n // chunk — across chunks the later write silently wins — so left to the\n // service the outcome would depend on item position.\n const seenKeys = new Set<string>();\n for (const item of items) {\n const itemKey = [storeConfig.partitionKey, ...storeConfig.sortKeys].map((key) => String(item[key.key])).join('#');\n if (seenKeys.has(itemKey)) {\n return actionResultError(\n askKeyValueStoreUpsertManyBase.errorType.DuplicateKey,\n `Duplicate key [${itemKey}] in batch upsert to [${keyValueStoreName}]`,\n );\n }\n seenKeys.add(itemKey);\n }\n\n // The scope lives inside the stored partition key value, so each item is\n // persisted with a composed pk; reads strip it back off.\n const scoped = getScopedKvsTranslatorOrThrow(qpqConfig, keyValueStoreName, options?.scope);\n\n const scopedItems = items.map((item) => scoped.item(item));\n\n if (options?.ifNotExists) {\n await transactPutItems(dynamoTableName, scopedItems, storeConfig.partitionKey.key, region);\n } else {\n await batchPutItems(dynamoTableName, scopedItems, region);\n }\n\n return actionResult(void 0);\n } catch (error: unknown) {\n return actionResultErrorFromCaughtError(error, {\n InternalServerError: () => actionResultError(askKeyValueStoreUpsertManyBase.errorType.ServiceUnavailable, 'KVS Service Unavailable'),\n ResourceNotFoundException: () => actionResultError(askKeyValueStoreUpsertManyBase.errorType.ResourceNotFound, 'KVS Resource Not Found'),\n // Retries exhausted on UnprocessedItems — sustained throttle, a transient\n // fault: callers must see ServiceUnavailable, not a permanent failure.\n BatchWriteUnprocessedItemsError: () =>\n actionResultError(askKeyValueStoreUpsertManyBase.errorType.ServiceUnavailable, 'KVS batch write throttled'),\n ConditionalCheckFailedException: () => actionResultError(askKeyValueStoreUpsertManyBase.errorType.Conflict, 'KVS item already exists'),\n TransactionConflictException: () =>\n actionResultError(askKeyValueStoreUpsertManyBase.errorType.WriteContention, 'KVS item is being written by a transaction'),\n InvalidScopeError: (error) => actionResultError(askKeyValueStoreUpsertManyBase.errorType.InvalidScope, error.message),\n KvsStoreNotFoundError: (error) => actionResultError(askKeyValueStoreUpsertManyBase.errorType.StoreNotFound, error.message),\n });\n }\n };\n};\n\nexport const getKeyValueStoreUpsertManyActionProcessor = createActionProcessor(askKeyValueStoreUpsertManyBase, getProcessKeyValueStoreUpsertMany);\n"]}
|
|
@@ -1,25 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Standalone 301 handler for subdomain redirects.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Standalone 301 handler for subdomain redirects. The CDK redirect construct resolves the
|
|
3
|
+
* target at synth and JSON-encodes it onto the function env: `redirectBaseUrl` (absolute,
|
|
4
|
+
* no trailing path) and `appendPath` (true for domain redirects, which keep the request
|
|
5
|
+
* path; false for an explicit absolute url, which redirects as-is). No qpq runtime involved.
|
|
5
6
|
*/
|
|
6
7
|
const apiGatewayEventHandler_redirect = async (event, context) => {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let redirectUrl = redirectConfig.redirectUrl;
|
|
11
|
-
if (!redirectConfig.redirectUrl.startsWith('http')) {
|
|
12
|
-
const environment = JSON.parse(process.env.environment);
|
|
13
|
-
const featureEnvironment = JSON.parse(process.env.featureEnvironment || '""');
|
|
14
|
-
let baseDomain = redirectConfig.redirectUrl;
|
|
15
|
-
if (redirectConfig.addEnvironment && environment !== 'production') {
|
|
16
|
-
baseDomain = `${environment}.${baseDomain}`;
|
|
17
|
-
}
|
|
18
|
-
if (redirectConfig.addFeatureEnvironment && featureEnvironment) {
|
|
19
|
-
baseDomain = `${featureEnvironment}.${baseDomain}`;
|
|
20
|
-
}
|
|
21
|
-
redirectUrl = `https://${baseDomain}${event.path}`;
|
|
22
|
-
}
|
|
8
|
+
const redirectBaseUrl = JSON.parse(process.env.redirectBaseUrl);
|
|
9
|
+
const appendPath = JSON.parse(process.env.appendPath || 'false');
|
|
10
|
+
let redirectUrl = appendPath ? `${redirectBaseUrl}${event.path}` : redirectBaseUrl;
|
|
23
11
|
const queryParams = event.queryStringParameters;
|
|
24
12
|
if (queryParams) {
|
|
25
13
|
const queryString = Object.entries(queryParams)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getApiGatewayEventHandler_redirect.js","sourceRoot":"","sources":["../../../src/lambdaHandlers/getApiGatewayEventHandler_redirect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"getApiGatewayEventHandler_redirect.js","sourceRoot":"","sources":["../../../src/lambdaHandlers/getApiGatewayEventHandler_redirect.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,+BAA+B,GAAG,KAAK,EAAE,KAAsB,EAAE,OAAgB,EAAE,EAAE;IACzF,MAAM,eAAe,GAAW,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,eAAyB,CAAC,CAAC;IAClF,MAAM,UAAU,GAAY,IAAI,CAAC,KAAK,CAAE,OAAO,CAAC,GAAG,CAAC,UAAiC,IAAI,OAAO,CAAC,CAAC;IAElG,IAAI,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,eAAe,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC;IAEnF,MAAM,WAAW,GAAG,KAAK,CAAC,qBAAqB,CAAC;IAChD,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;aAC5C,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC;aAC1C,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,KAAe,CAAC,EAAE,CAAC;aAC1F,IAAI,CAAC,GAAG,CAAC,CAAC;QACb,WAAW,IAAI,IAAI,WAAW,EAAE,CAAC;IACnC,CAAC;IAED,OAAO;QACL,UAAU,EAAE,GAAG;QACf,IAAI,EAAE,EAAE;QACR,OAAO,EAAE;YACP,QAAQ,EAAE,WAAW;SACtB;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kCAAkC,GAAG,GAAG,EAAE,CAAC,+BAA+B,CAAC","sourcesContent":["import { APIGatewayEvent, Context } from 'aws-lambda';\n\n/**\n * Standalone 301 handler for subdomain redirects. The CDK redirect construct resolves the\n * target at synth and JSON-encodes it onto the function env: `redirectBaseUrl` (absolute,\n * no trailing path) and `appendPath` (true for domain redirects, which keep the request\n * path; false for an explicit absolute url, which redirects as-is). No qpq runtime involved.\n */\nconst apiGatewayEventHandler_redirect = async (event: APIGatewayEvent, context: Context) => {\n const redirectBaseUrl: string = JSON.parse(process.env.redirectBaseUrl as string);\n const appendPath: boolean = JSON.parse((process.env.appendPath as string | undefined) || 'false');\n\n let redirectUrl = appendPath ? `${redirectBaseUrl}${event.path}` : redirectBaseUrl;\n\n const queryParams = event.queryStringParameters;\n if (queryParams) {\n const queryString = Object.entries(queryParams)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`)\n .join('&');\n redirectUrl += `?${queryString}`;\n }\n\n return {\n statusCode: 301,\n body: '',\n headers: {\n Location: redirectUrl,\n },\n };\n};\n\nexport const getApiGatewayEventHandler_redirect = () => apiGatewayEventHandler_redirect;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC","sourcesContent":["export * from './batchPutItems';\nexport * from './convertDynamoMapToObject';\nexport * from './deleteItem';\nexport * from './getAllItems';\nexport * from './getItem';\nexport * from './putItem';\nexport * from './query';\nexport * from './scan';\nexport * from './updateItem';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,oBAAoB,CAAC;AACnC,cAAc,cAAc,CAAC","sourcesContent":["export * from './batchPutItems';\nexport * from './convertDynamoMapToObject';\nexport * from './deleteItem';\nexport * from './getAllItems';\nexport * from './getItem';\nexport * from './putItem';\nexport * from './query';\nexport * from './scan';\nexport * from './transactPutItems';\nexport * from './updateItem';\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { KvsObjectDataType } from 'quidproquo-core';
|
|
2
|
+
export declare function transactPutItems(tableName: string, items: KvsObjectDataType[], partitionKeyAttribute: string, region: string): Promise<void>;
|
|
3
|
+
/** Same name the single-item PutItem path throws on a failed condition. */
|
|
4
|
+
export declare class ConditionalCheckFailedException extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/** Same name the single-item PutItem path throws when a transaction holds the item. */
|
|
8
|
+
export declare class TransactionConflictException extends Error {
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DynamoDBClient, TransactWriteItemsCommand } from '@aws-sdk/client-dynamodb';
|
|
2
|
+
import { createAwsClient } from '../createAwsClient';
|
|
3
|
+
import { convertObjectToDynamoItem } from './qpqDynamoOrm';
|
|
4
|
+
// TransactWriteItems accepts at most 100 actions per call.
|
|
5
|
+
const TRANSACT_WRITE_MAX_ITEMS = 100;
|
|
6
|
+
// Conditional batched puts: every item is a Put guarded by attribute_not_exists
|
|
7
|
+
// on the partition key, and each chunk commits atomically - one existing key
|
|
8
|
+
// fails the whole chunk and nothing in it lands. That is the property
|
|
9
|
+
// BatchWriteItem cannot give (no conditions at all), and why this exists
|
|
10
|
+
// alongside batchPutItems.
|
|
11
|
+
//
|
|
12
|
+
// Chunks are INDEPENDENT transactions. A batch over 100 items that fails on a
|
|
13
|
+
// later chunk leaves the earlier chunks committed, so callers claiming a run of
|
|
14
|
+
// keys must treat a thrown ConditionalCheckFailedException as "re-read and
|
|
15
|
+
// re-lap the whole batch" - the earlier chunks' keys are now simply taken, by
|
|
16
|
+
// them.
|
|
17
|
+
//
|
|
18
|
+
// The SDK reports a lost race as TransactionCanceledException with a reason per
|
|
19
|
+
// action. A ConditionalCheckFailed reason (the key exists) is rethrown as
|
|
20
|
+
// ConditionalCheckFailedException and a TransactionConflict reason (another
|
|
21
|
+
// write holds the item for the moment) as TransactionConflictException, the same
|
|
22
|
+
// names the single-item PutItem raises, so the processors' error maps key on one
|
|
23
|
+
// name each. Exists wins when both appear: it is final. Every other reason
|
|
24
|
+
// (throttle, validation) is rethrown untouched.
|
|
25
|
+
export async function transactPutItems(tableName, items, partitionKeyAttribute, region) {
|
|
26
|
+
const dynamoDBClient = createAwsClient(DynamoDBClient, { region });
|
|
27
|
+
for (let offset = 0; offset < items.length; offset += TRANSACT_WRITE_MAX_ITEMS) {
|
|
28
|
+
const transactItems = items.slice(offset, offset + TRANSACT_WRITE_MAX_ITEMS).map((item) => ({
|
|
29
|
+
Put: {
|
|
30
|
+
TableName: tableName,
|
|
31
|
+
Item: convertObjectToDynamoItem(item),
|
|
32
|
+
ConditionExpression: 'attribute_not_exists(#ineAttr)',
|
|
33
|
+
ExpressionAttributeNames: { '#ineAttr': partitionKeyAttribute },
|
|
34
|
+
},
|
|
35
|
+
}));
|
|
36
|
+
try {
|
|
37
|
+
await dynamoDBClient.send(new TransactWriteItemsCommand({ TransactItems: transactItems }));
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const reasons = cancellationReasons(error);
|
|
41
|
+
if (reasons.includes('ConditionalCheckFailed')) {
|
|
42
|
+
throw new ConditionalCheckFailedException(`Conditional batch write to [${tableName}] lost to an existing item`);
|
|
43
|
+
}
|
|
44
|
+
if (reasons.includes('TransactionConflict')) {
|
|
45
|
+
throw new TransactionConflictException(`Conditional batch write to [${tableName}] raced another write on one of its items`);
|
|
46
|
+
}
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Same name the single-item PutItem path throws on a failed condition. */
|
|
52
|
+
export class ConditionalCheckFailedException extends Error {
|
|
53
|
+
constructor(message) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = 'ConditionalCheckFailedException';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Same name the single-item PutItem path throws when a transaction holds the item. */
|
|
59
|
+
export class TransactionConflictException extends Error {
|
|
60
|
+
constructor(message) {
|
|
61
|
+
super(message);
|
|
62
|
+
this.name = 'TransactionConflictException';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const cancellationReasons = (error) => {
|
|
66
|
+
const cancelled = error;
|
|
67
|
+
if (cancelled?.name !== 'TransactionCanceledException') {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
return (cancelled.CancellationReasons ?? []).map((reason) => reason.Code ?? '');
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=transactPutItems.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactPutItems.js","sourceRoot":"","sources":["../../../../src/logic/dynamo/transactPutItems.ts"],"names":[],"mappings":"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","sourcesContent":["import { KvsObjectDataType } from 'quidproquo-core';\n\nimport { DynamoDBClient, TransactWriteItemsCommand } from '@aws-sdk/client-dynamodb';\n\nimport { createAwsClient } from '../createAwsClient';\nimport { convertObjectToDynamoItem } from './qpqDynamoOrm';\n\n// TransactWriteItems accepts at most 100 actions per call.\nconst TRANSACT_WRITE_MAX_ITEMS = 100;\n\n// Conditional batched puts: every item is a Put guarded by attribute_not_exists\n// on the partition key, and each chunk commits atomically - one existing key\n// fails the whole chunk and nothing in it lands. That is the property\n// BatchWriteItem cannot give (no conditions at all), and why this exists\n// alongside batchPutItems.\n//\n// Chunks are INDEPENDENT transactions. A batch over 100 items that fails on a\n// later chunk leaves the earlier chunks committed, so callers claiming a run of\n// keys must treat a thrown ConditionalCheckFailedException as \"re-read and\n// re-lap the whole batch\" - the earlier chunks' keys are now simply taken, by\n// them.\n//\n// The SDK reports a lost race as TransactionCanceledException with a reason per\n// action. A ConditionalCheckFailed reason (the key exists) is rethrown as\n// ConditionalCheckFailedException and a TransactionConflict reason (another\n// write holds the item for the moment) as TransactionConflictException, the same\n// names the single-item PutItem raises, so the processors' error maps key on one\n// name each. Exists wins when both appear: it is final. Every other reason\n// (throttle, validation) is rethrown untouched.\nexport async function transactPutItems(tableName: string, items: KvsObjectDataType[], partitionKeyAttribute: string, region: string): Promise<void> {\n const dynamoDBClient = createAwsClient(DynamoDBClient, { region });\n\n for (let offset = 0; offset < items.length; offset += TRANSACT_WRITE_MAX_ITEMS) {\n const transactItems = items.slice(offset, offset + TRANSACT_WRITE_MAX_ITEMS).map((item) => ({\n Put: {\n TableName: tableName,\n Item: convertObjectToDynamoItem(item),\n ConditionExpression: 'attribute_not_exists(#ineAttr)',\n ExpressionAttributeNames: { '#ineAttr': partitionKeyAttribute },\n },\n }));\n\n try {\n await dynamoDBClient.send(new TransactWriteItemsCommand({ TransactItems: transactItems }));\n } catch (error: unknown) {\n const reasons = cancellationReasons(error);\n\n if (reasons.includes('ConditionalCheckFailed')) {\n throw new ConditionalCheckFailedException(`Conditional batch write to [${tableName}] lost to an existing item`);\n }\n if (reasons.includes('TransactionConflict')) {\n throw new TransactionConflictException(`Conditional batch write to [${tableName}] raced another write on one of its items`);\n }\n throw error;\n }\n }\n}\n\n/** Same name the single-item PutItem path throws on a failed condition. */\nexport class ConditionalCheckFailedException extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConditionalCheckFailedException';\n }\n}\n\n/** Same name the single-item PutItem path throws when a transaction holds the item. */\nexport class TransactionConflictException extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TransactionConflictException';\n }\n}\n\ntype TransactionCancelledLike = {\n name?: string;\n CancellationReasons?: { Code?: string }[];\n};\n\nconst cancellationReasons = (error: unknown): string[] => {\n const cancelled = error as TransactionCancelledLike;\n\n if (cancelled?.name !== 'TransactionCanceledException') {\n return [];\n }\n\n return (cancelled.CancellationReasons ?? []).map((reason) => reason.Code ?? '');\n};\n"]}
|