diff --git a/src/aws/resource.ts b/src/aws/resource.ts index a5dd74e..76d3547 100644 --- a/src/aws/resource.ts +++ b/src/aws/resource.ts @@ -13,4 +13,8 @@ export default class Resource { return this } + + public toJSON():any { + throw new Error('override this') + } } diff --git a/src/aws/target.ts b/src/aws/target.ts index ac48cf0..8a519a1 100644 --- a/src/aws/target.ts +++ b/src/aws/target.ts @@ -18,7 +18,7 @@ export default class Target extends Resource { } const nameTarget = this.name.target(this.read) - const nameRole = this.name.role() + const nameRole = this.options.role || this.name.role() const nameDimension = this.name.dimension(this.read) const DependsOn = [ this.options.table, nameRole ].concat(this.dependencies) diff --git a/src/plugin.ts b/src/plugin.ts index fb6320a..22402b2 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -5,6 +5,8 @@ import * as util from 'util' import Policy from './aws/policy' import Role from './aws/role' import Target from './aws/target' +import Resource from './aws/resource' +import { coerceToArray, getFirstKey } from './utils' const text = { CLI_DONE: 'Added DynamoDB Auto Scaling to CloudFormation!', @@ -12,7 +14,7 @@ const text = { CLI_SKIP: 'Skipping DynamoDB Auto Scaling: %s!', CLI_START: 'Configure DynamoDB Auto Scaling …', INVALID_CONFIGURATION: 'Invalid serverless configuration', - NO_AUTOSCALING_CONFIG: 'Not Auto Scaling configuration found', + NO_AUTOSCALING_CONFIG: 'No Auto Scaling configuration found', ONLY_AWS_SUPPORT: 'Only supported for AWS provicer' } @@ -23,6 +25,7 @@ interface Defaults { class AWSDBAutoScaling { public hooks: {} + private options: AutoScalingOptions /** * Constructur @@ -65,7 +68,7 @@ class AWSDBAutoScaling { assert(this.serverless.service.provider.name === 'aws', text.ONLY_AWS_SUPPORT) assert(this.serverless.service.custom, text.NO_AUTOSCALING_CONFIG) - assert(this.serverless.service.custom.capacities, text.NO_AUTOSCALING_CONFIG) + assert(this.options.capacities, text.NO_AUTOSCALING_CONFIG) } /** @@ -89,7 +92,7 @@ class AWSDBAutoScaling { /** * Create CloudFormation resources for table (and optional index) */ - private resources(table: string, index: string, config: Capacity): any[] { + private resources(table: string, index: string, config: Capacity): Resource[] { const data = this.defaults(config) const options: Options = { @@ -97,7 +100,8 @@ class AWSDBAutoScaling { region: this.getRegion(), service: this.getServiceName(), stage: this.getStage(), - table + table, + role: this.options.role } // Start processing configuration @@ -106,9 +110,10 @@ class AWSDBAutoScaling { ) // Add role to manage Auto Scaling policies - const resources: any[] = [ - new Role(options) - ] + const resources: any[] = [] + if (!this.options.role) { + resources.push(new Role(options)) + } // Only add Auto Scaling for read capacity if configuration set is available if (!!config.read) { @@ -137,10 +142,10 @@ class AWSDBAutoScaling { * Generate CloudFormation resources for DynamoDB table and indexes */ private generate(table: string, config: Capacity) { - let resources: any[] = [] - let lastRessources: any[] = [] + let resources: Resource[] = [] + let lastRessources: string[] = [] - const indexes = this.normalize(config.index) + const indexes = coerceToArray(config.index) if (!config.indexOnly) { indexes.unshift('') // Horrible solution } @@ -148,41 +153,29 @@ class AWSDBAutoScaling { indexes.forEach( (index: string) => { const current = this.resources(table, index, config).map( - (resource: any) => resource.setDependencies(lastRessources).toJSON() + resource => resource.setDependencies(lastRessources).toJSON() ) resources = resources.concat(current) - lastRessources = current.map((item: any) => Object.keys(item).pop()) + lastRessources = current.map(getFirstKey) } ) return resources } - /** - * Check if parameter is defined and return as array if only a string is provided - */ - private normalize(data: string|string[]): string[] { - if (data && data.constructor !== Array) { - return [ data as string ] - } - - return (data as string[] || []).slice(0) - } - /** * Process the provided configuration */ private process() { - this.serverless.service.custom.capacities.filter( + const { capacities } = this.options + const allResources = this.serverless.service.provider.compiledCloudFormationTemplate.Resources + capacities.filter( (config: Capacity) => !!config.read || !!config.write ).forEach( - (config: Capacity) => this.normalize(config.table).forEach( + (config: Capacity) => coerceToArray(config.table).forEach( (table: string) => this.generate(table, config).forEach( - (resource: string) => _.merge( - this.serverless.service.provider.compiledCloudFormationTemplate.Resources, - resource - ) + (resources: any) => _.merge(allResources, resources) ) ) ) @@ -190,7 +183,10 @@ class AWSDBAutoScaling { private beforeDeployResources(): Promise { return Promise.resolve().then( - () => this.validate() + () => { + this.options = this.serverless.service.custom['dynamodb-autoscaling'] + this.validate() + } ).then( () => this.serverless.cli.log(util.format(text.CLI_START)) ).then( diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..66e7e41 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,5 @@ +export const coerceToArray = (data: T|T[]): T[] => { + return ([] as T[]).concat(data || []) +} + +export const getFirstKey = (obj: any):string => Object.keys(obj).pop() as string diff --git a/test/plugin.spec.ts b/test/plugin.spec.ts index 68bc905..e919813 100644 --- a/test/plugin.spec.ts +++ b/test/plugin.spec.ts @@ -1,17 +1,5 @@ import * as Plugin from '../src/plugin' -describe('Normalize', () => { - it('converts everything to an array', () => { - const test = new Plugin({ service: { provider: { stage: 'foo' } } }) - - expect(test.normalize('test')).toEqual(['test']) - expect(test.normalize(['test'])).toEqual(['test']) - expect(test.normalize(['test', 'foo'])).toEqual(['test', 'foo']) - expect(test.normalize([])).toEqual([]) - expect(test.normalize()).toEqual([]) - }) -}) - describe('Defaults', () => { it('creates object with defaults', () => { const config = { diff --git a/test/utils.spec.ts b/test/utils.spec.ts new file mode 100644 index 0000000..063c4cb --- /dev/null +++ b/test/utils.spec.ts @@ -0,0 +1,11 @@ +import { coerceToArray } from '../src/utils' + +describe('Normalize', () => { + it('converts everything to an array', () => { + expect(coerceToArray('test')).toEqual(['test']) + expect(coerceToArray(['test'])).toEqual(['test']) + expect(coerceToArray(['test', 'foo'])).toEqual(['test', 'foo']) + expect(coerceToArray([])).toEqual([]) + expect(coerceToArray()).toEqual([]) + }) +}) diff --git a/vendor/main.d.ts b/vendor/main.d.ts index 1a718cf..61263b8 100644 --- a/vendor/main.d.ts +++ b/vendor/main.d.ts @@ -4,6 +4,7 @@ declare interface Capacity { indexOnly?: boolean write?: CapacityConfiguration read?: CapacityConfiguration + generateRoles?: boolean } declare interface CapacityConfiguration { @@ -18,6 +19,12 @@ declare interface Options { service: string stage: string table: string + role?: string +} + +declare interface AutoScalingOptions { + role?: string // external role logical id + capacities: Capacity[] } /** @@ -26,7 +33,7 @@ declare interface Options { declare namespace Serverless { namespace Service { interface Custom { - capacities: Capacity[] + ['dynamodb-autoscaling']: AutoScalingOptions } } -} \ No newline at end of file +}