I’m creating an app that (for now) takes a file as input and returns its hash (sha256).
It works fine as long as the user use a file as input, but if he puts something else (a string for example), the application silently fails (there is a stack trace in the app’s logs but Zapier displays nothing particular) and returns a wrong hash.
I do no have the impression that the error is handleable by my code and the stack is pretty obfuscated:
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Log │ Unhandled error: Error: Error: Could not find the method to call: authentication.test │
│ │ What happened: │
│ │ Executing authentication.test with bundle │
│ │ Error: Could not find the method to call: authentication.test │
│ │ Error: Error: Could not find the method to call: authentication.test │
│ │ at execute (:censored:9:d1ba0cf2aa:/node_modules/zapier-platform-core/src/execute.js:83:11) │
│ │ at input (:censored:9:d1ba0cf2aa:/node_modules/zapier-platform-core/src/create-command-handler.js:26:12) │
│ │ at Object.beforeMiddleware.then.newInput (:censored:9:d1ba0cf2aa:/node_modules/zapier-platform-core/src/middleware.js:90:22) │
│ │ at bound (domain.js:280:14) │
│ │ at Object.runBound (domain.js:293:12) │
│ │ at Object.tryCatcher (:censored:9:d1ba0cf2aa:/node_modules/bluebird/js/release/util.js:16:23) │
│ │ at Promise._settlePromiseFromHandler (:censored:9:d1ba0cf2aa:/node_modules/bluebird/js/release/promise.js:512:31) │
│ │ at Promise._settlePromise (:censored:9:d1ba0cf2aa:/node_modules/bluebird/js/release/promise.js:569:18) │
│ │ at Promise._settlePromise0 (:censored:9:d1ba0cf2aa:/node_modules/bluebird/js/release/promise.js:614:10) │
│ │ at Promise._settlePromises (:censored:9:d1ba0cf2aa:/node_modules/bluebird/js/release/promise.js:693:18) │
│ │ at Async._drainQueue (:censored:9:d1ba0cf2aa:/node_modules/bluebird/js/release/async.js:133:16) │
│ │ at Async._drainQueues (:censored:9:d1ba0cf2aa:/node_modules/bluebird/js/release/async.js:143:10) │
│ │ at Immediate.Async.drainQueues (:censored:9:d1ba0cf2aa:/node_modules/bluebird/js/release/async.js:17:14) │
│ │ at runCallback (timers.js:672:20) │
│ │ at tryOnImmediate (timers.js:645:5) │
│ │ at processImmediate [as _immediateCallback] (timers.js:617:5) │
│ Version │ 1.0.7 │
│ Step │ │
│ Timestamp │ 2018-05-24T03:57:57-05:00 │
└───────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
My code is essentially a copy of the "Files" Example App with the creates/uploadFile.js
file replaced by:
const request = require('request');
const crypto = require('crypto');
function hashStream(stream) {
const hash = crypto.createHash('sha256').setEncoding('hex');
return new Promise((resolve, reject) => {
stream.pipe(hash)
.on('finish', () => resolve(hash.read()))
.on('error', reject)
})
}
function hashFile(z, bundle) {
const fileStream = request(bundle.inputData.file);
return hashStream(fileStream).then((hash) => ({hash}));
};
module.exports = {
key: 'hashFile',
noun: 'File',
display: {
label: 'Hash File',
description: 'Performs a sha256 on file.'
},
operation: {
inputFields: [
{key: 'file', required: true, type: 'file', label: 'File'},
],
perform: hashFile,
sample: { filename: 'example.pdf' },
outputFields: [
{key: 'hash', type: 'string', label: 'Hash'}
],
}
};
Update:
I eventually found my mistake: I was assuming that the request
function would throw an error upon failure.
So the hashFile
function were certainly hashing the error page.
Changing the hashFile
function solved the problem:
function hashFile(z, bundle) {
return new Promise((resolve, reject) => {
request(bundle.inputData.file)
.on('response', function ({statusCode}) {
if (200 === statusCode) {
hashStream(this).then((hash) => resolve({hash}));
} else {
reject(new Error(`Invalid status code ${statusCode}`));
}
})
.on('error', (err) => reject(err))
})
}
But: I'm not able to catch "Unhandled errors"; I tried with
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at:', p, 'reason:', reason);
process.exit(1);
});
but I think that the Zapier engine prevents that kind of tricks because it has no effect...