EndemolShineGroup/serverless-dynamodb-autoscaler

View on GitHub
src/DynamoDBAutoscaling.ts

Summary

Maintainability
A
0 mins
Test Coverage
import util from 'util';

import { AutoScalingPlans, Refs } from 'cloudform';
import { isArray, merge } from 'lodash';

import getScalingInstruction from './getScalingInstruction';
import { CapacityConfig } from './types/plugin';
import * as OutputMessages from './utils/output-messages';
import validateInput from './utils/validation';

export default class DynamoDBAutoscaling {
  public hooks: {
    [key: string]: () => Promise<any>;
  };

  private serverless: any;

  constructor(serverless: any) {
    this.serverless = serverless;

    this.hooks = {
      'package:compileEvents': this.execute,
    };
  }

  private execute = (): Promise<any> => {
    return Promise.resolve()
      .then(() => {
        return validateInput(this.serverless);
      })
      .then(() => {
        return this.serverless.cli.log(util.format(OutputMessages.CLI_START));
      })
      .then(this.process)
      .then(() => {
        return this.serverless.cli.log(util.format(OutputMessages.CLI_DONE));
      })
      .catch((err: Error) => {
        this.serverless.cli.log(
          util.format(OutputMessages.CLI_SKIP, err.message),
        );
        throw new Error('aws-dynamodb-autoscaling failed with exit code 1');
      });
  };

  private process = () => {
    let scalingInstructions: any[] = [];
    const DependsOn: string[] = [];

    this.serverless.service.custom.capacities.map((config: CapacityConfig) => {
      if (config.index) {
        config.index = isArray(config.index) ? config.index : [config.index];
      }

      scalingInstructions = [
        ...scalingInstructions,
        ...this.getScalingInstructions(config),
      ];

      DependsOn.push(config.table);
    });

    const AutoScalingPlan = new AutoScalingPlans.ScalingPlan({
      ApplicationSource: new AutoScalingPlans.ScalingPlan.ApplicationSource({
        CloudFormationStackARN: Refs.StackId,
      }),
      ScalingInstructions: scalingInstructions,
    });

    AutoScalingPlan.DependsOn = DependsOn;

    merge(
      this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
      { AutoScalingPlan },
    );
  };

  private getScalingInstructions = (config: CapacityConfig) => {
    let scalingInstructions: any[] = [];

    if (config.index) {
      // Create scaling instruction for each index
      (config.index as string[]).forEach((i: string) => {
        scalingInstructions = [
          ...scalingInstructions,
          ...this.generateInstructions(config),
        ];
      });

      if (config.indexOnly) {
        return scalingInstructions;
      }
    }

    // Create scaling instruction for the table
    scalingInstructions = [
      ...scalingInstructions,
      ...this.generateInstructions({
        table: config.table,
        write: config.write,
        read: config.read,
      }),
    ];

    return scalingInstructions;
  };

  private generateInstructions = (config: CapacityConfig) => {
    const scalingInstructions: any[] = [];

    this.serverless.cli.log(
      util.format(
        OutputMessages.CLI_RESOURCE,
        config.table,
        config.index ? '/index/' + config.index : '',
      ),
    );

    // Only add Auto Scaling for read capacity if configuration set is available
    if (!!config.read) {
      scalingInstructions.push(getScalingInstruction(config, true));
    }

    // Only add Auto Scaling for write capacity if configuration set is available
    if (!!config.write) {
      scalingInstructions.push(getScalingInstruction(config, false));
    }

    return scalingInstructions;
  };
}