Initial import of zappier-edge from zapier monorepo

This commit is contained in:
George Lambert 2026-09-11 12:58:25 -04:00
commit b8d239e759
120 changed files with 19867 additions and 0 deletions

View 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}}',
};

View 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
View 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

File diff suppressed because it is too large Load diff

16
zapier-app/package.json Normal file
View 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"
}
}

View 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');
});
});

View 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',
},
},
};