Initial import of zappier-edge from zapier monorepo
This commit is contained in:
commit
d6b86e4284
120 changed files with 19867 additions and 0 deletions
23
zapier-app/authentication.js
Normal file
23
zapier-app/authentication.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
module.exports = {
|
||||
type: 'custom',
|
||||
test: (z, bundle) =>
|
||||
z.request({ url: `${bundle.authData.baseUrl}/v1/status` }).then((r) => r.data),
|
||||
fields: [
|
||||
{
|
||||
key: 'baseUrl',
|
||||
label: 'API Base URL',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: 'http://localhost:3000',
|
||||
helpText: 'Where your Zappier API is running.',
|
||||
},
|
||||
{
|
||||
key: 'apiKey',
|
||||
label: 'API Key',
|
||||
type: 'password',
|
||||
required: true,
|
||||
helpText: 'Your Zappier customer API key.',
|
||||
},
|
||||
],
|
||||
connectionLabel: '{{bundle.authData.baseUrl}}',
|
||||
};
|
||||
54
zapier-app/creates/store_data.js
Normal file
54
zapier-app/creates/store_data.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
const FormData = require('form-data');
|
||||
|
||||
const perform = async (z, bundle) => {
|
||||
const form = new FormData();
|
||||
form.append(
|
||||
'metadata',
|
||||
JSON.stringify({ title: bundle.inputData.title, note: bundle.inputData.note }),
|
||||
);
|
||||
|
||||
if (bundle.inputData.file) {
|
||||
const fileResponse = await z.request({
|
||||
url: bundle.inputData.file,
|
||||
raw: true,
|
||||
redirect: 'follow',
|
||||
});
|
||||
form.append('attachments', fileResponse.body, {
|
||||
filename: bundle.inputData.filename || 'attachment.bin',
|
||||
});
|
||||
}
|
||||
|
||||
const response = await z.request({
|
||||
url: `${bundle.authData.baseUrl}/v1/storage`,
|
||||
method: 'POST',
|
||||
body: form,
|
||||
headers: form.getHeaders(),
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'store_data',
|
||||
noun: 'Stored Item',
|
||||
display: {
|
||||
label: 'Store Data',
|
||||
description:
|
||||
'Stores metadata and an optional file attachment. Priced per call plus metadata KB and attachment MB on your plan.',
|
||||
},
|
||||
operation: {
|
||||
inputFields: [
|
||||
{ key: 'title', label: 'Title', type: 'string', required: true },
|
||||
{ key: 'note', label: 'Note', type: 'text', required: false },
|
||||
{
|
||||
key: 'file',
|
||||
label: 'Attachment',
|
||||
type: 'file',
|
||||
required: false,
|
||||
helpText: 'Optional file. Attachment size is billed per MB on your plan.',
|
||||
},
|
||||
{ key: 'filename', label: 'Filename', type: 'string', required: false },
|
||||
],
|
||||
perform,
|
||||
sample: { id: '3fa85f64-5717-4562-b3fc-2c963f66afa6' },
|
||||
},
|
||||
};
|
||||
20
zapier-app/index.js
Normal file
20
zapier-app/index.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const authentication = require('./authentication');
|
||||
const newItem = require('./triggers/new_item');
|
||||
const storeData = require('./creates/store_data');
|
||||
|
||||
const addApiKeyHeader = (request, z, bundle) => {
|
||||
request.headers = request.headers || {};
|
||||
if (request.url && request.url.startsWith(bundle.authData.baseUrl)) {
|
||||
request.headers['x-api-key'] = bundle.authData.apiKey;
|
||||
}
|
||||
return request;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
version: require('./package.json').version,
|
||||
platformVersion: require('zapier-platform-core').version,
|
||||
authentication,
|
||||
beforeRequest: [addApiKeyHeader],
|
||||
triggers: { [newItem.key]: newItem },
|
||||
creates: { [storeData.key]: storeData },
|
||||
};
|
||||
2087
zapier-app/package-lock.json
generated
Normal file
2087
zapier-app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
16
zapier-app/package.json
Normal file
16
zapier-app/package.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "zappier",
|
||||
"version": "1.0.0",
|
||||
"description": "Store data and files through the metered Zappier API.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha --recursive --timeout 10000"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-data": "^4.0.6",
|
||||
"zapier-platform-core": "^19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "^11.7.6"
|
||||
}
|
||||
}
|
||||
70
zapier-app/test/app.test.js
Normal file
70
zapier-app/test/app.test.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
const assert = require('assert');
|
||||
const App = require('../index');
|
||||
const storeData = require('../creates/store_data');
|
||||
|
||||
describe('Zapier app definition', () => {
|
||||
it('exposes custom auth, one trigger, and one action', () => {
|
||||
assert.equal(App.authentication.type, 'custom');
|
||||
assert.ok(App.triggers.new_item);
|
||||
assert.ok(App.creates.store_data);
|
||||
assert.equal(App.beforeRequest.length, 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addApiKeyHeader (beforeRequest)', () => {
|
||||
it('injects x-api-key only for the configured baseUrl', () => {
|
||||
const addApiKeyHeader = App.beforeRequest[0];
|
||||
const bundle = { authData: { baseUrl: 'http://localhost:3000', apiKey: 'key-ada' } };
|
||||
|
||||
const own = addApiKeyHeader(
|
||||
{ url: 'http://localhost:3000/v1/storage', headers: {} },
|
||||
null,
|
||||
bundle,
|
||||
);
|
||||
assert.equal(own.headers['x-api-key'], 'key-ada');
|
||||
|
||||
const foreign = addApiKeyHeader({ url: 'https://evil.example.com/f.pdf', headers: {} }, null, bundle);
|
||||
assert.equal(foreign.headers['x-api-key'], undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('store_data perform', () => {
|
||||
it('posts metadata JSON to /v1/storage', async () => {
|
||||
const requests = [];
|
||||
const z = {
|
||||
request: async (opts) => {
|
||||
requests.push(opts);
|
||||
return { data: { id: 'item_1' } };
|
||||
},
|
||||
};
|
||||
const bundle = {
|
||||
authData: { baseUrl: 'http://localhost:3000', apiKey: 'key-ada' },
|
||||
inputData: { title: 'report', note: 'hello' },
|
||||
};
|
||||
const result = await storeData.operation.perform(z, bundle);
|
||||
assert.equal(result.id, 'item_1');
|
||||
assert.equal(requests.length, 1);
|
||||
assert.equal(requests[0].method, 'POST');
|
||||
assert.equal(requests[0].url, 'http://localhost:3000/v1/storage');
|
||||
});
|
||||
|
||||
it('downloads the mapped file first, then posts it as an attachment', async () => {
|
||||
const requests = [];
|
||||
const z = {
|
||||
request: async (opts) => {
|
||||
requests.push(opts);
|
||||
return opts.raw ? { body: Buffer.from('x') } : { data: { id: 'item_2' } };
|
||||
},
|
||||
};
|
||||
const bundle = {
|
||||
authData: { baseUrl: 'http://localhost:3000', apiKey: 'key-ada' },
|
||||
inputData: { title: 'with file', file: 'https://example.com/f.pdf', filename: 'f.pdf' },
|
||||
};
|
||||
await storeData.operation.perform(z, bundle);
|
||||
assert.equal(requests.length, 2);
|
||||
assert.equal(requests[0].url, 'https://example.com/f.pdf');
|
||||
assert.equal(requests[0].raw, true);
|
||||
assert.equal(requests[1].url, 'http://localhost:3000/v1/storage');
|
||||
assert.equal(requests[1].method, 'POST');
|
||||
});
|
||||
});
|
||||
24
zapier-app/triggers/new_item.js
Normal file
24
zapier-app/triggers/new_item.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
const perform = async (z, bundle) => {
|
||||
const response = await z.request({ url: `${bundle.authData.baseUrl}/v1/storage` });
|
||||
return response.data.items;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'new_item',
|
||||
noun: 'Stored Item',
|
||||
display: {
|
||||
label: 'New Stored Item',
|
||||
description: 'Triggers when a new item is stored through the Zappier API.',
|
||||
},
|
||||
operation: {
|
||||
type: 'polling',
|
||||
perform,
|
||||
sample: {
|
||||
id: '3fa85f64-5717-4562-b3fc-2c963f66afa6',
|
||||
customerId: 'cust_1',
|
||||
metadata: { title: 'example' },
|
||||
attachments: [],
|
||||
createdAt: '2026-07-27T10:00:00.000Z',
|
||||
},
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue