diff --git a/packages/core/integration-test/integration-test.js b/packages/core/integration-test/integration-test.js index 13971ab49..feadb8e0b 100644 --- a/packages/core/integration-test/integration-test.js +++ b/packages/core/integration-test/integration-test.js @@ -190,7 +190,7 @@ const doTest = (runner) => { }); }); - it('should handle array of [appRawOverrideHash, appRawExtension]', () => { + it('should handle array of [appRawOverrideHash, appRawExtension] and override inputFields', () => { const definition = { creates: { foo: { @@ -198,7 +198,13 @@ const doTest = (runner) => { noun: 'Foo', operation: { perform: { source: 'return [{id: 12345}]' }, - inputFields: [{ key: 'name', type: 'string' }], + inputFields: [ + { key: 'name', type: 'string' }, + { key: 'testing', type: 'string' }, + ], + sample: { + id: 123, + }, }, }, }, @@ -209,11 +215,13 @@ const doTest = (runner) => { const definitionExtension = { creates: { foo: { - noun: 'Foobar', + key: 'foo', + noun: 'Foo', operation: { + perform: { source: 'return [{id: 12345}]' }, inputFields: [{ key: 'message', type: 'string' }], sample: { - id: 678, + name: 'sample', }, }, }, @@ -234,10 +242,105 @@ const doTest = (runner) => { }; return runner(event).then((response) => { - response.results.should.eql([ - { key: 'name', type: 'string' }, - { key: 'message', type: 'string' }, - ]); + response.results.should.eql([{ key: 'message', type: 'string' }]); + }); + }); + + it('should handle array of [appRawOverrideHash, appRawExtension] and override specific inputField', () => { + const definition = { + creates: { + foo: { + key: 'foo', + noun: 'Foo', + operation: { + perform: { source: 'return [{id: 12345}]' }, + inputFields: [{ key: 'message', type: 'integer' }], + }, + }, + }, + }; + + mocky.mockRpcCall(definition); + + const definitionExtension = { + creates: { + foo: { + noun: 'Foobar', + operation: { + inputFields: [{ key: 'message', type: 'string' }], + }, + }, + }, + }; + + const definitionHash = crypto + .createHash('md5') + .update(JSON.stringify(definition)) + .digest('hex'); + + const event = { + command: 'execute', + method: 'creates.foo.operation.inputFields', + appRawOverride: [definitionHash, definitionExtension], + rpc_base: 'https://mock.zapier.com/platform/rpc/cli', + token: 'fake', + }; + + return runner(event).then((response) => { + response.results.should.eql([{ key: 'message', type: 'string' }]); + }); + }); + + it('should handle array of [appRawOverrideHash, appRawExtension] and overrides with an operation create key', () => { + const definition = { + creates: { + operation: { + key: 'operation', + operation: { + perform: { source: 'return [{id: 12345}]' }, + inputFields: [ + { key: 'name', type: 'string' }, + { key: 'testing', type: 'string' }, + ], + sample: { + id: 123, + }, + }, + }, + }, + }; + + mocky.mockRpcCall(definition); + + const definitionExtension = { + creates: { + operation: { + operation: { + perform: { source: 'return [{id: 123}]' }, + inputFields: [{ key: 'message', type: 'string' }], + sample: { + name: 'sample', + }, + }, + }, + }, + }; + + const definitionHash = crypto + .createHash('md5') + .update(JSON.stringify(definition)) + .digest('hex'); + + const event = { + command: 'execute', + method: 'creates.operation.operation.inputFields', + appRawOverride: [definitionHash, definitionExtension], + rpc_base: 'https://mock.zapier.com/platform/rpc/cli', + token: 'fake', + }; + + return runner(event).then((response) => { + response.results.should.eql([{ key: 'message', type: 'string' }]); }); }); diff --git a/packages/core/src/tools/create-lambda-handler.js b/packages/core/src/tools/create-lambda-handler.js index 5956f7a57..a345e2462 100644 --- a/packages/core/src/tools/create-lambda-handler.js +++ b/packages/core/src/tools/create-lambda-handler.js @@ -19,42 +19,43 @@ const environmentTools = require('./environment'); const schemaTools = require('./schema'); const ZapierPromise = require('./promise'); -const RequestSchema = require('zapier-platform-schema/lib/schemas/RequestSchema'); -const FunctionSchema = require('zapier-platform-schema/lib/schemas/FunctionSchema'); - -const isRequestOrFunction = (obj) => { +const isDefinedPrimitive = (value) => { return ( - RequestSchema.validate(obj).valid || FunctionSchema.validate(obj).valid + value === null || + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' ); }; -const extendAppRaw = (base, extension) => { - const keysToOverride = [ - 'test', - 'perform', - 'performList', - 'performSubscribe', - 'performUnsubscribe', - ]; - const concatArrayAndOverrideKeys = (objValue, srcValue, key) => { - if (Array.isArray(objValue) && Array.isArray(srcValue)) { - return objValue.concat(srcValue); - } +const shouldFullyReplace = (path) => { + // covers inputFields, outputFields, sample, throttle, etc + const isOperation = path[path.length - 2] === 'operation'; + return isOperation; +}; - if ( - // Do full replacement when it comes to keysToOverride - keysToOverride.indexOf(key) !== -1 && - _.isPlainObject(srcValue) && - _.isPlainObject(objValue) && - isRequestOrFunction(srcValue) && - isRequestOrFunction(objValue) - ) { - return srcValue; +const extendAppRaw = (base, extension, path) => { + if (extension === undefined) { + return base; + } else if (isDefinedPrimitive(extension)) { + return extension; + } else if (Array.isArray(extension)) { + return [...extension]; + } else if (_.isPlainObject(extension)) { + path = path || []; + if (shouldFullyReplace(path)) { + return extension; + } else { + const baseObject = _.isPlainObject(base) ? base : {}; + const result = { ...baseObject }; + for (const [key, value] of Object.entries(extension)) { + const newPath = [...path, key]; + result[key] = extendAppRaw(baseObject[key], value, newPath); + } + return result; } - - return undefined; - }; - return _.mergeWith(base, extension, concatArrayAndOverrideKeys); + } + throw new TypeError('Unexpected type'); }; const getAppRawOverride = (rpc, appRawOverride) => {