Skip to content

Commit

Permalink
refactor: Reorganize hooks resolution
Browse files Browse the repository at this point in the history
Its to have access to meta that describes hook origin when processing the hook
  • Loading branch information
medikoo committed Sep 20, 2021
1 parent 8b4498c commit 76006ec
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 95 deletions.
75 changes: 39 additions & 36 deletions lib/classes/PluginManager.js
Expand Up @@ -536,29 +536,29 @@ class PluginManager {
);
}

getEvents(command) {
return command.lifecycleEvents
? command.lifecycleEvents.reduce(
(acc, event) =>
acc.concat([
`before:${command.key}:${event}`,
`${command.key}:${event}`,
`after:${command.key}:${event}`,
]),
[]
)
: [];
}

getPlugins() {
return this.plugins;
}

getHooks(lifecycleEventNames) {
if (!Array.isArray(lifecycleEventNames)) lifecycleEventNames = [lifecycleEventNames];
return _.flatten(
lifecycleEventNames.map((lifecycleEventName) => this.hooks[lifecycleEventName] || [])
);
getLifecycleEventsData(command) {
const lifecycleEventsData = [];
let hooksLength = 0;
for (const lifecycleEventSubName of command.lifecycleEvents || []) {
const lifecycleEventName = `${command.key}:${lifecycleEventSubName}`;
const hooksData = {
before: this.hooks[`before:${lifecycleEventName}`] || [],
at: this.hooks[lifecycleEventName] || [],
after: this.hooks[`after:${lifecycleEventName}`] || [],
};
hooksLength += hooksData.before.length + hooksData.at.length + hooksData.after.length;
lifecycleEventsData.push({
command,
lifecycleEventSubName,
lifecycleEventName,
hooksData,
});
}
return { lifecycleEventsData, hooksLength };
}

async invoke(commandsArray, allowEntryPoints) {
Expand All @@ -573,36 +573,39 @@ class PluginManager {
this.assignDefaultOptions(command);
this.validateOptions(command);

const events = this.getEvents(command);
const hooks = this.getHooks(events);
const { lifecycleEventsData, hooksLength } = this.getLifecycleEventsData(command);

log
.get('lifecycle:command:invoke')
.debug(
`Invoke ${commandsArray.join(':')}${
!hooks.length ? ' (noop due to no registered hooks)' : ''
!hooksLength ? ' (noop due to no registered hooks)' : ''
}`
);
if (process.env.SLS_DEBUG) {
legacy.log(`Invoke ${commandsArray.join(':')}`);
if (hooks.length === 0) {
if (hooksLength === 0) {
const warningMessage = 'Warning: The command you entered did not catch on any hooks';
legacy.log(warningMessage);
}
}

for (const hook of hooks) {
try {
await hook.hook();
} catch (error) {
if (error instanceof TerminateHookChain) {
if (process.env.SLS_DEBUG) {
this.serverless.cli.log(`Terminate ${commandsArray.join(':')}`);
}
return;
try {
for (const {
hooksData: { before, at, after },
} of lifecycleEventsData) {
for (const { hook } of before) await hook();
for (const { hook } of at) await hook();
for (const { hook } of after) await hook();
}
} catch (error) {
if (error instanceof TerminateHookChain) {
if (process.env.SLS_DEBUG) {
this.serverless.cli.log(`Terminate ${commandsArray.join(':')}`);
}
throw error;
return;
}
throw error;
}
}

Expand All @@ -628,7 +631,7 @@ class PluginManager {
this.commandRunStartTime = Date.now();
if (resolveCliInput().commands[0] !== 'plugin') {
// first initialize hooks
for (const { hook } of this.getHooks(['initialize'])) await hook();
for (const { hook } of this.hooks.initialize || []) await hook();
}

let deferredBackendNotificationRequest;
Expand All @@ -655,7 +658,7 @@ class PluginManager {
await this.invoke(commandsArray);
} catch (commandException) {
try {
for (const { hook } of this.getHooks(['error'])) await hook(commandException);
for (const { hook } of this.hooks.error || []) await hook(commandException);
} catch (errorHookException) {
const errorHookExceptionMeta = tokenizeException(errorHookException);
this.serverless.cli.log(
Expand All @@ -670,7 +673,7 @@ class PluginManager {
}

try {
for (const { hook } of this.getHooks(['finalize'])) await hook();
for (const { hook } of this.hooks.finalize || []) await hook();
} catch (finalizeHookException) {
await deferredBackendNotificationRequest;
throw finalizeHookException;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -82,7 +82,7 @@
"devDependencies": {
"@commitlint/cli": "^12.1.4",
"@serverless/eslint-config": "^3.0.0",
"@serverless/test": "^8.3.1",
"@serverless/test": "^8.4.0",
"adm-zip": "^0.5.6",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
Expand Down
58 changes: 0 additions & 58 deletions test/unit/lib/classes/PluginManager.test.js
Expand Up @@ -1168,64 +1168,6 @@ describe('PluginManager', () => {
});
});

describe('#getEvents()', () => {
beforeEach(() => {
pluginManager.addPlugin(SynchronousPluginMock);
});

it('should get all the matching events for a root level command in the correct order', () => {
const command = pluginManager.getCommand(['deploy']);
const events = pluginManager.getEvents(command);

expect(events[0]).to.equal('before:deploy:resources');
expect(events[1]).to.equal('deploy:resources');
expect(events[2]).to.equal('after:deploy:resources');
expect(events[3]).to.equal('before:deploy:functions');
expect(events[4]).to.equal('deploy:functions');
expect(events[5]).to.equal('after:deploy:functions');
});

it('should get all the matching events for a nested level command in the correct order', () => {
const command = pluginManager.getCommand(['deploy', 'onpremises']);
const events = pluginManager.getEvents(command);

expect(events[0]).to.equal('before:deploy:onpremises:resources');
expect(events[1]).to.equal('deploy:onpremises:resources');
expect(events[2]).to.equal('after:deploy:onpremises:resources');
expect(events[3]).to.equal('before:deploy:onpremises:functions');
expect(events[4]).to.equal('deploy:onpremises:functions');
expect(events[5]).to.equal('after:deploy:onpremises:functions');
});
});

describe('#getHooks()', () => {
beforeEach(() => {
pluginManager.addPlugin(SynchronousPluginMock);
});

it('should get hooks for an event with some registered', () => {
expect(pluginManager.getHooks(['deploy:functions']))
.to.be.an('Array')
.with.length(1);
});

it('should have the plugin name and function on the hook', () => {
const hooks = pluginManager.getHooks(['deploy:functions']);
expect(hooks[0].pluginName).to.equal('SynchronousPluginMock');
expect(hooks[0].hook).to.be.a('Function');
});

it('should not get hooks for an event that does not have any', () => {
expect(pluginManager.getHooks(['deploy:resources']))
.to.be.an('Array')
.with.length(0);
});

it('should accept a single event in place of an array', () => {
expect(pluginManager.getHooks('deploy:functions')).to.be.an('Array').with.length(1);
});
});

describe('#getPlugins()', () => {
beforeEach(() => {
mockRequire('ServicePluginMock1', ServicePluginMock1);
Expand Down

0 comments on commit 76006ec

Please sign in to comment.