Initial import of verae-zapier-app from zapier monorepo
This commit is contained in:
commit
c19bccd652
16 changed files with 618 additions and 0 deletions
10
NATS.md
Normal file
10
NATS.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# NATS — verae-zapier-app
|
||||
|
||||
This Zapier Platform app **does not use NATS**.
|
||||
|
||||
| Direction | Address | Peer | Body |
|
||||
|-----------|---------|------|------|
|
||||
| IN | Zapier runtime | user Zap | create/search/trigger fields |
|
||||
| OUT HTTPS | middleware `/zapier/v1/timestamp`, `/wait`, `/verify`, `/status/{id}`, `/hashes/{sha256}`, `/hashes/{sha256}?includeTree=true`, REST Hook subscribe | verae-middleware | Bearer `zmw_` |
|
||||
|
||||
Job completion is a REST Hook HTTP POST from webhook-deliver, not a NATS client in Zapier.
|
||||
34
README.md
Normal file
34
README.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# verae-zapier
|
||||
|
||||
Zapier Platform CLI app for Verae. **Phase 11** in [../TODO.md](../TODO.md).
|
||||
|
||||
## Role
|
||||
|
||||
Runs on **Zapier’s servers**. Calls only the middleware HTTPS API (`MIDDLEWARE_BASE_URL`), never NATS and never `api.veraetime.net` directly.
|
||||
|
||||
## Planned modules
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `authentication.js` | Custom API key auth → `GET /zapier/v1/auth/me` |
|
||||
| `index.js` | App definition, beforeRequest, afterResponse error mapping |
|
||||
| `creates/timestamp_and_wait.js` | Primary action |
|
||||
| `creates/create_timestamp.js` | Async jobId action |
|
||||
| `creates/verify_timestamp.js` | Verify certificate |
|
||||
| `creates/batch_timestamp.js` | Batch create |
|
||||
| `searches/job_status.js` | Lookup by jobId |
|
||||
| `triggers/timestamp_completed.js` | REST Hook |
|
||||
|
||||
See [../docs/developer/modules/function-reference.md](../docs/developer/modules/function-reference.md) for I/O contracts.
|
||||
|
||||
## Env
|
||||
|
||||
```bash
|
||||
export MIDDLEWARE_BASE_URL=https://your-middleware.example.com
|
||||
```
|
||||
|
||||
## Gate
|
||||
|
||||
```bash
|
||||
npm run gate:11 # from monorepo root, after Phase 11 implementation
|
||||
```
|
||||
9
SUMMARY.md
Normal file
9
SUMMARY.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# verae-zapier-app
|
||||
|
||||
**Job:** Full Zapier CLI app: timestamp, wait, verify, batch, job status, hash lookup, REST Hook.
|
||||
|
||||
**Expects:** `zmw_` middleware key (today) or zappier key after composition.
|
||||
|
||||
**Sends:** HTTPS to middleware `/zapier/v1/*`.
|
||||
|
||||
**Does not** speak NATS.
|
||||
38
authentication.js
Normal file
38
authentication.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Custom API key auth against Verae middleware.
|
||||
* @module authentication
|
||||
*/
|
||||
|
||||
const middlewareBase = () =>
|
||||
process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
/**
|
||||
* Zapier connection test — validates API key.
|
||||
* @param {object} z
|
||||
* @param {object} bundle
|
||||
*/
|
||||
const testAuth = async (z, bundle) => {
|
||||
const response = await z.request({
|
||||
url: `${middlewareBase()}/zapier/v1/auth/me`,
|
||||
headers: {
|
||||
Authorization: `Bearer ${bundle.authData.api_key}`,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
type: 'custom',
|
||||
fields: [
|
||||
{
|
||||
key: 'api_key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
label: 'API Key',
|
||||
helpText:
|
||||
'Your Verae API key (zmw_…) from signup or admin provisioning.',
|
||||
},
|
||||
],
|
||||
test: testAuth,
|
||||
connectionLabel: '{{tenantId}} ({{plan}})',
|
||||
};
|
||||
2
creates/add_numbers.js
Normal file
2
creates/add_numbers.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/** Re-export activate-now Add Numbers so this package stays self-contained. */
|
||||
module.exports = require('../../verae-activate/creates/add_numbers');
|
||||
39
creates/batch_timestamp.js
Normal file
39
creates/batch_timestamp.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
const perform = async (z, bundle) => {
|
||||
const items = (bundle.inputData.items || '')
|
||||
.split('\n')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
.map((data) => ({ data }));
|
||||
|
||||
const response = await z.request({
|
||||
method: 'POST',
|
||||
url: `${base()}/zapier/v1/timestamp/batch`,
|
||||
body: { items },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'batch_timestamp',
|
||||
noun: 'Timestamp',
|
||||
display: {
|
||||
label: 'Create Batch Timestamps',
|
||||
description: 'Submit multiple data lines for timestamping (paid plans).',
|
||||
},
|
||||
operation: {
|
||||
inputFields: [
|
||||
{
|
||||
key: 'items',
|
||||
label: 'Data Lines',
|
||||
type: 'text',
|
||||
required: true,
|
||||
helpText: 'One payload per line.',
|
||||
},
|
||||
],
|
||||
perform,
|
||||
sample: { jobIds: ['id-1', 'id-2'] },
|
||||
outputFields: [{ key: 'jobIds', label: 'Job IDs' }],
|
||||
},
|
||||
};
|
||||
37
creates/create_timestamp.js
Normal file
37
creates/create_timestamp.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
const perform = async (z, bundle) => {
|
||||
const response = await z.request({
|
||||
method: 'POST',
|
||||
url: `${base()}/zapier/v1/timestamp`,
|
||||
body: {
|
||||
data: bundle.inputData.data,
|
||||
hashAlg: bundle.inputData.hashAlg || undefined,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'create_timestamp',
|
||||
noun: 'Timestamp',
|
||||
display: {
|
||||
label: 'Create Timestamp (Async)',
|
||||
description: 'Submits data for timestamping and returns a job ID.',
|
||||
},
|
||||
operation: {
|
||||
inputFields: [
|
||||
{ key: 'data', label: 'Data', type: 'string', required: true },
|
||||
{
|
||||
key: 'hashAlg',
|
||||
label: 'Hash Algorithm',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: 'SHA256',
|
||||
},
|
||||
],
|
||||
perform,
|
||||
sample: { jobId: '550e8400-e29b-41d4-a716-446655440000' },
|
||||
outputFields: [{ key: 'jobId', label: 'Job ID' }],
|
||||
},
|
||||
};
|
||||
54
creates/timestamp_and_wait.js
Normal file
54
creates/timestamp_and_wait.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
const perform = async (z, bundle) => {
|
||||
const response = await z.request({
|
||||
method: 'POST',
|
||||
url: `${base()}/zapier/v1/timestamp/wait`,
|
||||
body: {
|
||||
data: bundle.inputData.data,
|
||||
hashAlg: bundle.inputData.hashAlg || undefined,
|
||||
},
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'timestamp_and_wait',
|
||||
noun: 'Timestamp',
|
||||
display: {
|
||||
label: 'Create Timestamp and Wait',
|
||||
description:
|
||||
'Submits data for blockchain timestamping and waits for the certificate.',
|
||||
},
|
||||
operation: {
|
||||
inputFields: [
|
||||
{
|
||||
key: 'data',
|
||||
label: 'Data',
|
||||
type: 'string',
|
||||
required: true,
|
||||
helpText: 'Content to timestamp on the blockchain.',
|
||||
},
|
||||
{
|
||||
key: 'hashAlg',
|
||||
label: 'Hash Algorithm',
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: 'SHA256',
|
||||
},
|
||||
],
|
||||
perform,
|
||||
sample: {
|
||||
id: '550e8400-e29b-41d4-a716-446655440000',
|
||||
status: 'completed',
|
||||
result: 'mock-cert-example',
|
||||
completedAt: '2023-01-01T12:05:00Z',
|
||||
},
|
||||
outputFields: [
|
||||
{ key: 'id', label: 'Job ID' },
|
||||
{ key: 'status', label: 'Status' },
|
||||
{ key: 'result', label: 'Certificate' },
|
||||
{ key: 'completedAt', label: 'Completed At', type: 'datetime' },
|
||||
],
|
||||
},
|
||||
};
|
||||
36
creates/verify_timestamp.js
Normal file
36
creates/verify_timestamp.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
const perform = async (z, bundle) => {
|
||||
const response = await z.request({
|
||||
method: 'POST',
|
||||
url: `${base()}/zapier/v1/verify`,
|
||||
body: { certificate: bundle.inputData.certificate },
|
||||
});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'verify_timestamp',
|
||||
noun: 'Verification',
|
||||
display: {
|
||||
label: 'Verify Timestamp',
|
||||
description: 'Verify a timestamp certificate.',
|
||||
},
|
||||
operation: {
|
||||
inputFields: [
|
||||
{
|
||||
key: 'certificate',
|
||||
label: 'Certificate',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
perform,
|
||||
sample: { valid: true, timestamp: '2023-01-01T12:00:00Z', blockIndex: 42 },
|
||||
outputFields: [
|
||||
{ key: 'valid', label: 'Valid', type: 'boolean' },
|
||||
{ key: 'timestamp', label: 'Timestamp', type: 'datetime' },
|
||||
{ key: 'blockIndex', label: 'Block Index', type: 'integer' },
|
||||
],
|
||||
},
|
||||
};
|
||||
82
index.js
Normal file
82
index.js
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Verae Zapier Platform app definition.
|
||||
* @module index
|
||||
*/
|
||||
|
||||
const authentication = require('./authentication');
|
||||
const timestampAndWait = require('./creates/timestamp_and_wait');
|
||||
const createTimestamp = require('./creates/create_timestamp');
|
||||
const verifyTimestamp = require('./creates/verify_timestamp');
|
||||
const batchTimestamp = require('./creates/batch_timestamp');
|
||||
const addNumbers = require('./creates/add_numbers');
|
||||
const jobStatus = require('./searches/job_status');
|
||||
const hashLookup = require('./searches/hash_lookup');
|
||||
const treeLookup = require('./searches/tree_lookup');
|
||||
const timestampCompleted = require('./triggers/timestamp_completed');
|
||||
|
||||
/**
|
||||
* Attach middleware API key to every outbound request.
|
||||
* @param {object} request
|
||||
* @param {object} _z
|
||||
* @param {object} bundle
|
||||
*/
|
||||
const addApiKey = (request, _z, bundle) => {
|
||||
request.headers = request.headers || {};
|
||||
request.headers.Authorization = `Bearer ${bundle.authData.api_key}`;
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map middleware billing errors to Zapier errors.
|
||||
* @param {object} response
|
||||
* @param {object} z
|
||||
*/
|
||||
const mapMiddlewareErrors = (response, z) => {
|
||||
if (response.status === 402) {
|
||||
throw new z.errors.Error(
|
||||
`${response.data?.error ?? 'Quota exceeded'}. Upgrade at ${response.data?.details?.upgradeUrl ?? response.data?.upgradeUrl ?? 'your billing portal'}.`,
|
||||
'QuotaExceeded',
|
||||
402,
|
||||
);
|
||||
}
|
||||
|
||||
if (response.status === 403 && response.data?.code === 'PLAN_UPGRADE_REQUIRED') {
|
||||
throw new z.errors.Error(
|
||||
response.data?.error ?? 'This action requires a paid plan.',
|
||||
'PlanUpgradeRequired',
|
||||
403,
|
||||
);
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
let platformVersion = '15.19.0';
|
||||
try {
|
||||
platformVersion = require('zapier-platform-core').version;
|
||||
} catch {
|
||||
// optional for unit tests without full install
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
version: require('./package.json').version,
|
||||
platformVersion,
|
||||
authentication,
|
||||
beforeRequest: [addApiKey],
|
||||
afterResponse: [mapMiddlewareErrors],
|
||||
triggers: {
|
||||
[timestampCompleted.key]: timestampCompleted,
|
||||
},
|
||||
creates: {
|
||||
[addNumbers.key]: addNumbers,
|
||||
[timestampAndWait.key]: timestampAndWait,
|
||||
[createTimestamp.key]: createTimestamp,
|
||||
[verifyTimestamp.key]: verifyTimestamp,
|
||||
[batchTimestamp.key]: batchTimestamp,
|
||||
},
|
||||
searches: {
|
||||
[jobStatus.key]: jobStatus,
|
||||
[hashLookup.key]: hashLookup,
|
||||
[treeLookup.key]: treeLookup,
|
||||
},
|
||||
};
|
||||
17
package.json
Normal file
17
package.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "verae-zapier",
|
||||
"version": "1.0.0",
|
||||
"description": "Zapier CLI app for Verae Timestamping via middleware",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "node --test test/**/*.test.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"npm": ">=5.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"zapier-platform-core": "15.19.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
34
searches/hash_lookup.js
Normal file
34
searches/hash_lookup.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
const perform = async (z, bundle) => {
|
||||
try {
|
||||
const response = await z.request({
|
||||
method: 'GET',
|
||||
url: `${base()}/zapier/v1/hashes/${encodeURIComponent(bundle.inputData.sha256)}`,
|
||||
});
|
||||
return [response.data];
|
||||
} catch (err) {
|
||||
if (err.status === 404) return [];
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'hash_lookup',
|
||||
noun: 'Timestamp',
|
||||
display: {
|
||||
label: 'Find Timestamp by SHA256',
|
||||
description: 'Looks up an existing timestamp for a SHA256 hex digest (mock/middleware).',
|
||||
},
|
||||
operation: {
|
||||
inputFields: [
|
||||
{ key: 'sha256', label: 'SHA256', type: 'string', required: true },
|
||||
],
|
||||
perform,
|
||||
sample: {
|
||||
sha256: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
|
||||
exists: true,
|
||||
jobId: '550e8400-e29b-41d4-a716-446655440000',
|
||||
},
|
||||
},
|
||||
};
|
||||
33
searches/job_status.js
Normal file
33
searches/job_status.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
const perform = async (z, bundle) => {
|
||||
const response = await z.request({
|
||||
url: `${base()}/zapier/v1/status/${encodeURIComponent(bundle.inputData.jobId)}`,
|
||||
});
|
||||
return [response.data];
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'job_status',
|
||||
noun: 'Job',
|
||||
display: {
|
||||
label: 'Find Job Status',
|
||||
description: 'Look up a timestamp job by ID.',
|
||||
},
|
||||
operation: {
|
||||
inputFields: [
|
||||
{ key: 'jobId', label: 'Job ID', type: 'string', required: true },
|
||||
],
|
||||
perform,
|
||||
sample: {
|
||||
id: '550e8400-e29b-41d4-a716-446655440000',
|
||||
status: 'completed',
|
||||
result: 'mock-cert',
|
||||
},
|
||||
outputFields: [
|
||||
{ key: 'id', label: 'Job ID' },
|
||||
{ key: 'status', label: 'Status' },
|
||||
{ key: 'result', label: 'Result' },
|
||||
],
|
||||
},
|
||||
};
|
||||
45
searches/tree_lookup.js
Normal file
45
searches/tree_lookup.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
const perform = async (z, bundle) => {
|
||||
try {
|
||||
const sha = encodeURIComponent(bundle.inputData.sha256);
|
||||
const response = await z.request({
|
||||
method: 'GET',
|
||||
url: `${base()}/zapier/v1/hashes/${sha}?includeAttached=true&includeTree=true`,
|
||||
});
|
||||
return [response.data];
|
||||
} catch (err) {
|
||||
if (err.status === 404) return [];
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
key: 'tree_lookup',
|
||||
noun: 'Timestamp',
|
||||
display: {
|
||||
label: 'Find Hash (tree nodes + central chain)',
|
||||
description:
|
||||
'Looks up a SHA256 on the main Verae chain, then queries external tree-node archives for leaves that were only sealed as a bulk Merkle summary.',
|
||||
},
|
||||
operation: {
|
||||
inputFields: [
|
||||
{
|
||||
key: 'sha256',
|
||||
label: 'SHA256',
|
||||
type: 'string',
|
||||
required: true,
|
||||
helpText:
|
||||
'64-char hex. If this hash was part of a batch, it may not be itemized on the main chain — this search still finds the Merkle proof on tree nodes.',
|
||||
},
|
||||
],
|
||||
perform,
|
||||
sample: {
|
||||
sha256: '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
|
||||
exists: true,
|
||||
itemizedOnMainChain: false,
|
||||
merkleRoot: 'ab'.repeat(32),
|
||||
proofOk: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
95
test/app.test.js
Normal file
95
test/app.test.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* GATE 11 — Zapier package shape tests (no live Zapier CLI required)
|
||||
*/
|
||||
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const App = require('../index');
|
||||
const authentication = require('../authentication');
|
||||
const timestampAndWait = require('../creates/timestamp_and_wait');
|
||||
const createTimestamp = require('../creates/create_timestamp');
|
||||
const verifyTimestamp = require('../creates/verify_timestamp');
|
||||
const batchTimestamp = require('../creates/batch_timestamp');
|
||||
const jobStatus = require('../searches/job_status');
|
||||
const timestampCompleted = require('../triggers/timestamp_completed');
|
||||
|
||||
describe('verae-zapier app definition', () => {
|
||||
it('exports authentication with api_key field', () => {
|
||||
assert.equal(authentication.type, 'custom');
|
||||
assert.ok(authentication.fields.some((f) => f.key === 'api_key'));
|
||||
});
|
||||
|
||||
it('wires creates, searches, triggers', () => {
|
||||
assert.ok(App.creates.add_numbers);
|
||||
assert.ok(App.creates.timestamp_and_wait);
|
||||
assert.ok(App.creates.create_timestamp);
|
||||
assert.ok(App.creates.verify_timestamp);
|
||||
assert.ok(App.creates.batch_timestamp);
|
||||
assert.ok(App.searches.job_status);
|
||||
assert.ok(App.searches.hash_lookup);
|
||||
assert.ok(App.searches.tree_lookup);
|
||||
assert.ok(App.triggers.timestamp_completed);
|
||||
});
|
||||
|
||||
it('create actions target middleware paths', () => {
|
||||
process.env.MIDDLEWARE_BASE_URL = 'https://mw.example.com';
|
||||
const urls = [];
|
||||
const z = {
|
||||
request: async (opts) => {
|
||||
urls.push(opts.url);
|
||||
return { data: { ok: true } };
|
||||
},
|
||||
};
|
||||
const bundle = {
|
||||
authData: { api_key: 'zmw_test' },
|
||||
inputData: { data: 'x', certificate: 'c', items: 'a\nb', jobId: 'j1' },
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
timestampAndWait.operation.perform(z, bundle),
|
||||
createTimestamp.operation.perform(z, bundle),
|
||||
verifyTimestamp.operation.perform(z, bundle),
|
||||
batchTimestamp.operation.perform(z, bundle),
|
||||
jobStatus.operation.perform(z, bundle),
|
||||
]).then(() => {
|
||||
assert.ok(urls.some((u) => u.endsWith('/zapier/v1/timestamp/wait')));
|
||||
assert.ok(urls.some((u) => u.endsWith('/zapier/v1/timestamp')));
|
||||
assert.ok(urls.some((u) => u.endsWith('/zapier/v1/verify')));
|
||||
assert.ok(urls.some((u) => u.endsWith('/zapier/v1/timestamp/batch')));
|
||||
assert.ok(urls.some((u) => u.includes('/zapier/v1/status/')));
|
||||
});
|
||||
});
|
||||
|
||||
it('trigger subscribe/unsubscribe shapes', async () => {
|
||||
process.env.MIDDLEWARE_BASE_URL = 'https://mw.example.com';
|
||||
const calls = [];
|
||||
const z = {
|
||||
request: async (opts) => {
|
||||
calls.push(opts);
|
||||
return { data: { id: 'hook-1' } };
|
||||
},
|
||||
};
|
||||
|
||||
const id = await timestampCompleted.operation.performSubscribe(z, {
|
||||
targetUrl: 'https://hooks.zapier.com/x',
|
||||
authData: { api_key: 'k' },
|
||||
});
|
||||
assert.equal(id, 'hook-1');
|
||||
assert.equal(calls[0].method, 'POST');
|
||||
assert.match(calls[0].url, /webhooks\/subscribe$/);
|
||||
assert.equal(calls[0].body.event, 'timestamp.completed');
|
||||
|
||||
await timestampCompleted.operation.performUnsubscribe(z, {
|
||||
subscribeData: 'hook-1',
|
||||
authData: { api_key: 'k' },
|
||||
});
|
||||
assert.equal(calls[1].method, 'DELETE');
|
||||
assert.match(calls[1].url, /webhooks\/unsubscribe$/);
|
||||
});
|
||||
|
||||
it('beforeRequest adds Authorization bearer', () => {
|
||||
const req = App.beforeRequest[0]({}, null, { authData: { api_key: 'zmw_abc' } });
|
||||
assert.equal(req.headers.Authorization, 'Bearer zmw_abc');
|
||||
});
|
||||
});
|
||||
53
triggers/timestamp_completed.js
Normal file
53
triggers/timestamp_completed.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
const base = () => process.env.MIDDLEWARE_BASE_URL || 'http://127.0.0.1:3100';
|
||||
|
||||
const subscribeHook = async (z, bundle) => {
|
||||
const response = await z.request({
|
||||
method: 'POST',
|
||||
url: `${base()}/zapier/v1/webhooks/subscribe`,
|
||||
body: {
|
||||
targetUrl: bundle.targetUrl,
|
||||
event: 'timestamp.completed',
|
||||
},
|
||||
});
|
||||
return response.data.id;
|
||||
};
|
||||
|
||||
const unsubscribeHook = async (z, bundle) => {
|
||||
await z.request({
|
||||
method: 'DELETE',
|
||||
url: `${base()}/zapier/v1/webhooks/unsubscribe`,
|
||||
body: {
|
||||
hookId: bundle.subscribeData,
|
||||
},
|
||||
});
|
||||
return {};
|
||||
};
|
||||
|
||||
const perform = async (z, bundle) => [bundle.cleanedRequest];
|
||||
|
||||
const performList = async () => [];
|
||||
|
||||
module.exports = {
|
||||
key: 'timestamp_completed',
|
||||
noun: 'Timestamp',
|
||||
display: {
|
||||
label: 'Timestamp Completed',
|
||||
description: 'Triggers when a blockchain timestamp job completes.',
|
||||
},
|
||||
operation: {
|
||||
type: 'hook',
|
||||
perform,
|
||||
performList,
|
||||
performSubscribe: subscribeHook,
|
||||
performUnsubscribe: unsubscribeHook,
|
||||
sample: {
|
||||
event: 'timestamp.completed',
|
||||
jobId: '550e8400-e29b-41d4-a716-446655440000',
|
||||
status: { id: '550e8400-e29b-41d4-a716-446655440000', status: 'completed' },
|
||||
},
|
||||
outputFields: [
|
||||
{ key: 'event', label: 'Event' },
|
||||
{ key: 'jobId', label: 'Job ID' },
|
||||
],
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue