packages/utilities/mdaa-naming/lib/index.ts (83 lines of code) (raw):
/*!
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
import { Node } from 'constructs';
/**
* Basic config for a MDAA naming implementation.
* Standard config properties are passed directly, while
* additional config values can be pulled from CDK context using
* the provided cdkNode.tryGetContext()
* */
export interface MdaaResourceNamingConfig {
/** A CDK node on which tryGetContext()
* can be used to obtain additional naming config context
* for use in non-default implementations */
readonly cdkNode: Node;
/** 'org' from the MDAA config */
readonly org: string;
/** 'env' from the MDAA config */
readonly env: string;
/** 'domain' from the MDAA config */
readonly domain: string;
/** 'module_name' from the MDAA config */
readonly moduleName: string;
}
/**
* Interface specification for a MDAA naming implementation.
* Can be implemented in any way, but should ensure that naming
* semantics support deployment of multiple domains/envs/modules
* within the same account, otherwise resource naming collisions may occur.
*/
export interface IMdaaResourceNaming {
readonly props: MdaaResourceNamingConfig;
/**
* Returns this naming object but with a new moduleName
*
* @param moduleName The new module name
*/
withModuleName(moduleName: string): IMdaaResourceNaming;
/**
* Should produce unique but stable resource names.
*
* @param resourceNameSuffix Optional naming suffix to be added to the generated resource name.
* Useful when multiple resources of the same type are created within the same stack.
*
* @param maxLength Should be used to truncate the generated resource names to a specified length.
* The result should still be unique and stable.
*/
resourceName(resourceNameSuffix?: string, maxLength?: number): string;
/**
* Generates an SSM param name.
*
* @param path The generated name will be suffixed by the path component.
* The base path should provide uniqueness across stacks, and the path component should provide uniqueness within stacks.
*
* @param includeModuleName Optionally include the module name in the base path of the SSM param name.
* Usefull when scoping params at the domain/env level instead of the module level.
*
* @param lowerCase Optionally force the generated param name to lower case
*/
ssmPath(path: string, includeModuleName?: boolean, lowerCase?: boolean): string;
/**
* Generates a compliance stack name
* @param stackName The base stack name. Should be used to implement a globally unique stack name.
*/
stackName(stackName?: string): string;
/**
* Generates an CFN Stack Export name.
*
* @param path The generated name will be suffixed by the path component.
* The base path should provide uniqueness across stacks, and the path component should provide uniqueness within stacks.
*
* @param includeModuleName Optionally include the module name in the base path of the export name.
* Usefull when scoping params at the domain/env level instead of the module level.
*
* @param lowerCase Optionally force the generated export name to lower case
*/
exportName(path: string, includeModuleName?: boolean, lowerCase?: boolean): string;
}
/**
* A default MDAA Naming implementation
*/
export class MdaaDefaultResourceNaming implements IMdaaResourceNaming {
public readonly props: MdaaResourceNamingConfig;
constructor(props: MdaaResourceNamingConfig) {
this.props = props;
}
/**
* Returns this naming object but with a new moduleName
*
* @param moduleName The new module name
*/
public withModuleName(moduleName: string): IMdaaResourceNaming {
const newProps: MdaaResourceNamingConfig = {
cdkNode: this.props.cdkNode,
org: this.props.org,
env: this.props.env,
domain: this.props.domain,
moduleName: moduleName,
};
return new MdaaDefaultResourceNaming(newProps);
}
/**
* Generates a resource name in the format of <org>-<env>-<domain>-<module_name>
*/
public resourceName(resourceNameSuffix?: string, maxLength?: number): string {
let name = `${this.props.org}-${this.props.env}-${this.props.domain}-${this.props.moduleName}`;
if (resourceNameSuffix) {
name = `${name}-${this.lowerCase(resourceNameSuffix)}`;
}
if (maxLength && name.length >= maxLength) {
const hashCodeHex = MdaaDefaultResourceNaming.hashCodeHex(name);
return `${name.substring(0, maxLength - (hashCodeHex.length + 1))}-${hashCodeHex}`;
}
return name;
}
/**
* Generates a ssm param name in the format of /<org>/<env>/<domain>/<module_name>
*/
public ssmPath(path: string, includeModuleName = true, lowerCase = true): string {
let name = `/${this.props.org}/${this.props.domain}`;
if (includeModuleName) {
name = `${name}/${this.props.moduleName}`;
}
return lowerCase ? this.lowerCase(`${name}/${path}`) : `${name}/${path}`;
}
/**
* Generates a export name in the format of <org>:<env>:<domain>:<module_name>
*/
public exportName(path: string, includeModuleName = true, lowerCase = true): string {
let name = `${this.props.org}:${this.props.domain}`;
if (includeModuleName) {
name = `${name}:${this.props.moduleName}`;
}
return lowerCase ? this.lowerCase(`${name}:${path}`) : `${name}:${path}`;
}
/**
* Generates a stack name in the format of <org>-<env>-<domain>-<module_name>.
* Sanitizes non-alpha numeric characters and replaces underscores with '-'
*/
public stackName(stackNameSuffix?: string): string {
const org = MdaaDefaultResourceNaming.sanitize(this.props.org);
const env = MdaaDefaultResourceNaming.sanitize(this.props.env);
const domain = MdaaDefaultResourceNaming.sanitize(this.props.domain);
const module_name = MdaaDefaultResourceNaming.sanitize(this.props.moduleName);
const suffix = stackNameSuffix ? MdaaDefaultResourceNaming.sanitize(stackNameSuffix) : undefined;
let stackName = `${org}-${env}-${domain}-${module_name}`;
if (suffix) {
stackName = `${stackName}-${this.lowerCase(suffix)}`;
}
return stackName;
}
protected static sanitize(component: string): string | undefined {
if (!component) {
return component;
}
return component.replace(/^\W+$/g, '').replace(/_/g, '-');
}
protected static hashCodeHex(s: string) {
let h = 0;
for (let i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
return h.toString(16);
}
protected lowerCase(input: string): string {
return input.toLowerCase().replace(/\{token\[token\.(\d+)\]\}/, '{Token[TOKEN.$1]}');
}
}