diff --git a/lib/configuration/variables/sources/resolve-external-plugin-sources.js b/lib/configuration/variables/sources/resolve-external-plugin-sources.js new file mode 100644 index 00000000000..39bbc818ac9 --- /dev/null +++ b/lib/configuration/variables/sources/resolve-external-plugin-sources.js @@ -0,0 +1,66 @@ +'use strict'; + +const ensurePlainObject = require('type/plain-object/ensure'); +const ensurePlainFunction = require('type/plain-function/ensure'); +const ServerlessError = require('../../../serverless-error'); +const logDeprecation = require('../../../utils/logDeprecation'); + +module.exports = (configuration, resolverConfiguration, externalPlugins) => { + for (const externalPlugin of externalPlugins) { + const pluginName = externalPlugin.constructor.name; + if (externalPlugin.configurationVariablesSources != null) { + ensurePlainObject(externalPlugin.configurationVariablesSources, { + errorMessage: + 'Invalid "configurationVariablesSources" ' + + `configuration on "${pluginName}", expected object, got: %v"`, + Error: ServerlessError, + errorCode: 'INVALID_VARIABLE_SOURCES_CONFIGURATION', + }); + + for (const [sourceName, sourceConfig] of Object.entries( + externalPlugin.configurationVariablesSources + )) { + if (resolverConfiguration.sources[sourceName]) { + throw new ServerlessError( + `Cannot add "${sourceName}" configuration variable source ` + + `(through "${pluginName}" plugin) as resolution rules ` + + 'for this source name are already configured', + 'DUPLICATE_VARIABLE_SOURCE_CONFIGURATION' + ); + } + ensurePlainFunction( + ensurePlainObject(sourceConfig, { + errorMessage: + `Invalid "configurationVariablesSources.${sourceName}" ` + + `configuration on "${pluginName}", expected object, got: %v"`, + Error: ServerlessError, + errorCode: 'INVALID_VARIABLE_SOURCE_CONFIGURATION', + }).resolve, + { + errorMessage: + `Invalid "configurationVariablesSources.${sourceName}.resolve" ` + + `value on "${pluginName}", expected function, got: %v"`, + Error: ServerlessError, + errorCode: 'INVALID_VARIABLE_SOURCE_RESOLVER_CONFIGURATION', + } + ); + + resolverConfiguration.sources[sourceName] = sourceConfig; + resolverConfiguration.fulfilledSources.add(sourceName); + } + } else if ( + externalPlugin.variableResolvers && + configuration.variablesResolutionMode < 20210326 + ) { + logDeprecation( + 'NEW_VARIABLES_RESOLVER', + `Plugin "${pluginName}" attempts to extend old variables resolver. ` + + 'Ensure to rely on latest version of a plugin and if this warning is ' + + 'still displayed please report the problem at plugin issue tracker' + + 'Starting with next major release, ' + + 'old variables resolver will not be supported.\n', + { serviceConfig: configuration } + ); + } + } +}; diff --git a/scripts/serverless.js b/scripts/serverless.js index a11350f57a6..18840d9b0c4 100755 --- a/scripts/serverless.js +++ b/scripts/serverless.js @@ -554,67 +554,13 @@ const processSpanPromise = (async () => { } } - const ensurePlainFunction = require('type/plain-function/ensure'); - const ensurePlainObject = require('type/plain-object/ensure'); - // Register variable source resolvers provided by external plugins - for (const externalPlugin of serverless.pluginManager.externalPlugins) { - const pluginName = externalPlugin.constructor.name; - if (externalPlugin.configurationVariablesSources != null) { - ensurePlainObject(externalPlugin.configurationVariablesSources, { - errorMessage: - 'Invalid "configurationVariablesSources" ' + - `configuration on "${pluginName}", expected object, got: %v"`, - Error: ServerlessError, - errorCode: 'INVALID_VARIABLE_SOURCES_CONFIGURATION', - }); - - for (const [sourceName, sourceConfig] of Object.entries( - externalPlugin.configurationVariablesSources - )) { - if (resolverConfiguration.sources[sourceName]) { - throw new ServerlessError( - `Cannot add "${sourceName}" configuration variable source ` + - `(through "${pluginName}" plugin) as resolution rules ` + - 'for this source name are already configured', - 'DUPLICATE_VARIABLE_SOURCE_CONFIGURATION' - ); - } - ensurePlainFunction( - ensurePlainObject(sourceConfig, { - errorMessage: - `Invalid "configurationVariablesSources.${sourceName}" ` + - `configuration on "${pluginName}", expected object, got: %v"`, - Error: ServerlessError, - errorCode: 'INVALID_VARIABLE_SOURCE_CONFIGURATION', - }).resolve, - { - errorMessage: - `Invalid "configurationVariablesSources.${sourceName}.resolve" ` + - `value on "${pluginName}", expected function, got: %v"`, - Error: ServerlessError, - errorCode: 'INVALID_VARIABLE_SOURCE_RESOLVER_CONFIGURATION', - } - ); - - resolverConfiguration.sources[sourceName] = sourceConfig; - resolverConfiguration.fulfilledSources.add(sourceName); - } - } else if ( - externalPlugin.variableResolvers && - configuration.variablesResolutionMode < 20210326 - ) { - logDeprecation( - 'NEW_VARIABLES_RESOLVER', - `Plugin "${pluginName}" attempts to extend old variables resolver. ` + - 'Ensure to rely on latest version of a plugin and if this warning is ' + - 'still displayed please report the problem at plugin issue tracker' + - 'Starting with next major release, ' + - 'old variables resolver will not be supported.\n', - { serviceConfig: configuration } - ); - } - } + const resolverExternalPluginSources = require('../lib/configuration/variables/sources/resolve-external-plugin-sources'); + resolverExternalPluginSources( + configuration, + resolverConfiguration, + serverless.pluginManager.externalPlugins + ); // Having all source resolvers configured, resolve variables await resolveVariables(resolverConfiguration); diff --git a/test/unit/lib/configuration/variables/sources/resolve-external-plugin-resources.test.js b/test/unit/lib/configuration/variables/sources/resolve-external-plugin-resources.test.js new file mode 100644 index 00000000000..52978b18342 --- /dev/null +++ b/test/unit/lib/configuration/variables/sources/resolve-external-plugin-resources.test.js @@ -0,0 +1,90 @@ +'use strict'; + +const { expect } = require('chai'); + +const ServerlessError = require('../../../../../../lib/serverless-error'); +const resolveExternalPluginSources = require('../../../../../../lib/configuration/variables/sources/resolve-external-plugin-sources'); + +describe('test/unit/lib/configuration/variables/sources/resolve-external-plugin-resources.test.js', () => { + it('should resolve external plugin sources', () => { + const sources = {}; + const fulfilledSources = new Set(); + const externalPlugins = [ + { + configurationVariablesSources: { + ext1: { resolve: () => {} }, + ext2: { resolve: () => {} }, + }, + }, + { + configurationVariablesSources: { + ext3: { resolve: () => {} }, + }, + }, + ]; + resolveExternalPluginSources({}, { sources, fulfilledSources }, new Set(externalPlugins)); + expect(sources).to.deep.equal({ + ...externalPlugins[0].configurationVariablesSources, + ...externalPlugins[1].configurationVariablesSources, + }); + expect(fulfilledSources).to.deep.equal(new Set(Object.keys(sources))); + }); + + it('should reject meaningfully invalid sources configuration', () => { + expect(() => + resolveExternalPluginSources( + {}, + { sources: {}, fulfilledSources: new Set() }, + new Set([ + { + configurationVariablesSources: 'foo', + }, + ]) + ) + ) + .to.throw(ServerlessError) + .with.property('code', 'INVALID_VARIABLE_SOURCES_CONFIGURATION'); + + expect(() => + resolveExternalPluginSources( + {}, + { sources: { existing: { resolve: () => {} } }, fulfilledSources: new Set(['existing']) }, + new Set([ + { + configurationVariablesSources: { existing: { resolve: () => {} } }, + }, + ]) + ) + ) + .to.throw(ServerlessError) + .with.property('code', 'DUPLICATE_VARIABLE_SOURCE_CONFIGURATION'); + + expect(() => + resolveExternalPluginSources( + {}, + { sources: {}, fulfilledSources: new Set() }, + new Set([ + { + configurationVariablesSources: { source: 'foo' }, + }, + ]) + ) + ) + .to.throw(ServerlessError) + .with.property('code', 'INVALID_VARIABLE_SOURCE_CONFIGURATION'); + + expect(() => + resolveExternalPluginSources( + {}, + { sources: {}, fulfilledSources: new Set() }, + new Set([ + { + configurationVariablesSources: { source: { resolve: 'foo ' } }, + }, + ]) + ) + ) + .to.throw(ServerlessError) + .with.property('code', 'INVALID_VARIABLE_SOURCE_RESOLVER_CONFIGURATION'); + }); +});