Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sdenardi committed May 14, 2020
0 parents commit 18e8b4a
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 0 deletions.
61 changes: 61 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"env": {
"browser": true,
"node": true,
"es6": true
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"sourceType": "module"
},
"extends": "eslint:recommended",
"rules": {
"array-bracket-spacing": ["warn"],
"array-callback-return": ["warn"],
"arrow-parens": ["warn"],
"arrow-spacing": ["warn", { "before": true, "after": true }],
"block-spacing": ["error", "always"],
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"comma-spacing": ["error", {"before": false, "after": true}],
"comma-style": ["warn"],
"consistent-this": ["error", "self"],
"dot-notation": ["error"],
"eol-last": ["error"],
"eqeqeq": ["error", "smart"],
"func-style": ["error", "expression", { "allowArrowFunctions": true }],
"handle-callback-err": ["error"],
"indent": ["error", 2, {"SwitchCase": 1, "VariableDeclarator": { "let": 2, "const": 2}}],
"key-spacing": ["error", {"beforeColon": false, "afterColon": true}],
"new-cap": ["error", {"capIsNew": false}],
"newline-per-chained-call": ["off"],
"no-console": "off",
"no-iterator": ["warn"],
"no-mixed-requires": ["error"],
"no-nested-ternary": ["off"],
"no-path-concat": ["error"],
"no-restricted-globals": ["error", "event", "fdescribe"],
"no-unneeded-ternary": ["error"],
"no-unused-vars": ["off"],
"no-useless-constructor": ["off"],
"no-var": ["warn"],
"object-curly-spacing": ["warn", "always", { "arraysInObjects": true, "objectsInObjects": true }],
"prefer-arrow-callback": ["warn"],
"prefer-const": ["warn"],
"prefer-template": ["off"],
"quote-props": ["warn", "as-needed"],
"require-atomic-updates": "off",
"space-before-blocks": ["warn"],
"space-in-parens": ["warn"],
"space-infix-ops": ["warn"],
"strict": ["error", "never"],
"@typescript-eslint/semi": ["error", "always"],
"@typescript-eslint/no-unused-vars": ["error", {"vars": "all", "args": "after-used", "varsIgnorePattern": "React"}],
"quotes": ["error", "single"]
}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
.DS_Store
package-lock.json
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# zoomapi

NodeJS library for working with the Zoom API.

`zoomapi` provides server-side access to the [Zoom APIs](https://marketplace.zoom.us/docs/api-reference/introduction) via JWT access tokens. It's written completely in Typescript, and only has one dependency. Works on NodeJS version 8 and higher.

```js
npm i zoomapi
```

## Usage

```js
import zoomApi from 'zoomapi';

const client = zoomApi({
apiKey: process.NODE_ENV.ZoomApiKey,
apiSecret: process.NODE_ENV.ZoomApiSecret
});
const users = await client.users.ListUsers();
```

## API Progress

This library doesn't (yet) implement every API call. PRs are welcome and encouraged.

## Tests

None yet.

## Contributing

Pull requests are more than welcome! Please follow existing naming and style conventions, and correct any linting errors.

## License

MIT
24 changes: 24 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "zoomapi",
"version": "1.0.0",
"private": true,
"description": "NodeJS library for working with the Zoom API.",
"repository": {
"type": "git",
"url": "https://github.com/sedenardi/zoomapi.git"
},
"author": "Sanders DeNardi <sedenardi@gmail.com> (http://www.sandersdenardi.com/)",
"homepage": "https://github.com/sedenardi/zoomapi",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^8.5.1"
},
"devDependencies": {
"@types/jsonwebtoken": "^8.5.0",
"@types/node": "^14.0.1",
"@typescript-eslint/eslint-plugin": "^2.31.0",
"@typescript-eslint/parser": "^2.31.0",
"eslint": "^6.8.0",
"typescript": "^3.8.3"
}
}
12 changes: 12 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import users from './users';

export type ZoomOptions = {
apiKey: string;
apiSecret: string;
tokenExpiresIn?: string | number;
};
export default function(zoomApiOpts: ZoomOptions) {
return {
users: users(zoomApiOpts)
};
}
48 changes: 48 additions & 0 deletions src/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import request from './util/request';
import { ZoomOptions } from '.';

export default function(zoomApiOpts: ZoomOptions) {
const zoomRequest = request(zoomApiOpts);

type ListUsersParams = {
status?: 'active' | 'inactive' | 'pending';
page_size?: number;
page_number?: number;
role_id?: string;
};
type ListUserResponse = {
page_count: number;
page_number: number;
page_size: number;
total_records: number;
users: {
id: string;
first_name: string;
last_name: string;
email: string;
type: 1 | 2 | 3;
status: string;
pmi: number;
timezone: string;
dept: string;
created_at: string;
last_login_time: string;
last_client_version: string;
group_ids: string[];
im_group_ids: string[];
verified: 0 | 1;
}[];
};
const ListUsers = function(params?: ListUsersParams) {
return zoomRequest<ListUserResponse>({
method: 'GET',
path: '/users',
params: params
});
};


return {
ListUsers
};
}
18 changes: 18 additions & 0 deletions src/util/getAuthToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import jwt from 'jsonwebtoken';
import { ZoomOptions } from '../';

export default function getAuthToken(zoomApiOpts: ZoomOptions) {
const payload = {
iss: zoomApiOpts.apiKey
};
const options: jwt.SignOptions = {
algorithm: 'HS256',
expiresIn: zoomApiOpts.tokenExpiresIn ?? '5s'
};
return new Promise<string>((resolve, reject) => {
jwt.sign(payload, zoomApiOpts.apiSecret, options, (err, res) => {
if (err) { return reject(err); }
return resolve(res);
});
});
}
67 changes: 67 additions & 0 deletions src/util/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import https from 'https';
import getAuthToken from './getAuthToken';
import { ZoomOptions } from '../';

const BASE_URL = 'api.zoom.us';
const API_VERSION = '/v2';

type ZoomRequestOpts = {
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
path: string;
params?: object;
body?: object;
};

const buildURL = function(url: string, params?: object) {
if (!params) {
return url;
}
const sp = new URLSearchParams();
for (const k in params) {
if (params[k] !== undefined) {
sp.set(k, params[k]);
}
}
return url + '?' + sp.toString();
};

export default function(zoomApiOpts: ZoomOptions) {
return async function zoomRequest<T>(opts: ZoomRequestOpts) {
const authToken = await getAuthToken(zoomApiOpts);
const requestOpts = {
method: opts.method,
hostname: BASE_URL,
path: API_VERSION + buildURL(opts.path, opts.params),
headers: {
'content-type': 'application/json',
authorization: `Bearer ${authToken}`
}
};
return await new Promise<T>((resolve, reject) => {
const httpsRequest = https.request(requestOpts, (res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error(`HTTPS request failed, status code: ${res.statusCode}`));
}

const data = [];
res.on('data', (chunk) => {
data.push(chunk);
});
res.on('end', () => {
const body = Buffer.concat(data);
resolve(JSON.parse(body.toString()));
});
});

httpsRequest.on('error', (err) => {
reject(err);
});

if (opts.body) {
httpsRequest.write(JSON.stringify(opts.body));
}

httpsRequest.end();
});
};
}

0 comments on commit 18e8b4a

Please sign in to comment.