diff --git a/lib/commands/smapi/cli-customization-processor.js b/lib/commands/smapi/cli-customization-processor.js index 4d4c32d2..12429ff8 100644 --- a/lib/commands/smapi/cli-customization-processor.js +++ b/lib/commands/smapi/cli-customization-processor.js @@ -90,6 +90,10 @@ class CliCustomizationProcessor { return definitions.get(schema); } + _shouldParseAsJson(property) { + return property.type === 'object' || (property.type === 'array' && '$ref' in property.items); + } + _appendSecondLevelProperty(customizationMetadata, parentName, rootName, secondLevelDefinition, required, definitions) { let customizedParameter; const parentDescription = secondLevelDefinition.description; @@ -106,7 +110,7 @@ class CliCustomizationProcessor { enumOptions = schema.enum; description = schema.description || description; } - const json = property.type === 'object'; + const json = this._shouldParseAsJson(property); const bodyPath = `${parentName}${BODY_PATH_DELIMITER}${key}`; customizedParameter = { name: `${parentName} ${key}`, description, rootName, required, bodyPath, enum: enumOptions, json }; this._addCustomizedParameter(customizationMetadata, customizedParameter); @@ -130,7 +134,7 @@ class CliCustomizationProcessor { this._appendSecondLevelProperty(customizationMetadata, key, rootName, secondLevelDefinition, isRequired, definitions); } else { const { description } = property; - const json = property.type === 'object'; + const json = this._shouldParseAsJson(property); // required inherited from parent param const customizedParameter = { name: key, description, rootName, required: param.required, bodyPath: key, json }; this._addCustomizedParameter(customizationMetadata, customizedParameter); diff --git a/lib/commands/smapi/customizations/hook-functions/map-testers-emails.js b/lib/commands/smapi/customizations/hook-functions/map-testers-emails.js index 70037ef1..e80de7fa 100644 --- a/lib/commands/smapi/customizations/hook-functions/map-testers-emails.js +++ b/lib/commands/smapi/customizations/hook-functions/map-testers-emails.js @@ -4,7 +4,7 @@ */ const mapTestersEmails = (requestParameters) => { const { testersEmails } = requestParameters; - requestParameters.TestersRequest = { + requestParameters.testersRequest = { testers: testersEmails.map(email => ({ emailId: email })) }; diff --git a/lib/commands/smapi/customizations/smapi-hooks.js b/lib/commands/smapi/customizations/smapi-hooks.js index 7f86b36a..34b65fb8 100644 --- a/lib/commands/smapi/customizations/smapi-hooks.js +++ b/lib/commands/smapi/customizations/smapi-hooks.js @@ -1,3 +1,4 @@ +const { ModelIntrospector } = require('ask-smapi-sdk'); const appendVendorId = require('@src/commands/smapi/customizations/hook-functions/append-vendor-id'); const mapTestersEmails = require('@src/commands/smapi/customizations/hook-functions/map-testers-emails'); @@ -7,19 +8,31 @@ const events = { const operationHooks = new Map(); -operationHooks.set('createSkillForVendorV1', new Map([[events.BEFORE_SEND, appendVendorId]])); -operationHooks.set('createIspForVendorV1', new Map([[events.BEFORE_SEND, appendVendorId]])); -operationHooks.set('getIspListForVendorV1', new Map([[events.BEFORE_SEND, appendVendorId]])); -operationHooks.set('getAlexaHostedSkillUserPermissionsV1', new Map([[events.BEFORE_SEND, appendVendorId]])); -operationHooks.set('listInteractionModelCatalogsV1', new Map([[events.BEFORE_SEND, appendVendorId]])); -operationHooks.set('listInteractionModelSlotTypesV1', new Map([[events.BEFORE_SEND, appendVendorId]])); -operationHooks.set('listSkillsForVendorV1', new Map([[events.BEFORE_SEND, appendVendorId]])); -operationHooks.set('addTestersToBetaTestV1', new Map([[events.BEFORE_SEND, mapTestersEmails]])); -operationHooks.set('removeTestersFromBetaTestV1', new Map([[events.BEFORE_SEND, mapTestersEmails]])); -operationHooks.set('sendReminderToTestersV1', new Map([[events.BEFORE_SEND, mapTestersEmails]])); -operationHooks.set('requestFeedbackFromTestersV1', new Map([[events.BEFORE_SEND, mapTestersEmails]])); +const _autoRegisterHooks = () => { + const modelIntrospector = new ModelIntrospector(); + const operations = modelIntrospector.getOperations(); + operations.forEach(operation => { + operation.params.forEach(param => { + switch (param.name) { + case 'vendorId': + operationHooks.set(operation.apiOperationName, new Map([[events.BEFORE_SEND, appendVendorId]])); + break; + case 'TestersRequest': + operationHooks.set(operation.apiOperationName, new Map([[events.BEFORE_SEND, mapTestersEmails]])); + break; + default: + } + }); + }); +}; +const _manualRegisterHooks = () => { + operationHooks.set('createIspForVendorV1', new Map([[events.BEFORE_SEND, appendVendorId]])); + operationHooks.set('createSkillForVendorV1', new Map([[events.BEFORE_SEND, appendVendorId]])); +}; +_autoRegisterHooks(); +_manualRegisterHooks(); class SmapiHooks { /** * Returns object with available hook events. diff --git a/lib/commands/smapi/smapi-command-handler.js b/lib/commands/smapi/smapi-command-handler.js index 9ece17b6..dcabd698 100644 --- a/lib/commands/smapi/smapi-command-handler.js +++ b/lib/commands/smapi/smapi-command-handler.js @@ -9,6 +9,7 @@ const jsonView = require('@src/view/json-view'); const Messenger = require('@src/view/messenger'); const profileHelper = require('@src/utils/profile-helper'); const unflatten = require('@src/utils/unflatten'); +const { getParamNames } = require('@src/utils/string-utils'); const { BODY_PATH_DELIMITER, ARRAY_SPLIT_DELIMITER } = require('./cli-customization-processor'); const SmapiHooks = require('./customizations/smapi-hooks'); @@ -18,12 +19,13 @@ const configFilePath = path.join(os.homedir(), CONSTANTS.FILE_PATH.ASK.HIDDEN_FO const _mapToArgs = (params, paramsObject) => { const res = []; params.forEach(param => { - const { name } = param; - if (paramsObject[name]) { - res.push(paramsObject[name]); - } else { - res.push(null); - } + let value = null; + Object.keys(paramsObject).forEach(k => { + if (k.toLowerCase() === param.toLowerCase()) { + value = paramsObject[k]; + } + }); + res.push(value); }); return res; }; @@ -62,7 +64,7 @@ const _mapToParams = (optionsValues, flatParamsMap, commanderToApiCustomizationM * for api properties. * @param {Object} cmdObj Commander object with passed values. */ -const smapiCommandHandler = (swaggerApiOperationName, swaggerParams, flatParamsMap, commanderToApiCustomizationMap, inputCmdObj) => { +const smapiCommandHandler = (swaggerApiOperationName, flatParamsMap, commanderToApiCustomizationMap, inputCmdObj) => { new AppConfig(configFilePath); const authorizationController = new AuthorizationController({ auth_client_type: 'LWA' @@ -90,7 +92,8 @@ const smapiCommandHandler = (swaggerApiOperationName, swaggerParams, flatParamsM Messenger.getInstance().info('Payload:'); Messenger.getInstance().info(jsonView.toString(paramsObject)); } - const args = _mapToArgs(swaggerParams, paramsObject); + const params = getParamNames(client[swaggerApiOperationName]); + const args = _mapToArgs(params, paramsObject); return client[swaggerApiOperationName](...args); }; diff --git a/lib/commands/smapi/smapi-commander.js b/lib/commands/smapi/smapi-commander.js index 3bad2805..a8aa59f4 100644 --- a/lib/commands/smapi/smapi-commander.js +++ b/lib/commands/smapi/smapi-commander.js @@ -80,7 +80,6 @@ const makeSmapiCommander = () => { }); commanderInstance.action((inputCmdObj) => smapiCommandHandler( apiOperationName, - operation.params, flatParamsMap, commanderToApiCustomizationMap, inputCmdObj diff --git a/lib/utils/string-utils.js b/lib/utils/string-utils.js index 10cc4bb2..1b6dc8c5 100644 --- a/lib/utils/string-utils.js +++ b/lib/utils/string-utils.js @@ -2,6 +2,7 @@ const R = require('ramda'); const CONSTANTS = require('@src/utils/constants'); module.exports = { + getParamNames, camelCase, kebabCase, isNonEmptyString, @@ -12,6 +13,15 @@ module.exports = { validateSyntax }; +function getParamNames(func) { + const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; + const ARGUMENT_NAMES = /([^\s,]+)/g; + const fnStr = func.toString().replace(STRIP_COMMENTS, ''); + let result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); + if (result === null) result = []; + return result; +} + function camelCase(str) { return str .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => (index === 0 ? word.toLowerCase() : word.toUpperCase())) diff --git a/test/unit/commands/smapi/cli-customization-processor-test.js b/test/unit/commands/smapi/cli-customization-processor-test.js index a4319b31..9a4e391d 100644 --- a/test/unit/commands/smapi/cli-customization-processor-test.js +++ b/test/unit/commands/smapi/cli-customization-processor-test.js @@ -35,6 +35,7 @@ describe('Smapi test - CliCustomizationProcessor class', () => { const nestedPropertyName = 'nestedProperty'; const nestedEnumPropertyName = 'nestedEnumProperty'; const bodyProperty = {}; + const bodyPropertyWithArray = { type: 'array', items: { $ref: 'test' } }; const bodyPropertyWithDescription = { description: 'property description' }; const nestedProperty = { $ref: 'SomeObjectType' }; const nestedEnumProperty = { $ref: 'SomeEnumTypeWithDescription' }; @@ -57,7 +58,11 @@ describe('Smapi test - CliCustomizationProcessor class', () => { ['SomeEnumTypeWithDescription', { enum: enumValues, description: enumDescription }], ['SomeTypeWithDescription', { enum: enumValues, description: refObjectDescription }], ['SomeObjectType', { description: parentDescription, - properties: { propertyOne: bodyProperty, propertyTwo: bodyPropertyWithDescription } }], + properties: { propertyOne: bodyProperty, + propertyTwo: bodyPropertyWithDescription, + propertyThree: { $ref: 'SomeEnumType' }, + propertyFour: { $ref: 'SomeEnumTypeWithDescription' }, + propertyFive: bodyPropertyWithArray } }], ['SomeNestedType', { required: [nestedPropertyName], properties: { nestedProperty } }], ['SomeTooManyPropertiesType', { properties: { ...objectWithTooManyProperties } }], diff --git a/test/unit/commands/smapi/hook-functions/map-testers-emails-test.js b/test/unit/commands/smapi/hook-functions/map-testers-emails-test.js index 68a25a35..6fc8f8bd 100644 --- a/test/unit/commands/smapi/hook-functions/map-testers-emails-test.js +++ b/test/unit/commands/smapi/hook-functions/map-testers-emails-test.js @@ -3,13 +3,13 @@ const mapTestersEmails = require('@src/commands/smapi/customizations/hook-functi describe('Smapi hook functions test - mapTestersEmails tests', () => { - it('| should map testers emails to TestersRequest parameter', () => { + it('| should map testers emails to testersRequest parameter', () => { const requestParameters = { testersEmails: ['tester1', 'tester2'] }; mapTestersEmails(requestParameters); const expected = { testers: [{ emailId: 'tester1' }, { emailId: 'tester2' }] }; - expect(requestParameters.TestersRequest).eql(expected); + expect(requestParameters.testersRequest).eql(expected); }); }); diff --git a/test/unit/commands/smapi/smapi-command-handler-test.js b/test/unit/commands/smapi/smapi-command-handler-test.js index 5bb50551..8df51bab 100644 --- a/test/unit/commands/smapi/smapi-command-handler-test.js +++ b/test/unit/commands/smapi/smapi-command-handler-test.js @@ -56,14 +56,16 @@ describe('Smapi test - smapiCommandHandler function', () => { } }); clientStub[apiOperationName] = sinon.stub().resolves(); + clientStub[apiOperationName].toString = () => 'function (someJson, skillId, ' + + 'someNonPopulatedProperty, someArray, simulationsApiRequest) { return 0};'; sinon.stub(StandardSmapiClientBuilder.prototype, 'client').returns(clientStub); }); it('| should send smapi command with correct parameter mapping', async () => { - await smapiCommandHandler(apiOperationName, params, flatParamsMap, commanderToApiCustomizationMap, cmdObj); + await smapiCommandHandler(apiOperationName, flatParamsMap, commanderToApiCustomizationMap, cmdObj); - const expectedParams = [skillId, null, { input: { content: inputContent }, device: { locale: deviceLocale } }, jsonValue, arrayValue]; + const expectedParams = [jsonValue, skillId, null, arrayValue, { input: { content: inputContent }, device: { locale: deviceLocale } }]; const calledParams = clientStub[apiOperationName].args[0]; expect(calledParams).eql(expectedParams); @@ -73,7 +75,7 @@ describe('Smapi test - smapiCommandHandler function', () => { const stubHook = sinon.stub(); sinon.stub(SmapiHooks, 'getFunction').returns(stubHook); - await smapiCommandHandler(apiOperationName, params, flatParamsMap, commanderToApiCustomizationMap, cmdObj); + await smapiCommandHandler(apiOperationName, flatParamsMap, commanderToApiCustomizationMap, cmdObj); expect(stubHook.calledOnce).eql(true); }); @@ -87,7 +89,7 @@ describe('Smapi test - smapiCommandHandler function', () => { const messengerStub = sinon.stub(Messenger, 'displayMessage'); - await smapiCommandHandler(apiOperationName, params, flatParamsMap, commanderToApiCustomizationMap, cmdObjDebug); + await smapiCommandHandler(apiOperationName, flatParamsMap, commanderToApiCustomizationMap, cmdObjDebug); expect(messengerStub.args[0]).eql(['INFO', 'Operation: someApiOperation']); expect(messengerStub.args[1]).eql(['INFO', 'Payload:']);