Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions lib/deploy/events/apiGateway/compilationPipeline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

/**
* Runs an ordered list of compiler steps sequentially on a given context.
* Each step is a method name that exists on the context object.
* Steps are reorderable and conditionally includable without modifying callers.
*/
class CompilationPipeline {
constructor(steps) {
this.steps = steps;
}

run(context) {
return this.steps.reduce(
(promise, step) => promise.then(() => context[step]()),
Promise.resolve(),
);
}
}

module.exports = CompilationPipeline;
69 changes: 69 additions & 0 deletions lib/deploy/events/apiGateway/compilationPipeline.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict';

const expect = require('chai').expect;
const sinon = require('sinon');
const CompilationPipeline = require('./compilationPipeline');

describe('CompilationPipeline', () => {
it('should call all steps in order', async () => {
const callOrder = [];
const context = {
stepA: sinon.stub().callsFake(() => {
callOrder.push('A');
return Promise.resolve();
}),
stepB: sinon.stub().callsFake(() => {
callOrder.push('B');
return Promise.resolve();
}),
stepC: sinon.stub().callsFake(() => {
callOrder.push('C');
return Promise.resolve();
}),
};

const pipeline = new CompilationPipeline(['stepA', 'stepB', 'stepC']);
await pipeline.run(context);

expect(callOrder).to.deep.equal(['A', 'B', 'C']);
});

it('should call each step on the given context', async () => {
const context = {
step: sinon.stub().returns(Promise.resolve()),
};

const pipeline = new CompilationPipeline(['step']);
await pipeline.run(context);

expect(context.step.callCount).to.equal(1);
});

it('should wait for an async step to resolve before calling the next', async () => {
let firstResolved = false;
const context = {
stepA: sinon.stub().returns(
new Promise((resolve) => {
setTimeout(() => {
firstResolved = true;
resolve();
}, 10);
}),
),
stepB: sinon.stub().callsFake(() => {
expect(firstResolved).to.equal(true);
return Promise.resolve();
}),
};

const pipeline = new CompilationPipeline(['stepA', 'stepB']);
await pipeline.run(context);

expect(context.stepB.callCount).to.equal(1);
});

it('should resolve immediately with an empty steps array', async () => {
const pipeline = new CompilationPipeline([]);
await pipeline.run({});
});
});
30 changes: 17 additions & 13 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const httpLambdaPermissions = require('./deploy/events/apiGateway/lambdaPermissi
const httpDeployment = require('./deploy/events/apiGateway/deployment');
const httpRestApi = require('./deploy/events/apiGateway/restApi');
const httpInfo = require('./deploy/events/apiGateway/endpointInfo');
const CompilationPipeline = require('./deploy/events/apiGateway/compilationPipeline');
const compileScheduledEvents = require('./deploy/events/schedule/compileScheduledEvents');
const compileCloudWatchEventEvents = require('./deploy/events/cloudWatchEvent/compileCloudWatchEventEvents');
const invoke = require('./invoke/invoke');
Expand All @@ -31,6 +32,21 @@ const naming = require('./naming');

const logger = require('./utils/logger');

const apiGatewayPipeline = new CompilationPipeline([
'compileRestApi',
'compileResources',
'compileMethods',
'compileRequestValidators',
'compileAuthorizers',
'compileHttpLambdaPermissions',
'compileCors',
'compileHttpIamRole',
'compileDeployment',
'compileApiKeys',
'compileUsagePlan',
'compileUsagePlanKeys',
]);

class ServerlessStepFunctions {
constructor(serverless, options, v3Api) {
this.serverless = serverless;
Expand Down Expand Up @@ -135,19 +151,7 @@ class ServerlessStepFunctions {
return BbPromise.resolve();
}

return BbPromise.bind(this)
.then(this.compileRestApi)
.then(this.compileResources)
.then(this.compileMethods)
.then(this.compileRequestValidators)
.then(this.compileAuthorizers)
.then(this.compileHttpLambdaPermissions)
.then(this.compileCors)
.then(this.compileHttpIamRole)
.then(this.compileDeployment)
.then(this.compileApiKeys)
.then(this.compileUsagePlan)
.then(this.compileUsagePlanKeys);
return apiGatewayPipeline.run(this);
}).then(() => this.compileCloudWatchEventEvents()),
'after:deploy:deploy': () => BbPromise.bind(this)
.then(this.getEndpointInfo)
Expand Down
Loading