From 1a64b745d20972600ee31f2359fe7b5337ed7829 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Thu, 28 Oct 2021 17:53:03 +0200 Subject: [PATCH] feat: Implement typeform connection --- .../src/apps/typeform/assets/favicon.svg | 6 + packages/backend/src/apps/typeform/index.ts | 93 ++++++++ packages/backend/src/apps/typeform/info.json | 218 ++++++++++++++++++ 3 files changed, 317 insertions(+) create mode 100644 packages/backend/src/apps/typeform/assets/favicon.svg create mode 100644 packages/backend/src/apps/typeform/index.ts create mode 100644 packages/backend/src/apps/typeform/info.json diff --git a/packages/backend/src/apps/typeform/assets/favicon.svg b/packages/backend/src/apps/typeform/assets/favicon.svg new file mode 100644 index 00000000..56381390 --- /dev/null +++ b/packages/backend/src/apps/typeform/assets/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/backend/src/apps/typeform/index.ts b/packages/backend/src/apps/typeform/index.ts new file mode 100644 index 00000000..11b13bb3 --- /dev/null +++ b/packages/backend/src/apps/typeform/index.ts @@ -0,0 +1,93 @@ +import { URLSearchParams } from 'url'; +import axios, { AxiosInstance } from + 'axios'; +import App from '../../models/app'; +import Field from '../../types/field'; + +export default class Typeform { + client?: any + connectionData: any + appData: any + scope: string[] = [ + 'forms:read', + 'forms:write', + 'webhooks:read', + 'webhooks:write', + 'responses:read', + 'accounts:read', + 'workspaces:read', + ] + httpClient: AxiosInstance = axios.create({ + baseURL: 'https://api.typeform.com' + }) + + constructor(connectionData: any) { + this.connectionData = connectionData; + this.appData = App.findOneByKey('typeform'); + } + + get oauthRedirectUrl() { + return this.appData.fields.find((field: Field) => field.key == 'oAuthRedirectUrl').value; + } + + async createAuthData() { + const searchParams = new URLSearchParams({ + client_id: this.connectionData.consumerKey, + redirect_uri: this.oauthRedirectUrl, + scope: this.scope.join(' '), + }); + + const url = `https://api.typeform.com/oauth/authorize?${searchParams.toString()}`; + + return { url }; + } + + async verifyCredentials() { + const params = new URLSearchParams({ + grant_type: 'authorization_code', + code: this.connectionData.oauthVerifier, + client_id: this.connectionData.consumerKey, + client_secret: this.connectionData.consumerSecret, + redirect_uri: this.oauthRedirectUrl, + }); + + const { data: verifiedCredentials }: any = await this.httpClient.post('/oauth/token', params.toString()); + + const { + access_token: accessToken, + expires_in: expiresIn, + token_type: tokenType, + } = verifiedCredentials; + + const { data: user }: any = await this.httpClient.get('/me', { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + return { + consumerKey: this.connectionData.consumerKey, + consumerSecret: this.connectionData.consumerSecret, + accessToken, + expiresIn, + tokenType, + userId: user.user_id, + screenName: user.alias, + email: user.email, + }; + } + + async isStillVerified() { + try { + await this.httpClient.get('/me', { + headers: { + Authorization: `Bearer ${this.connectionData.accessToken}`, + }, + }); + + return true; + } catch { + return false + } + } +} diff --git a/packages/backend/src/apps/typeform/info.json b/packages/backend/src/apps/typeform/info.json new file mode 100644 index 00000000..9cb796c8 --- /dev/null +++ b/packages/backend/src/apps/typeform/info.json @@ -0,0 +1,218 @@ +{ + "name": "Typeform", + "key": "typeform", + "iconUrl": "{BASE_URL}/apps/typeform/assets/favicon.svg", + "docUrl": "https://automatisch.io/docs/typeform", + "primaryColor": "5865f2", + "fields": [ + { + "key": "oAuthRedirectUrl", + "label": "OAuth Redirect URL", + "type": "string", + "required": true, + "readOnly": true, + "value": "https://localhost:3001/app/typeform/connections/add", + "placeholder": null, + "description": "When asked to input an OAuth callback or redirect URL in Typeform OAuth, enter the URL above.", + "docUrl": "https://automatisch.io/docs/typeform#oauth-redirect-url", + "clickToCopy": true + }, + { + "key": "consumerKey", + "label": "Client ID", + "type": "string", + "required": true, + "readOnly": false, + "value": null, + "placeholder": null, + "description": null, + "docUrl": "https://automatisch.io/docs/typeform#consumer-key", + "clickToCopy": false + }, + { + "key": "consumerSecret", + "label": "Client Secret", + "type": "string", + "required": true, + "readOnly": false, + "value": null, + "placeholder": null, + "description": null, + "docUrl": "https://automatisch.io/docs/typeform#consumer-secret", + "clickToCopy": false + } + ], + "authenticationSteps": [ + { + "step": 1, + "type": "mutation", + "name": "createConnection", + "arguments": [ + { + "name": "key", + "value": "{key}" + }, + { + "name": "data", + "value": null, + "properties": [ + { + "name": "consumerKey", + "value": "{fields.consumerKey}" + }, + { + "name": "consumerSecret", + "value": "{fields.consumerSecret}" + } + ] + } + ] + }, + { + "step": 2, + "type": "mutation", + "name": "createAuthData", + "arguments": [ + { + "name": "id", + "value": "{createConnection.id}" + } + ] + }, + { + "step": 3, + "type": "openWithPopup", + "name": "openAuthPopup", + "arguments": [ + { + "name": "url", + "value": "{createAuthData.url}" + } + ] + }, + { + "step": 4, + "type": "mutation", + "name": "updateConnection", + "arguments": [ + { + "name": "id", + "value": "{createConnection.id}" + }, + { + "name": "data", + "value": null, + "properties": [ + { + "name": "oauthVerifier", + "value": "{openAuthPopup.code}" + } + ] + } + ] + }, + { + "step": 5, + "type": "mutation", + "name": "verifyConnection", + "arguments": [ + { + "name": "id", + "value": "{createConnection.id}" + } + ] + } + ], + "reconnectionSteps": [ + { + "step": 1, + "type": "mutation", + "name": "resetConnection", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + } + ] + }, + { + "step": 2, + "type": "mutation", + "name": "updateConnection", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + }, + { + "name": "data", + "value": null, + "properties": [ + { + "name": "consumerKey", + "value": "{fields.consumerKey}" + }, + { + "name": "consumerSecret", + "value": "{fields.consumerSecret}" + } + ] + } + ] + }, + { + "step": 3, + "type": "mutation", + "name": "createAuthData", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + } + ] + }, + { + "step": 4, + "type": "openWithPopup", + "name": "openAuthPopup", + "arguments": [ + { + "name": "url", + "value": "{createAuthData.url}" + } + ] + }, + { + "step": 5, + "type": "mutation", + "name": "updateConnection", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + }, + { + "name": "data", + "value": null, + "properties": [ + { + "name": "oauthVerifier", + "value": "{openAuthPopup.code}" + } + ] + } + ] + }, + { + "step": 6, + "type": "mutation", + "name": "verifyConnection", + "arguments": [ + { + "name": "id", + "value": "{connection.id}" + } + ] + } + ] +}