From ca3421b1168df7eb10060d3a01e409af9551db85 Mon Sep 17 00:00:00 2001 From: Igor Schechtel Date: Thu, 23 May 2024 16:47:29 -0300 Subject: [PATCH 1/5] Added package.json file and updated package-lock --- package-lock.json | 264 +++++++++++++++++++++++++++++++++++-- packages/zoom/package.json | 24 ++++ 2 files changed, 274 insertions(+), 14 deletions(-) create mode 100644 packages/zoom/package.json diff --git a/package-lock.json b/package-lock.json index 5bcdc22..7677d25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2298,10 +2298,14 @@ "resolved": "packages/unbabel", "link": true }, + "node_modules/@friggframework/api-module-zoom": { + "resolved": "packages/zoom", + "link": true + }, "node_modules/@friggframework/core": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@friggframework/core/-/core-1.1.2.tgz", - "integrity": "sha512-rwf3P88xBr7qhRMclkQRASWeK/ps5HY0lqhY684XIfcqaybko9uqQW4r1u65zGvxpY8xmf+uMeHfYFsEiw+esQ==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@friggframework/core/-/core-1.1.6.tgz", + "integrity": "sha512-KKCYddd97Tky+DRAluJOQSI9LTP138z6tF0tiw2RlmfbU8hb3+aLi/M9E2dr2xYYafnyhN+23UF/oFPoGDv4Hw==", "dependencies": { "@hapi/boom": "^10.0.1", "aws-sdk": "^2.1200.0", @@ -2359,14 +2363,14 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/@friggframework/devtools": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@friggframework/devtools/-/devtools-1.1.2.tgz", - "integrity": "sha512-K48RxGtLwhEWXGfSHQNgKS5T/57/iuKqU4qu2aNuMhdDUotD8ppNnpA8kZXEdsRJf3gbzTeqKElYR+rPRfIHIA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@friggframework/devtools/-/devtools-1.1.6.tgz", + "integrity": "sha512-ToTr1qdSO2MLDW8FX+9Fnp2gtNBU+Gkunuk/ZBoz5UvtciC/1KHRZ01NCTb9B6AaIuidC39Rq2sq3WWr/RJnCQ==", "dev": true, "dependencies": { "@babel/eslint-parser": "^7.18.9", - "@friggframework/core": "^1.1.2", - "@friggframework/test": "^1.1.2", + "@friggframework/core": "^1.1.6", + "@friggframework/test": "^1.1.6", "eslint": "^8.22.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-json": "^3.1.0", @@ -2376,9 +2380,9 @@ } }, "node_modules/@friggframework/test": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@friggframework/test/-/test-1.1.2.tgz", - "integrity": "sha512-S8PMrn/uKjvzXaWdtYHSpJAoaAa9r6l2z9hMJO+pBINj0DvAPChaVXyEOFfjQwfL0Ad867ebjR3iTIaOXr1QYQ==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@friggframework/test/-/test-1.1.6.tgz", + "integrity": "sha512-FLhUpX3dcxNZUrCDTfh7+QfIBzmY2Jm7Ah25Y4QZjPTiztVEf9YC7nVEF3DMzoPYkWeIbmxvK+ZQa9ab2AH6Kg==", "dev": true, "dependencies": { "@babel/eslint-parser": "^7.18.9", @@ -2441,11 +2445,24 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@hutson/parse-repository-url": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", @@ -21050,6 +21067,52 @@ } } }, + "packages/node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/node_modules/@eslint/js": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", + "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "packages/node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, "packages/node_modules/@jest/console": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", @@ -21488,6 +21551,60 @@ "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, + "packages/node_modules/eslint-scope": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", + "integrity": "sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/node_modules/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "packages/node_modules/expect": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", @@ -21504,6 +21621,43 @@ "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, + "packages/node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "packages/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "packages/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/node_modules/istanbul-lib-instrument": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", @@ -22145,6 +22299,88 @@ "jest": "^29.4.3", "prettier": "^2.8.4" } + }, + "packages/zoom": { + "name": "@friggframework/api-module-zoom", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@friggframework/core": "^1.1.6" + }, + "devDependencies": { + "@friggframework/devtools": "^1.1.6", + "@friggframework/test": "^1.1.6", + "dotenv": "^16.4.5", + "eslint": "^9.3.0", + "jest": "^29.7.0", + "prettier": "^3.2.5" + } + }, + "packages/zoom/node_modules/eslint": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.3.0.tgz", + "integrity": "sha512-5Iv4CsZW030lpUqHBapdPo3MJetAPtejVW8B84GIcIIv8+ohFaddXsrn1Gn8uD9ijDb+kcYKFUVmC8qG8B2ORQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.3.0", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.0.1", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.0.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "packages/zoom/node_modules/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } } } } diff --git a/packages/zoom/package.json b/packages/zoom/package.json new file mode 100644 index 0000000..a40fbdb --- /dev/null +++ b/packages/zoom/package.json @@ -0,0 +1,24 @@ +{ + "name": "@friggframework/api-module-zoom", + "version": "1.0.0", + "prettier": "@friggframework/prettier-config", + "description": "Zoom API module that lets the Frigg Framework interact with Zoom", + "main": "index.js", + "scripts": { + "lint:fix": "prettier --write --loglevel error . && eslint . --fix", + "test": "jest" + }, + "author": "", + "license": "MIT", + "devDependencies": { + "@friggframework/devtools": "^1.1.6", + "@friggframework/test": "^1.1.6", + "dotenv": "^16.4.5", + "eslint": "^9.3.0", + "jest": "^29.7.0", + "prettier": "^3.2.5" + }, + "dependencies": { + "@friggframework/core": "^1.1.6" + } +} From 38ed806ff489df68c370260b000d268f484537dc Mon Sep 17 00:00:00 2001 From: Igor Schechtel Date: Mon, 27 May 2024 16:33:25 -0300 Subject: [PATCH 2/5] Update Zoom module to v1 --- packages/zoom/.env.example | 3 + packages/zoom/README.md | 6 +- packages/zoom/api.js | 6 +- packages/zoom/definition.js | 48 +++++++++++ packages/zoom/jest-setup.js | 1 + packages/zoom/manager.js | 80 ------------------ packages/zoom/manager.test.js | 26 ------ packages/zoom/models/credential.js | 13 --- packages/zoom/models/entity.js | 9 --- packages/zoom/package.json | 6 +- packages/zoom/tests/api.test.js | 114 ++++++++++++++++++++++++++ packages/zoom/tests/auther.test.js | 125 +++++++++++++++++++++++++++++ 12 files changed, 299 insertions(+), 138 deletions(-) create mode 100644 packages/zoom/.env.example create mode 100644 packages/zoom/definition.js delete mode 100644 packages/zoom/manager.js delete mode 100644 packages/zoom/manager.test.js delete mode 100644 packages/zoom/models/credential.js delete mode 100644 packages/zoom/models/entity.js create mode 100644 packages/zoom/tests/api.test.js create mode 100644 packages/zoom/tests/auther.test.js diff --git a/packages/zoom/.env.example b/packages/zoom/.env.example new file mode 100644 index 0000000..06a0561 --- /dev/null +++ b/packages/zoom/.env.example @@ -0,0 +1,3 @@ +ZOOM_CLIENT_ID="" +ZOOM_CLIENT_SECRET="" +REDIRECT_URI=http://localhost:3000/redirect \ No newline at end of file diff --git a/packages/zoom/README.md b/packages/zoom/README.md index 60354d7..256211a 100644 --- a/packages/zoom/README.md +++ b/packages/zoom/README.md @@ -1,5 +1,5 @@ -# zoom +# Zoom -This is the API Module for zoom that allows the [Frigg](https://friggframework.org) code to talk to the zoom API. +This is the API Module for Zoom that allows the [Frigg](https://friggframework.org) code to talk to the Zoom API. -Read more on the [Frigg documentation site](https://docs.friggframework.org/api-modules/list/zoom \ No newline at end of file +Read more on the [Frigg documentation site](https://docs.friggframework.org/api-modules/list/zoom) \ No newline at end of file diff --git a/packages/zoom/api.js b/packages/zoom/api.js index 622d839..5b4a432 100644 --- a/packages/zoom/api.js +++ b/packages/zoom/api.js @@ -1,5 +1,4 @@ const {get, OAuth2Requester} = require('@friggframework/core'); -const moment = require('moment'); class Api extends OAuth2Requester { constructor(params) { @@ -8,7 +7,7 @@ class Api extends OAuth2Requester { this.baseUrl = `https://api.zoom.us/v2/`; this.client_id = process.env.ZOOM_CLIENT_ID; this.client_secret = process.env.ZOOM_CLIENT_SECRET; - this.redirect_uri = 'https://29a3f7035dbb.ngrok.io'; + this.redirect_uri = process.env.REDIRECT_URI; this.authorizationUri = encodeURI( `https://zoom.us/oauth/authorize?client_id=${this.client_id}&response_type=code&redirect_uri=${this.redirect_uri}` @@ -67,8 +66,7 @@ class Api extends OAuth2Requester { async createNewMeeting(userId, topic) { console.log('createNewMeeting'); let url = `users/${userId}/meetings`; - let time = moment().format(); - let startTime = time.slice(0, -6) + 'Z'; + let startTime = new Date().toISOString(); let body = { topic: topic, type: 2, diff --git a/packages/zoom/definition.js b/packages/zoom/definition.js new file mode 100644 index 0000000..43b57e2 --- /dev/null +++ b/packages/zoom/definition.js @@ -0,0 +1,48 @@ +require('dotenv').config(); +const {Api} = require('./api'); +const {get} = require("@friggframework/core"); +const config = require('./defaultConfig.json') + +const Definition = { + API: Api, + getName: function () { + return config.name + }, + moduleName: config.name, + modelName: 'Zoom', + requiredAuthMethods: { + getToken: async function (api, params) { + const code = get(params.data, 'code'); + return api.getTokenFromCode(code); + }, + getEntityDetails: async function (api, callbackParams, tokenResponse, userId) { + const userDetails = await api.getUserDetails(); + return { + identifiers: {externalId: userDetails.portalId, user: userId}, + details: {name: userDetails.hub_domain}, + } + }, + apiPropertiesToPersist: { + credential: [ + 'access_token', 'refresh_token' + ], + entity: [], + }, + getCredentialDetails: async function (api, userId) { + const userDetails = await api.getUserDetails(); + return { + identifiers: {externalId: userDetails.portalId, user: userId}, + details: {} + }; + }, + testAuthRequest: async function (api) { + return api.getUserDetails() + }, + }, + env: { + client_id: process.env.ZOOM_CLIENT_ID, + client_secret: process.env.ZOOM_CLIENT_SECRET, + } +}; + +module.exports = {Definition}; diff --git a/packages/zoom/jest-setup.js b/packages/zoom/jest-setup.js index 9dd3e0d..9d3161d 100644 --- a/packages/zoom/jest-setup.js +++ b/packages/zoom/jest-setup.js @@ -1,2 +1,3 @@ const {globalSetup} = require('@friggframework/test'); +require('dotenv').config(); module.exports = globalSetup; diff --git a/packages/zoom/manager.js b/packages/zoom/manager.js deleted file mode 100644 index a5ce189..0000000 --- a/packages/zoom/manager.js +++ /dev/null @@ -1,80 +0,0 @@ -const {get, ModuleManager} = require('@friggframework/core'); -const {Api} = require('./api'); -const {Entity} = require('./models/entity'); -const {Credential} = require('./models/credential'); -const Config = require('./defaultConfig.json'); - -class Manager extends ModuleManager { - static Entity = Entity; - static Credential = Credential; - - constructor(params) { - super(params); - } - - //------------------------------------------------------------ - // Required methods - static getName() { - return Config.name; - } - - static async getInstance(params) { - console.log(`getInstance: params=${params}`); - let instance = new this(params); - - const zoomParams = {delegate: instance}; - instance.api = await new Api(zoomParams); - - return instance; - } - - async getAuthorizationRequirements(params) { - return { - url: this.api.getAuthorizationUri(), - type: 'oauth2', - }; - } - - async processAuthorizationCallback(params) { - const code = get(params.data, 'code'); - await this.getAccessToken(code); - const userDetails = await this.api.getUserDetails(); - - await this.findOrCreateEntity({ - portalId: userDetails.portalId, - domainName: userDetails.hub_domain, - }); - return { - entity_id: this.entity.id, - credential_id: this.credential.id, - type: Manager.getName(), - }; - } - - async getEntityOptions() { - console.log('getEntityOptions'); - - let options = []; - return options; - } - - async findOrCreateEntity(data) { - console.log('findOrCreateEntity'); - } - - //------------------------------------------------------------ - - async deauthorize() { - console.log('deauthorize'); - } - - async send(notifier, delegateString, object = null) { - console.log('send'); - } - - async mark_credentials_invalid() { - console.log('mark_credentials_invalid'); - } -} - -module.exports = Manager; diff --git a/packages/zoom/manager.test.js b/packages/zoom/manager.test.js deleted file mode 100644 index b4c8e08..0000000 --- a/packages/zoom/manager.test.js +++ /dev/null @@ -1,26 +0,0 @@ -const Manager = require('./manager'); -const mongoose = require('mongoose'); -const config = require('./defaultConfig.json'); - -describe(`Should fully test the ${config.label} Manager`, () => { - let manager, userManager; - - beforeAll(async () => { - await mongoose.connect(process.env.MONGO_URI); - manager = await Manager.getInstance({ - userId: new mongoose.Types.ObjectId(), - }); - }); - - afterAll(async () => { - await Manager.Credential.deleteMany(); - await Manager.Entity.deleteMany(); - await mongoose.disconnect(); - }); - - it('should return auth requirements', async () => { - const requirements = await manager.getAuthorizationRequirements(); - expect(requirements).exists; - expect(requirements.type).toEqual('oauth2'); - }); -}); diff --git a/packages/zoom/models/credential.js b/packages/zoom/models/credential.js deleted file mode 100644 index 98dedef..0000000 --- a/packages/zoom/models/credential.js +++ /dev/null @@ -1,13 +0,0 @@ -const {Credential: Parent} = require('@friggframework/core'); -'use strict'; -const mongoose = require('mongoose'); - -const schema = new mongoose.Schema({ - access_token: {type: String, trim: true, lhEncrypt: true}, - refresh_token: {type: String, trim: true, lhEncrypt: true}, -}); - -const name = 'ZoomCredential'; -const Credential = - Parent.discriminators?.[name] || Parent.discriminator(name, schema); -module.exports = {Credential}; diff --git a/packages/zoom/models/entity.js b/packages/zoom/models/entity.js deleted file mode 100644 index 2cfb040..0000000 --- a/packages/zoom/models/entity.js +++ /dev/null @@ -1,9 +0,0 @@ -const {Entity: Parent} = require('@friggframework/core'); -'use strict'; -const mongoose = require('mongoose'); - -const schema = new mongoose.Schema({}); -const name = 'ZoomEntity'; -const Entity = - Parent.discriminators?.[name] || Parent.discriminator(name, schema); -module.exports = {Entity}; diff --git a/packages/zoom/package.json b/packages/zoom/package.json index a40fbdb..4e10599 100644 --- a/packages/zoom/package.json +++ b/packages/zoom/package.json @@ -18,7 +18,7 @@ "jest": "^29.7.0", "prettier": "^3.2.5" }, - "dependencies": { - "@friggframework/core": "^1.1.6" - } + "dependencies": { + "@friggframework/core": "^1.1.6" + } } diff --git a/packages/zoom/tests/api.test.js b/packages/zoom/tests/api.test.js new file mode 100644 index 0000000..aa532fe --- /dev/null +++ b/packages/zoom/tests/api.test.js @@ -0,0 +1,114 @@ +const {Api} = require('../api'); +const config = require('../defaultConfig.json'); +const {randomBytes} = require('crypto'); + +const apiParams = { + client_id: process.env.ZOOM_CLIENT_ID, + client_secret: process.env.ZOOM_CLIENT_SECRET, + redirect_uri: process.env.REDIRECT_URI +}; +const api = new Api(apiParams); + +const getRandomId = () => randomBytes(10).toString('hex'); + +describe(`${config.label} Zoom API tests`, () => { + + beforeEach(() => { + jest.clearAllMocks(); + }); + + // Constructor + describe('Constructor', () => { + it('Should initialize with expected properties', () => { + expect(api.client_id).toBe(process.env.ZOOM_CLIENT_ID); + expect(api.client_secret).toBe(process.env.ZOOM_CLIENT_SECRET); + expect(api.redirect_uri).toBe(process.env.REDIRECT_URI); + expect(api.baseUrl).toBe('https://api.zoom.us/v2/'); + }); + }); + + // User List + describe('Get user list', () => { + it('Should call _get with the proper URL', async () => { + const mockResponse = getRandomId(); + api._get = jest.fn().mockResolvedValue(mockResponse); + const response = await api.getUserList(); + expect(api._get).toHaveBeenCalledWith({ + url: api.baseUrl + 'users?status=active' + }); + expect(response).toEqual(mockResponse); + }); + }); + + // Meeting List by User + describe('Get meeting list by user', () => { + it('Should call _get with the proper URL', async () => { + const mockResponse = getRandomId(); + api._get = jest.fn().mockResolvedValue(mockResponse); + const userId = getRandomId(); + const response = await api.getMeetingListByUser(userId); + expect(api._get).toHaveBeenCalledWith({ + url: api.baseUrl + `users/${userId}/meetings` + }); + expect(response).toEqual(mockResponse); + }); + }); + + // Meeting Details + describe('Get meeting details', () => { + it('Should call _get with the proper URL', async () => { + const mockResponse = getRandomId(); + api._get = jest.fn().mockResolvedValue(mockResponse); + const meetingId = getRandomId(); + const response = await api.getMeetingDetails(meetingId); + expect(api._get).toHaveBeenCalledWith({ + url: api.baseUrl + `meetings/${meetingId}/` + }); + expect(response).toEqual(mockResponse); + }); + }); + + // Change Meeting Topic + describe('Change meeting topic', () => { + it('Should call _authedPatch with the proper URL and body', async () => { + const mockResponse = getRandomId(); + api._authedPatch = jest.fn().mockResolvedValue(mockResponse); + const meetingId = getRandomId(); + const topic = "Updated Topic"; + const response = await api.changeMeetingTopic(meetingId, topic); + expect(api._authedPatch).toHaveBeenCalledWith(`meetings/${meetingId}/`, {topic}); + expect(response).toEqual(mockResponse); + }); + }); + + // Create New Meeting + describe('Create new meeting', () => { + it('Should call _authedPost with the proper URL and body', async () => { + const mockResponse = getRandomId(); + api._authedPost = jest.fn().mockResolvedValue(mockResponse); + const userId = getRandomId(); + const topic = "New Meeting"; + const response = await api.createNewMeeting(userId, topic); + expect(api._authedPost).toHaveBeenCalledWith(`users/${userId}/meetings`, { + topic: topic, + type: 2, + start_time: expect.any(String), + duration: 1440, + timezone: 'America/New_York', + }); + expect(response).toEqual(mockResponse); + }); + }); + + // Delete Meeting + describe('Delete meeting', () => { + it('Should call _authedDelete with the proper URL', async () => { + const mockResponse = getRandomId(); + api._authedDelete = jest.fn().mockResolvedValue(mockResponse); + const meetingId = getRandomId(); + const response = await api.deleteMeeting(meetingId); + expect(api._authedDelete).toHaveBeenCalledWith(`meetings/${meetingId}`); + expect(response).toEqual(mockResponse); + }); + }); +}); diff --git a/packages/zoom/tests/auther.test.js b/packages/zoom/tests/auther.test.js new file mode 100644 index 0000000..4e3e55b --- /dev/null +++ b/packages/zoom/tests/auther.test.js @@ -0,0 +1,125 @@ +const {connectToDatabase, disconnectFromDatabase, createObjectId, Auther} = require('@friggframework/core'); +const {Authenticator, testAutherDefinition} = require('@friggframework/devtools'); +const {Definition} = require('../definition'); + + +const mocks = { + getUserDetails: { + "portalId": 111111111, + "timeZone": "US/Eastern", + "accountType": "DEVELOPER_TEST", + "currency": "USD", + "utcOffset": "-05:00", + "utcOffsetMilliseconds": -18000000, + "token": "test-token", + "user": "projectteam@lefthook.co", + "hub_domain": "Testing Object Things-dev-44613847.com", + "scopes": [ + "content", + "oauth", + "crm.objects.contacts.read", + "crm.objects.contacts.write", + "crm.objects.companies.write", + "crm.objects.companies.read", + "crm.objects.deals.read", + "crm.schemas.deals.read" + ], + "hub_id": 111111111, + "app_id": 22222222, + "expires_in": 1704, + "user_id": 33333333, + "token_type": "access" + }, + tokenResponse: { + "token_type": "bearer", + "refresh_token": "test-refresh-token", + "access_token": "test-access-token", + "expires_in": 1800 + }, + authorizeResponse: { + "base": "/redirect/zoom", + "data": { + "code": "test-code", + "state": "null" + } + } +} + +testAutherDefinition(Definition, mocks) + +describe.skip('Zoom Module Live Tests', () => { + let module, authUrl; + beforeAll(async () => { + await connectToDatabase(); + module = await Auther.getInstance({ + definition: Definition, + userId: createObjectId(), + }); + }); + + afterAll(async () => { + await module.CredentialModel.deleteMany(); + await module.EntityModel.deleteMany(); + await disconnectFromDatabase(); + }); + + describe('getAuthorizationRequirements() test', () => { + it('should return auth requirements', async () => { + const requirements = module.getAuthorizationRequirements(); + expect(requirements).toBeDefined(); + expect(requirements.type).toEqual('oauth2'); + expect(requirements.url).toBeDefined(); + authUrl = requirements.url; + }); + }); + + describe('Authorization requests', () => { + let firstRes; + it('processAuthorizationCallback()', async () => { + const response = await Authenticator.oauth2(authUrl); + firstRes = await module.processAuthorizationCallback({ + data: { + code: response.data.code, + }, + }); + expect(firstRes).toBeDefined(); + expect(firstRes.entity_id).toBeDefined(); + expect(firstRes.credential_id).toBeDefined(); + }); + it.skip('retrieves existing entity on subsequent calls', async () => { + const response = await Authenticator.oauth2(authUrl); + const res = await module.processAuthorizationCallback({ + data: { + code: response.data.code, + }, + }); + expect(res).toEqual(firstRes); + }); + }); + describe('Test credential retrieval and module instantiation', () => { + it('retrieve by entity id', async () => { + const newModule = await Auther.getInstance({ + userId: module.userId, + entityId: module.entity.id, + definition: Definition, + }); + expect(newModule).toBeDefined(); + expect(newModule.entity).toBeDefined(); + expect(newModule.credential).toBeDefined(); + expect(await newModule.testAuth()).toBeTruthy(); + + }); + + it('retrieve by credential id', async () => { + const newModule = await Auther.getInstance({ + userId: module.userId, + credentialId: module.credential.id, + definition: Definition, + }); + expect(newModule).toBeDefined(); + expect(newModule.credential).toBeDefined(); + expect(await newModule.testAuth()).toBeTruthy(); + + }); + }); +}); From abb29d34c2204511ea95ed33aac03f34dff052d7 Mon Sep 17 00:00:00 2001 From: Igor Schechtel Date: Mon, 27 May 2024 19:33:22 -0300 Subject: [PATCH 3/5] Indentation fix --- packages/zoom/api.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zoom/api.js b/packages/zoom/api.js index 5b4a432..1238708 100644 --- a/packages/zoom/api.js +++ b/packages/zoom/api.js @@ -66,7 +66,7 @@ class Api extends OAuth2Requester { async createNewMeeting(userId, topic) { console.log('createNewMeeting'); let url = `users/${userId}/meetings`; - let startTime = new Date().toISOString(); + let startTime = new Date().toISOString(); let body = { topic: topic, type: 2, From a2fb172ffe110e0ee641148c0c7e76b40439d0b4 Mon Sep 17 00:00:00 2001 From: Igor Schechtel Date: Thu, 30 May 2024 20:12:36 -0300 Subject: [PATCH 4/5] Remove unneeded attribution of redirect_uri --- packages/zoom/api.js | 1 - packages/zoom/tests/api.test.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/zoom/api.js b/packages/zoom/api.js index 1238708..7db9768 100644 --- a/packages/zoom/api.js +++ b/packages/zoom/api.js @@ -7,7 +7,6 @@ class Api extends OAuth2Requester { this.baseUrl = `https://api.zoom.us/v2/`; this.client_id = process.env.ZOOM_CLIENT_ID; this.client_secret = process.env.ZOOM_CLIENT_SECRET; - this.redirect_uri = process.env.REDIRECT_URI; this.authorizationUri = encodeURI( `https://zoom.us/oauth/authorize?client_id=${this.client_id}&response_type=code&redirect_uri=${this.redirect_uri}` diff --git a/packages/zoom/tests/api.test.js b/packages/zoom/tests/api.test.js index aa532fe..170b6f6 100644 --- a/packages/zoom/tests/api.test.js +++ b/packages/zoom/tests/api.test.js @@ -11,7 +11,7 @@ const api = new Api(apiParams); const getRandomId = () => randomBytes(10).toString('hex'); -describe(`${config.label} Zoom API tests`, () => { +describe(`${config.label} API tests`, () => { beforeEach(() => { jest.clearAllMocks(); From e189b49dbdc2e2d94ce7b432d9922478cd8a008e Mon Sep 17 00:00:00 2001 From: michael webber Date: Wed, 26 Jun 2024 12:30:45 -0400 Subject: [PATCH 5/5] add userDetails method and url update definition to gather data from userDetails add live tests to confirm all features remove token from code request --- packages/zoom/api.js | 12 +++++ packages/zoom/definition.js | 7 +-- packages/zoom/tests/auther.test.js | 76 ++++++++++++++++++------------ 3 files changed, 63 insertions(+), 32 deletions(-) diff --git a/packages/zoom/api.js b/packages/zoom/api.js index 7db9768..2541a8b 100644 --- a/packages/zoom/api.js +++ b/packages/zoom/api.js @@ -12,6 +12,10 @@ class Api extends OAuth2Requester { `https://zoom.us/oauth/authorize?client_id=${this.client_id}&response_type=code&redirect_uri=${this.redirect_uri}` ); + this.URLs = { + user: 'users/me', + } + this.tokenUri = `https://zoom.us/oauth/token`; this.access_token = get(params, 'access_token', null); @@ -19,9 +23,17 @@ class Api extends OAuth2Requester { } async getTokenFromCode(code) { + delete this.access_token; return await this.getTokenFromCodeBasicAuthHeader(code); } + async getUserDetails() { + const options = { + url: this.baseUrl + this.URLs.user, + } + return this._get(options); + } + async getUserList() { console.log('getUserList'); let options = { diff --git a/packages/zoom/definition.js b/packages/zoom/definition.js index 43b57e2..aa4d153 100644 --- a/packages/zoom/definition.js +++ b/packages/zoom/definition.js @@ -18,8 +18,8 @@ const Definition = { getEntityDetails: async function (api, callbackParams, tokenResponse, userId) { const userDetails = await api.getUserDetails(); return { - identifiers: {externalId: userDetails.portalId, user: userId}, - details: {name: userDetails.hub_domain}, + identifiers: {externalId: userDetails.id, user: userId}, + details: {name: userDetails.display_name}, } }, apiPropertiesToPersist: { @@ -31,7 +31,7 @@ const Definition = { getCredentialDetails: async function (api, userId) { const userDetails = await api.getUserDetails(); return { - identifiers: {externalId: userDetails.portalId, user: userId}, + identifiers: {externalId: userDetails.id, user: userId}, details: {} }; }, @@ -42,6 +42,7 @@ const Definition = { env: { client_id: process.env.ZOOM_CLIENT_ID, client_secret: process.env.ZOOM_CLIENT_SECRET, + redirect_uri: `${process.env.REDIRECT_URI}/zoom`, } }; diff --git a/packages/zoom/tests/auther.test.js b/packages/zoom/tests/auther.test.js index 4e3e55b..3d426b1 100644 --- a/packages/zoom/tests/auther.test.js +++ b/packages/zoom/tests/auther.test.js @@ -1,40 +1,52 @@ const {connectToDatabase, disconnectFromDatabase, createObjectId, Auther} = require('@friggframework/core'); -const {Authenticator, testAutherDefinition} = require('@friggframework/devtools'); +const {testAutherDefinition} = require('@friggframework/devtools'); +const {Authenticator} = require('@friggframework/test'); const {Definition} = require('../definition'); const mocks = { getUserDetails: { - "portalId": 111111111, - "timeZone": "US/Eastern", - "accountType": "DEVELOPER_TEST", - "currency": "USD", - "utcOffset": "-05:00", - "utcOffsetMilliseconds": -18000000, - "token": "test-token", - "user": "projectteam@lefthook.co", - "hub_domain": "Testing Object Things-dev-44613847.com", - "scopes": [ - "content", - "oauth", - "crm.objects.contacts.read", - "crm.objects.contacts.write", - "crm.objects.companies.write", - "crm.objects.companies.read", - "crm.objects.deals.read", - "crm.schemas.deals.read" + "id": "", + "first_name": "Jane", + "last_name": "Doe", + "display_name": "Jane Doe", + "email": "jane.doe@lefthook.com", + "type": 1, + "role_name": "Owner", + "pmi": 0, + "use_pmi": false, + "personal_meeting_url": "https://us04web.zoom.us/j/redacted?pwd=redacted", + "timezone": "", + "verified": 0, + "dept": "", + "created_at": "2023-07-26T14:16:21Z", + "last_login_time": "2024-06-26T15:58:30Z", + "last_client_version": "5.17.11.34827(win)", + "pic_url": "https://us04web.zoom.us/p/v2/", + "cms_user_id": "", + "jid": "test@xmpp.zoom.us", + "group_ids": [], + "im_group_ids": [], + "account_id": "", + "language": "en-US", + "phone_country": "", + "phone_number": "", + "status": "active", + "job_title": "", + "location": "", + "login_types": [ + 1 ], - "hub_id": 111111111, - "app_id": 22222222, - "expires_in": 1704, - "user_id": 33333333, - "token_type": "access" + "role_id": "0", + "cluster": "us04", + "user_created_at": "2023-07-26T14:16:21Z" }, tokenResponse: { + "access_token": "redacted", "token_type": "bearer", - "refresh_token": "test-refresh-token", - "access_token": "test-access-token", - "expires_in": 1800 + "refresh_token": "redacted", + "expires_in": 3599, + "scope": "user:read:user user:read:email meeting:read:list_meetings meeting:write:meeting meeting:update:meeting" }, authorizeResponse: { "base": "/redirect/zoom", @@ -65,7 +77,7 @@ describe.skip('Zoom Module Live Tests', () => { describe('getAuthorizationRequirements() test', () => { it('should return auth requirements', async () => { - const requirements = module.getAuthorizationRequirements(); + const requirements = await module.getAuthorizationRequirements(); expect(requirements).toBeDefined(); expect(requirements.type).toEqual('oauth2'); expect(requirements.url).toBeDefined(); @@ -86,7 +98,8 @@ describe.skip('Zoom Module Live Tests', () => { expect(firstRes.entity_id).toBeDefined(); expect(firstRes.credential_id).toBeDefined(); }); - it.skip('retrieves existing entity on subsequent calls', async () => { + it('retrieves existing entity on subsequent calls', async () => { + await new Promise(resolve => setTimeout(resolve, 1000)); const response = await Authenticator.oauth2(authUrl); const res = await module.processAuthorizationCallback({ data: { @@ -95,6 +108,11 @@ describe.skip('Zoom Module Live Tests', () => { }); expect(res).toEqual(firstRes); }); + it('refresh the token', async () => { + module.api.access_token = 'foobar'; + const res = await module.testAuth(); + expect(res).toBeTruthy(); + }); }); describe('Test credential retrieval and module instantiation', () => { it('retrieve by entity id', async () => {