diff --git a/src/common/context.ts b/src/common/context.ts index 1ece23f..6b36975 100644 --- a/src/common/context.ts +++ b/src/common/context.ts @@ -26,8 +26,9 @@ export const setContext = (config: { location?: string; parameters?: { [key: string]: string }; iacProvider?: ServerlessIac['provider']; + stages?: ServerlessIac['stages']; }): void => { - const context = { + const context: Context = { stage: config.stage ?? 'default', stackName: config.stackName ?? '', provider: (config.provider ?? config.iacProvider?.name ?? ProviderEnum.ALIYUN) as ProviderEnum, @@ -42,6 +43,13 @@ export const setContext = (config: { securityToken: config.securityToken ?? process.env.ALIYUN_SECURITY_TOKEN, iacLocation: getIacLocation(config.location), parameters: Object.entries(config.parameters ?? {}).map(([key, value]) => ({ key, value })), + stages: Object.entries(config.stages ?? {}).reduce( + (acc, [stage, parameters]) => ({ + ...acc, + [stage]: Object.entries(parameters).map(([key, value]) => ({ key, value })), + }), + {}, + ), }; asyncLocalStorage.enterWith(context); diff --git a/src/common/iacHelper.ts b/src/common/iacHelper.ts index 675995c..294f865 100644 --- a/src/common/iacHelper.ts +++ b/src/common/iacHelper.ts @@ -4,6 +4,7 @@ import * as ros from '@alicloud/ros-cdk-core'; import { Context } from '../types'; import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment'; import crypto from 'node:crypto'; +import { get } from 'lodash'; export const resolveCode = (location: string): string => { const filePath = path.resolve(process.cwd(), location); @@ -34,21 +35,17 @@ export const getFileSource = ( return { source, objectKey }; }; -const evalCtx = (value: string, ctx: Context): string => { - const containsStage = value.match(/\$\{ctx.\w+}/); - - return containsStage ? value.replace(/\$\{ctx.stage}/g, ctx.stage) : value; -}; - -export const replaceReference = (value: T, ctx: Context): T => { - if (typeof value === 'string') { - const matchVar = value.match(/^\$\{vars\.(\w+)}$/); - const containsVar = value.match(/\$\{vars\.(\w+)}/); - const matchMap = value.match(/^\$\{stages\.(\w+)}$/); - const containsMap = value.match(/\$\{stages\.(\w+)}/); - const matchFn = value.match(/^\$\{functions\.(\w+(\.\w+)?)}$/); - if (value.match(/\$\{ctx.\w+}/)) { - return evalCtx(value, ctx) as T; +export const calcRefs = (rawValue: T, ctx: Context): T => { + if (typeof rawValue === 'string') { + const containsStage = rawValue.match(/\$\{ctx.\w+}/); + const matchVar = rawValue.match(/^\$\{vars\.(\w+)}$/); + const containsVar = rawValue.match(/\$\{vars\.(\w+)}/); + const matchMap = rawValue.match(/^\$\{stages\.(\w+)}$/); + const containsMap = rawValue.match(/\$\{stages\.(\w+)}/); + const matchFn = rawValue.match(/^\$\{functions\.(\w+(\.\w+)?)}$/); + let value = rawValue as string; + if (containsStage) { + value = value.replace(/\$\{ctx.stage}/g, ctx.stage); } if (matchVar?.length) { @@ -57,33 +54,56 @@ export const replaceReference = (value: T, ctx: Context): T => { if (matchMap?.length) { return ros.Fn.findInMap('stages', ctx.stage, matchMap[1]) as T; } - if (matchFn?.length) { return ros.Fn.getAtt(matchFn[1], 'FunctionName') as T; } - if (containsMap?.length && containsVar?.length) { - return ros.Fn.sub( - value.replace(/\$\{stages\.(\w+)}/g, '${$1}').replace(/\$\{vars\.(\w+)}/g, '${$1}'), - ) as T; - } - if (containsVar?.length) { - return ros.Fn.sub(value.replace(/\$\{vars\.(\w+)}/g, '${$1}')) as T; - } - if (containsMap?.length) { - return ros.Fn.sub(value.replace(/\$\{stages\.(\w+)}/g, '${$1}')) as T; + + if (containsMap?.length || containsVar?.length) { + value = ros.Fn.sub( + rawValue.replace(/\$\{stages\.(\w+)}/g, '${$1}').replace(/\$\{vars\.(\w+)}/g, '${$1}'), + ); } - return value; + + return value as T; } - if (Array.isArray(value)) { - return value.map((item) => replaceReference(item, ctx)) as T; + if (Array.isArray(rawValue)) { + return rawValue.map((item) => calcRefs(item, ctx)) as T; } - if (typeof value === 'object' && value !== null) { + if (typeof rawValue === 'object' && rawValue !== null) { return Object.fromEntries( - Object.entries(value).map(([key, val]) => [key, replaceReference(val, ctx)]), + Object.entries(rawValue).map(([key, val]) => [key, calcRefs(val, ctx)]), ) as T; } - return value; + return rawValue; +}; + +const getParam = (key: string, records?: Array<{ key: string; value: string }>) => { + return records?.find((param) => param.key === key)?.value as string; +}; + +export const realValue = (rawValue: string, ctx: Context): T => { + const containsStage = rawValue.match(/\$\{ctx.stage}/); + const containsVar = rawValue.match(/\$\{vars.\w+}/); + const containsMap = rawValue.match(/\$\{stages\.(\w+)}/); + + let value = rawValue; + + if (containsStage?.length) { + value = rawValue.replace(/\$\{ctx.stage}/g, ctx.stage); + } + + if (containsVar?.length) { + value = value.replace(/\$\{vars\.(\w+)}/g, (_, key) => getParam(key, ctx.parameters)); + } + + if (containsMap?.length) { + value = value.replace(/\$\{stages\.(\w+)}/g, (_, key) => + getParam(key, get(ctx.stages, `${ctx.stage}`)), + ); + } + + return value as T; }; diff --git a/src/stack/rosStack/bucket.ts b/src/stack/rosStack/bucket.ts index aeb33b8..99451ea 100644 --- a/src/stack/rosStack/bucket.ts +++ b/src/stack/rosStack/bucket.ts @@ -5,7 +5,7 @@ import { encodeBase64ForRosId, getAssets, OSS_DEPLOYMENT_TIMEOUT, - replaceReference, + calcRefs, splitDomain, } from '../../common'; import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment'; @@ -58,27 +58,27 @@ export const resolveBuckets = ( } buckets.forEach((bucket) => { - const ossBucket = new oss.Bucket(scope, replaceReference(bucket.key, context), { - bucketName: replaceReference(bucket.name, context), + const ossBucket = new oss.Bucket(scope, calcRefs(bucket.key, context), { + bucketName: calcRefs(bucket.name, context), accessControl: aclMap.get( - replaceReference(bucket.security?.acl, context) ?? ('' as BucketAccessEnum), + calcRefs(bucket.security?.acl, context) ?? ('' as BucketAccessEnum), ), websiteConfigurationV2: bucket.website ? { indexDocument: { type: '0', - suffix: replaceReference(bucket.website.index, context), + suffix: calcRefs(bucket.website.index, context), supportSubDir: 'true', }, errorDocument: { - httpStatus: `${replaceReference(bucket.website.error_code, context)}`, - key: replaceReference(bucket.website.error_page, context), + httpStatus: `${calcRefs(bucket.website.error_code, context)}`, + key: calcRefs(bucket.website.error_page, context), }, } : undefined, }); if (bucket.website?.code) { - const filePath = path.resolve(process.cwd(), replaceReference(bucket.website.code, context)); + const filePath = path.resolve(process.cwd(), calcRefs(bucket.website.code, context)); new ossDeployment.BucketDeployment( scope, `si_auto_${bucket.key}_bucket_code_deployment`, @@ -100,7 +100,7 @@ export const resolveBuckets = ( `${bucket.key}_custom_domain_${encodeBase64ForRosId(bucket.website.domain)}`, { bucketName: ossBucket.attrName, - domainName: replaceReference(bucket.website.domain, context), + domainName: calcRefs(bucket.website.domain, context), }, ); diff --git a/src/stack/rosStack/database.ts b/src/stack/rosStack/database.ts index 2950eee..227ddd8 100644 --- a/src/stack/rosStack/database.ts +++ b/src/stack/rosStack/database.ts @@ -1,6 +1,6 @@ import * as ros from '@alicloud/ros-cdk-core'; import * as rds from '@alicloud/ros-cdk-rds'; -import { replaceReference } from '../../common'; +import { calcRefs } from '../../common'; import { Context, DatabaseDomain, DatabaseEnum, DatabaseVersionEnum } from '../../types'; import { isEmpty } from 'lodash'; import * as esServerless from '@alicloud/ros-cdk-elasticsearchserverless'; @@ -208,14 +208,14 @@ export const resolveDatabases = ( if ([DatabaseEnum.ELASTICSEARCH_SERVERLESS].includes(db.type)) { new esServerless.App( scope, - replaceReference(db.key, context), + calcRefs(db.key, context), { - appName: replaceReference(db.name, context), + appName: calcRefs(db.name, context), appVersion: version, authentication: { basicAuth: [ { - password: replaceReference(db.security.basicAuth.password, context), + password: calcRefs(db.security.basicAuth.password, context), }, ], }, @@ -249,7 +249,7 @@ export const resolveDatabases = ( ) { new rds.DBInstance( scope, - replaceReference(db.key, context), + calcRefs(db.key, context), { engine: engine as string, /** @@ -259,7 +259,7 @@ export const resolveDatabases = ( * PostgreSQL:14.0、15.0、16.0 - PGSQL_HA_14, PGSQL_14 PGSQL_HA_15, PGSQL_15, PGSQL_HA_16,PGSQL_16 */ engineVersion: version as string, - dbInstanceStorage: replaceReference(db.storage.min, context), + dbInstanceStorage: calcRefs(db.storage.min, context), /** Serverless 实例 * serverless_basic:Serverless 基础系列。(仅适用 MySQL 和 PostgreSQL) * serverless_standard:Serverless 高可用系列。(仅适用 MySQL 和 PostgreSQL) @@ -297,11 +297,11 @@ export const resolveDatabases = ( */ serverlessConfig: { // @TODO db.cu.min should get parameter value when it refer to a parameter - minCapacity: replaceReference( + minCapacity: calcRefs( db.cu.min === 0 ? quota!.minCapacity : db.cu.min + quota!.minCapacity, context, ), - maxCapacity: replaceReference( + maxCapacity: calcRefs( db.cu.max + quota!.minCapacity <= quota!.maxCapacity ? db.cu.max + quota!.minCapacity : quota!.maxCapacity, @@ -310,11 +310,11 @@ export const resolveDatabases = ( autoPause: db.cu.min === 0, switchForce: false, }, - masterUsername: replaceReference(db.security.basicAuth.username, context), - masterUserPassword: replaceReference(db.security.basicAuth.password, context), + masterUsername: calcRefs(db.security.basicAuth.username, context), + masterUserPassword: calcRefs(db.security.basicAuth.password, context), masterUserType: 'Super', multiAz: quota!.ha, - securityIpList: replaceReference(db.network.ingressRules.join(','), context), + securityIpList: calcRefs(db.network.ingressRules.join(','), context), connectionStringType: db.network.type === 'PRIVATE' ? 'Inner' : 'Public', dbInstanceNetType: db.network.type === 'PRIVATE' ? 'Intranet' : 'Internet', }, diff --git a/src/stack/rosStack/event.ts b/src/stack/rosStack/event.ts index 9f80ec1..0f05b0a 100644 --- a/src/stack/rosStack/event.ts +++ b/src/stack/rosStack/event.ts @@ -1,7 +1,7 @@ import * as ros from '@alicloud/ros-cdk-core'; import { Context, EventDomain, EventTypes, ServerlessIac } from '../../types'; import * as ram from '@alicloud/ros-cdk-ram'; -import { encodeBase64ForRosId, replaceReference, splitDomain } from '../../common'; +import { encodeBase64ForRosId, calcRefs, splitDomain } from '../../common'; import * as agw from '@alicloud/ros-cdk-apigateway'; import { isEmpty } from 'lodash'; import * as dns from '@alicloud/ros-cdk-dns'; @@ -22,10 +22,10 @@ export const resolveEvents = ( apiGateway.forEach((event) => { const gatewayAccessRole = new ram.RosRole( scope, - replaceReference(`${event.key}_role`, context), + calcRefs(`${event.key}_role`, context), { - roleName: replaceReference(`${service}-${event.name}-agw-access-role`, context), - description: replaceReference(`${service} role`, context), + roleName: calcRefs(`${service}-${event.name}-agw-access-role`, context), + description: calcRefs(`${service} role`, context), assumeRolePolicyDocument: { version: '1', statement: [ @@ -40,7 +40,7 @@ export const resolveEvents = ( }, policies: [ { - policyName: replaceReference(`${service}-${event.name}-policy`, context), + policyName: calcRefs(`${service}-${event.name}-policy`, context), policyDocument: { version: '1', statement: [ @@ -60,10 +60,10 @@ export const resolveEvents = ( const apiGatewayGroup = new agw.RosGroup( scope, - replaceReference(`${service}_apigroup`, context), + calcRefs(`${service}_apigroup`, context), { - groupName: replaceReference(`${service}_apigroup`, context), - tags: replaceReference(tags, context), + groupName: calcRefs(`${service}_apigroup`, context), + tags: calcRefs(tags, context), passthroughHeaders: 'host', }, true, @@ -96,37 +96,35 @@ export const resolveEvents = ( } event.triggers.forEach((trigger) => { - const key = encodeBase64ForRosId( - replaceReference(`${trigger.method}_${trigger.path}`, context), - ); + const key = encodeBase64ForRosId(calcRefs(`${trigger.method}_${trigger.path}`, context)); const api = new agw.RosApi( scope, `${event.key}_api_${key}`, { - apiName: replaceReference(`${event.name}_api_${key}`, context), + apiName: calcRefs(`${event.name}_api_${key}`, context), groupId: apiGatewayGroup.attrGroupId, visibility: 'PRIVATE', authType: 'ANONYMOUS', requestConfig: { requestProtocol: 'HTTP', - requestHttpMethod: replaceReference(trigger.method, context), - requestPath: replaceReference(trigger.path, context), + requestHttpMethod: calcRefs(trigger.method, context), + requestPath: calcRefs(trigger.path, context), requestMode: 'PASSTHROUGH', }, serviceConfig: { serviceProtocol: 'FunctionCompute', functionComputeConfig: { fcRegionId: context.region, - functionName: replaceReference(trigger.backend, context), + functionName: calcRefs(trigger.backend, context), roleArn: gatewayAccessRole.attrArn, fcVersion: '3.0', - method: replaceReference(trigger.method, context), + method: calcRefs(trigger.method, context), }, }, resultSample: 'ServerlessInsight resultSample', resultType: 'PASSTHROUGH', - tags: replaceReference(tags, context), + tags: calcRefs(tags, context), }, true, ); diff --git a/src/stack/rosStack/function.ts b/src/stack/rosStack/function.ts index 9cca03f..d7fc4a9 100644 --- a/src/stack/rosStack/function.ts +++ b/src/stack/rosStack/function.ts @@ -11,7 +11,7 @@ import { getFileSource, OSS_DEPLOYMENT_TIMEOUT, readCodeSize, - replaceReference, + calcRefs, resolveCode, } from '../../common'; import * as fc from '@alicloud/ros-cdk-fc3'; @@ -92,7 +92,7 @@ export const resolveFunctions = ( const slsService = new sls.Project( scope, `${service}_sls`, - { name: `${service}-sls`, tags: replaceReference(tags, context) }, + { name: `${service}-sls`, tags: calcRefs(tags, context) }, true, ); @@ -128,7 +128,7 @@ export const resolveFunctions = ( const fileSources = functions ?.filter(({ code }) => code?.path && readCodeSize(code.path) > CODE_ZIP_SIZE_LIMIT) .map(({ code, name }) => { - const fcName = replaceReference(name, context); + const fcName = calcRefs(name, context); return { fcName, ...getFileSource(fcName, code!.path) }; }); @@ -185,15 +185,14 @@ export const resolveFunctions = ( if (storeInBucket) { code = { ossBucketName: destinationBucketName, - ossObjectName: fileSources?.find( - ({ fcName }) => fcName === replaceReference(fnc.name, context), - )?.objectKey, + ossObjectName: fileSources?.find(({ fcName }) => fcName === calcRefs(fnc.name, context)) + ?.objectKey, }; } runtimeConfig = { code, - handler: replaceReference(fnc.code!.handler, context), - runtime: replaceReference(fnc.code!.runtime, context), + handler: calcRefs(fnc.code!.handler, context), + runtime: calcRefs(fnc.code!.runtime, context), }; } @@ -204,8 +203,8 @@ export const resolveFunctions = ( `${fnc.key}_security_group`, { securityGroupName: fnc.network.security_group.name, - vpcId: replaceReference(fnc.network.vpc_id, context), - tags: replaceReference(tags, context), + vpcId: calcRefs(fnc.network.vpc_id, context), + tags: calcRefs(tags, context), securityGroupIngress: transformSecurityRules( fnc.network.security_group.ingress, 'INGRESS', @@ -216,8 +215,8 @@ export const resolveFunctions = ( ); vpcConfig = { - vpcId: replaceReference(fnc.network.vpc_id, context), - vSwitchIds: replaceReference(fnc.network.subnet_ids, context), + vpcId: calcRefs(fnc.network.vpc_id, context), + vSwitchIds: calcRefs(fnc.network.subnet_ids, context), securityGroupId: securityGroup.attrSecurityGroupId, }; } @@ -269,10 +268,7 @@ export const resolveFunctions = ( fileSystemType, storageType, protocolType: 'NFS', - tags: [ - ...(replaceReference(tags, context) ?? []), - { key: 'function-name', value: fnc.name }, - ], + tags: [...(calcRefs(tags, context) ?? []), { key: 'function-name', value: fnc.name }], }, true, ); @@ -297,12 +293,12 @@ export const resolveFunctions = ( scope, fnc.key, { - functionName: replaceReference(fnc.name, context), - memorySize: replaceReference(fnc.memory, context), + functionName: calcRefs(fnc.name, context), + memorySize: calcRefs(fnc.memory, context), diskSize: fnc.storage?.disk, gpuConfig: transformGpuConfig(fnc.gpu), - timeout: replaceReference(fnc.timeout, context), - environmentVariables: replaceReference(fnc.environment, context), + timeout: calcRefs(fnc.timeout, context), + environmentVariables: calcRefs(fnc.environment, context), logConfig, vpcConfig, ...runtimeConfig, diff --git a/src/stack/rosStack/index.ts b/src/stack/rosStack/index.ts index db13c14..538b104 100644 --- a/src/stack/rosStack/index.ts +++ b/src/stack/rosStack/index.ts @@ -1,6 +1,6 @@ import * as ros from '@alicloud/ros-cdk-core'; import { Context, ServerlessIac } from '../../types'; -import { replaceReference } from '../../common'; +import { calcRefs } from '../../common'; import { resolveTags } from './tag'; import { resolveFunctions } from './function'; import { resolveStages } from './stage'; @@ -15,12 +15,12 @@ export class RosStack extends ros.Stack { private readonly service: string; constructor(scope: ros.Construct, iac: ServerlessIac, context: Context) { - super(scope, replaceReference(iac.service, context), { + super(scope, calcRefs(iac.service, context), { stackName: context.stackName, tags: resolveTags(iac.tags, context), }); - this.service = replaceReference(iac.service, context); + this.service = calcRefs(iac.service, context); new ros.RosInfo(this, ros.RosInfo.description, `${this.service} stack`); // Define Parameters diff --git a/src/stack/rosStack/stage.ts b/src/stack/rosStack/stage.ts index 76478a3..f2513b5 100644 --- a/src/stack/rosStack/stage.ts +++ b/src/stack/rosStack/stage.ts @@ -1,5 +1,5 @@ import * as ros from '@alicloud/ros-cdk-core'; -import { replaceReference } from '../../common'; +import { calcRefs } from '../../common'; import { Context, Stages } from '../../types'; import { isEmpty } from 'lodash'; @@ -11,5 +11,5 @@ export const resolveStages = ( if (isEmpty(stages)) { return undefined; } - new ros.RosMapping(scope, 'stages', { mapping: replaceReference(stages, context) }); + new ros.RosMapping(scope, 'stages', { mapping: calcRefs(stages, context) }); }; diff --git a/src/stack/rosStack/tag.ts b/src/stack/rosStack/tag.ts index 156fde7..5df2776 100644 --- a/src/stack/rosStack/tag.ts +++ b/src/stack/rosStack/tag.ts @@ -1,9 +1,9 @@ import { Context, ServerlessIac } from '../../types'; -import { replaceReference } from '../../common'; +import { calcRefs } from '../../common'; export const resolveTags = (tags: ServerlessIac['tags'], context: Context) => { return tags?.reduce((acc: { [key: string]: string }, tag) => { - acc[tag.key] = replaceReference(tag.value, context); + acc[tag.key] = calcRefs(tag.value, context); return acc; }, {}); }; diff --git a/src/types/domains/context.ts b/src/types/domains/context.ts index a0ac892..21739c1 100644 --- a/src/types/domains/context.ts +++ b/src/types/domains/context.ts @@ -10,6 +10,9 @@ export type Context = { securityToken?: string; iacLocation: string; parameters?: Array<{ key: string; value: string }>; + stages?: { + [stage: string]: Array<{ key: string; value: string }>; + }; tags?: Array<{ key: string; value: string }>; }; diff --git a/tests/common/iacHelper.test.ts b/tests/common/iacHelper.test.ts index b4ff650..7cd4370 100644 --- a/tests/common/iacHelper.test.ts +++ b/tests/common/iacHelper.test.ts @@ -1,6 +1,6 @@ import * as ros from '@alicloud/ros-cdk-core'; import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment'; -import { getFileSource, replaceReference } from '../../src/common'; +import { getFileSource, calcRefs, realValue } from '../../src/common'; import fs from 'node:fs'; import { context } from '../fixtures/contextFixture'; @@ -13,6 +13,7 @@ describe('Unit test for iacHelper', () => { beforeEach(() => { jest.restoreAllMocks(); }); + describe('Unit test for getFileSource', () => { it('should return the correct ossDeployment source', () => { getFileSource(fcName, location); @@ -32,47 +33,96 @@ describe('Unit test for iacHelper', () => { }); }); - describe('Unit test for replaceReference', () => { + describe('Unit test for calcRefers', () => { it('should return the value if it does not contain any reference', () => { const value = 'testValue'; - expect(replaceReference(value, context)).toEqual(value); + expect(calcRefs(value, context)).toEqual(value); expect(ros.Fn.ref).not.toHaveBeenCalled(); }); it('should return the value if it contains a stage reference', () => { const value = 'testValue-${ctx.stage}'; - expect(replaceReference(value, context)).toEqual('testValue-test'); + expect(calcRefs(value, context)).toEqual('testValue-test'); expect(ros.Fn.ref).not.toHaveBeenCalled(); }); it('should return the value if it match a vars reference', () => { - replaceReference('${vars.testVar}', context); + calcRefs('${vars.testVar}', context); expect(ros.Fn.ref).toHaveBeenCalledWith('testVar'); }); it('should return the value if it match a stages reference', () => { - replaceReference('${stages.testStage}', context); + calcRefs('${stages.testStage}', context); expect(ros.Fn.findInMap).toHaveBeenCalledWith('stages', 'test', 'testStage'); }); it('should return the value if it contains a stages reference', () => { - replaceReference('abcd-${stages.testVar}-efg', context); + calcRefs('abcd-${stages.testVar}-efg', context); expect(ros.Fn.sub).toHaveBeenCalledWith('abcd-${testVar}-efg'); }); it('should return the value if it contains both stages reference and vars references', () => { - replaceReference('abcd-${stages.testVar}-efg-${vars.newTestVar}-hij', context); + calcRefs('abcd-${stages.testVar}-efg-${vars.newTestVar}-hij', context); expect(ros.Fn.sub).toHaveBeenCalledWith('abcd-${testVar}-efg-${newTestVar}-hij'); }); it('should return the value if it contains a functions reference', () => { - replaceReference('${functions.testFunction}', context); + calcRefs('${functions.testFunction}', context); expect(ros.Fn.getAtt).toHaveBeenCalledWith('testFunction', 'FunctionName'); }); }); + + describe('Unit test for calcValue', () => { + it('should return the value when raw string match vars', () => { + const value = realValue('${vars.handler}', context); + + expect(value).toEqual('index.handler'); + }); + + it('should return the value when raw string contains vars', () => { + const value = realValue('test-${vars.testVar}-value', context); + + expect(value).toEqual('test-testVarValue-value'); + }); + + it('should return the value when raw string match stage', () => { + const value = realValue('${ctx.stage}', context); + + expect(value).toEqual('test'); + }); + + it('should return the value when raw string contains stage', () => { + const value = realValue('test${ctx.stage}-value', context); + + expect(value).toEqual('testtest-value'); + }); + + it('should return the value when raw string match stages', () => { + const value = realValue('${stages.testStage}', context); + + expect(value).toEqual('testStageValue'); + }); + + it('should return the value when raw string contains stages', () => { + const value = realValue('test-${stages.testStage}-value', context); + + expect(value).toEqual('test-testStageValue-value'); + }); + + it('should return the value when raw string contains stage, vars and stages', () => { + const value = realValue( + `wearedongyiIASJ#test$\{stages.testStage}SUPERE$\{ctx.stage}ROR-AHID_#YOUD$\{vars.testVar}-value`, + context, + ); + + expect(value).toEqual( + 'wearedongyiIASJ#testtestStageValueSUPEREtestROR-AHID_#YOUDtestVarValue-value', + ); + }); + }); }); diff --git a/tests/fixtures/contextFixture.ts b/tests/fixtures/contextFixture.ts index 978e09f..5139694 100644 --- a/tests/fixtures/contextFixture.ts +++ b/tests/fixtures/contextFixture.ts @@ -12,6 +12,14 @@ export const context: Context = { parameters: [ { key: 'testVar', value: 'testVarValue' }, { key: 'newTestVar', value: 'newTestVarValue' }, + { key: 'handler', value: 'index.handler' }, ], + stages: { + default: [{ key: 'testVar', value: 'testVarValue' }], + test: [ + { key: 'testVar', value: 'testVarValue' }, + { key: 'testStage', value: 'testStageValue' }, + ], + }, tags: [{ key: 'owner', value: 'geekfun' }], };