Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): Add ESM support to zapier init command for Minimal and Typescript templates [PDE-5248] #976

Draft
wants to merge 5 commits into
base: zapier-platform-major-release-17.0.0-dev
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
add example apps
kreddlear committed Mar 5, 2025
commit 340a243f1f5fc6a41d7ab531a501ac4bc7d03139
63 changes: 63 additions & 0 deletions example-apps/minimal-esm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/
dist/

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# environment variables file
.env
.environment

# next.js build output
.next
24 changes: 24 additions & 0 deletions example-apps/minimal-esm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# test-init

This Zapier integration project is generated by the `zapier init` CLI command.

These are what you normally do next:

```bash
# Install dependencies
npm install # or you can use yarn

# Run tests
zapier test

# Register the integration on Zapier if you haven't
zapier register "App Title"

# Or you can link to an existing integration on Zapier
zapier link

# Push it to Zapier
zapier push
```

Find out more on the latest docs: https://github.com/zapier/zapier-platform/blob/main/packages/cli/README.md.
20 changes: 20 additions & 0 deletions example-apps/minimal-esm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { version as packageVersion } from './package.json';
import { version as platformVersion } from 'zapier-platform-core';

export default {
// This is just shorthand to reference the installed dependencies you have.
// Zapier will need to know these before we can upload.
version: packageVersion,
platformVersion: platformVersion,

// If you want your trigger to show up, you better include it here!
triggers: {},

// If you want your searches to show up, you better include it here!
searches: {},

// If you want your creates to show up, you better include it here!
creates: {},

resources: {},
};
21 changes: 21 additions & 0 deletions example-apps/minimal-esm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "minimal-esm",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "jest --testTimeout 10000"
},
"dependencies": {
"zapier-platform-core": "16.3.0"
},
"devDependencies": {
"jest": "^29.6.0"
},
"private": true,
"exports": {
"import": "./index.js",
"require": "./index.js"
},
"type": "module"
}

7 changes: 7 additions & 0 deletions example-apps/minimal-esm/test/example.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* globals describe, it, expect */

describe('addition ', () => {
it('should work', () => {
expect(1 + 1).toEqual(2);
});
});
63 changes: 63 additions & 0 deletions example-apps/typescript-esm/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/
dist/

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# environment variables file
.env
.environment

# next.js build output
.next
24 changes: 24 additions & 0 deletions example-apps/typescript-esm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# typescript

This Zapier integration project is generated by the `zapier init` CLI command.

These are what you normally do next:

```bash
# Install dependencies
npm install # or you can use yarn

# Run tests
zapier test

# Register the integration on Zapier if you haven't
zapier register "App Title"

# Or you can link to an existing integration on Zapier
zapier link

# Push it to Zapier
zapier push
```

Find out more on the latest docs: https://github.com/zapier/zapier-platform/blob/main/packages/cli/README.md.
2 changes: 2 additions & 0 deletions example-apps/typescript-esm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// TODO likely should remove this once we support non-root index.js
export { default } from './dist/index.js';
25 changes: 25 additions & 0 deletions example-apps/typescript-esm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "test-init",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "vitest",
"clean": "rimraf ./dist ./build",
"build": "npm run clean && tsc",
"_zapier-build": "npm run build"
},
"dependencies": {
"zapier-platform-core": "16.3.0"
},
"devDependencies": {
"vitest": "^2.1.2",
"rimraf": "^5.0.10",
"typescript": "5.6.2"
},
"private": true,
"exports": {
"import": "./dist/index.js",
"require": "./dist/index.js"
},
"type": "module"
}
42 changes: 42 additions & 0 deletions example-apps/typescript-esm/src/authentication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Authentication } from 'zapier-platform-core';

import { API_URL, SCOPES } from './constants.js';

export default {
type: 'oauth2',
test: { url: `${API_URL}/me` },
connectionLabel: '{{email}}', // Set this from the test data.
oauth2Config: {
authorizeUrl: {
url: `${API_URL}/oauth/authorize`,
params: {
client_id: '{{process.env.CLIENT_ID}}',
response_type: 'code',
scope: SCOPES.join(' '),
redirect_uri: '{{bundle.inputData.redirect_uri}}',
state: '{{bundle.inputData.state}}',
},
},
getAccessToken: {
url: `${API_URL}/oauth/access-token`,
method: 'POST',
params: {
client_id: '{{process.env.CLIENT_ID}}',
client_secret: '{{process.env.CLIENT_SECRET}}',
code: '{{bundle.inputData.code}}',
grant_type: 'authorization_code',
redirect_uri: '{{bundle.inputData.redirect_uri}}',
},
},
refreshAccessToken: {
url: `${API_URL}/oauth/refresh-token`,
method: 'POST',
params: {
client_id: '{{process.env.CLIENT_ID}}',
client_secret: '{{process.env.CLIENT_SECRET}}',
refresh_token: '{{bundle.authData.refresh_token}}',
grant_type: 'refresh_token',
},
},
},
} satisfies Authentication;
3 changes: 3 additions & 0 deletions example-apps/typescript-esm/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const API_URL = 'https://auth-json-server.zapier-staging.com';

export const SCOPES = ['movies:read', 'movies:write'];
36 changes: 36 additions & 0 deletions example-apps/typescript-esm/src/creates/movie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Create, PerformFunction } from 'zapier-platform-core';
import { API_URL } from '../constants.js';

const perform: PerformFunction = async (z, bundle) => {
const response = await z.request({
method: 'POST',
url: `${API_URL}/movies`,
body: {
title: bundle.inputData.title,
year: bundle.inputData.year,
},
});
return response.data;
};

export default {
key: 'movie',
noun: 'Movie',

display: {
label: 'Create Movie',
description: 'Creates a new movie.',
},

operation: {
perform,
inputFields: [
{ key: 'title', required: true },
{ key: 'year', type: 'integer' },
],
sample: {
id: '1',
title: 'example',
},
},
} satisfies Create;
26 changes: 26 additions & 0 deletions example-apps/typescript-esm/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { App } from 'zapier-platform-core';
import pkg from 'zapier-platform-core';
const { version: platformVersion } = pkg;

import packageJson from '../package.json' assert { type: 'json' };

import MovieCreate from './creates/movie.js';
import MovieTrigger from './triggers/movie.js';
import authentication from './authentication.js';
import { addBearerHeader } from './middleware.js';

export default {
version: packageJson.version,
platformVersion,

authentication,
beforeRequest: [addBearerHeader],

triggers: {
[MovieTrigger.key]: MovieTrigger,
},

creates: {
[MovieCreate.key]: MovieCreate,
},
} satisfies App;
11 changes: 11 additions & 0 deletions example-apps/typescript-esm/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { BeforeRequestMiddleware } from 'zapier-platform-core';

export const addBearerHeader: BeforeRequestMiddleware = (request, z, bundle) => {
if (bundle.authData.access_token && !request.headers?.Authorization) {
request.headers = {
...request.headers,
Authorization: `Bearer ${bundle.authData.access_token}`,
}
}
return request;
};
21 changes: 21 additions & 0 deletions example-apps/typescript-esm/src/test/creates.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createAppTester, tools } from 'zapier-platform-core';
import { describe, test, expect } from 'vitest';

import App from '../index.ts';

const appTester = createAppTester(App);
tools.env.inject();

describe('movie', () => {
test('create a movie', async () => {
const bundle = {
inputData: { title: 'hello', year: 2020 },
authData: { access_token: 'a_token' },
};
const result = await appTester(App.creates.movie.operation.perform, bundle);
expect(result).toMatchObject({
title: 'hello',
year: 2020,
});
});
});
25 changes: 25 additions & 0 deletions example-apps/typescript-esm/src/test/triggers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createAppTester, tools } from 'zapier-platform-core';
import { describe, test, expect } from 'vitest';

import App from '../index.ts';

const appTester = createAppTester(App);
tools.env.inject();

describe('movie', () => {
test('list movies', async () => {
const bundle = { inputData: {}, authData: { access_token: 'a_token' } };
const results = (await appTester(
App.triggers.movie.operation.perform,
bundle
)) as Array<object>;

expect(results.length).toBeGreaterThan(0);

const firstMovie = results[0];
expect(firstMovie).toMatchObject({
id: '1',
title: 'title 1',
});
});
});
26 changes: 26 additions & 0 deletions example-apps/typescript-esm/src/triggers/movie.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { PerformFunction, Trigger } from 'zapier-platform-core';
import { API_URL } from '../constants.js';

const perform: PerformFunction = async (z, bundle) => {
const response = await z.request(`${API_URL}/movies`);
return response.data;
};

export default {
key: 'movie',
noun: 'Movie',

display: {
label: 'New Movie',
description: 'Triggers when a new movie is created.',
},

operation: {
type: 'polling',
perform,
sample: {
id: '1',
title: 'example',
},
},
} satisfies Trigger;
17 changes: 17 additions & 0 deletions example-apps/typescript-esm/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"resolveJsonModule": true,
"esModuleInterop": true,
"noUncheckedIndexedAccess": true,
"isolatedModules": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true
},
"include": ["./src/**/*.ts"],
"exclude": ["./**/*.test.ts"]
}