From 314787f39ce55a1d68f14ca382b3cd7d228f9d41 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Sun, 16 Oct 2022 19:49:45 +0200 Subject: [PATCH] refactor: clean up github and rewrite its auth --- .../src/apps/github/auth/create-auth-data.ts | 21 + .../backend/src/apps/github/auth/index.ts | 221 ++++++ .../src/apps/github/auth/is-still-verified.ts | 13 + .../apps/github/auth/verify-credentials.ts | 59 ++ .../backend/src/apps/github/authentication.ts | 95 --- .../apps/github/common/generate-request.ts | 28 + .../apps/github/common/get-current-user.ts | 14 + .../github/common/get-repo-owner-and-repo.ts | 13 + packages/backend/src/apps/github/data.ts | 16 - .../src/apps/github/data/list-branches.ts | 36 - .../src/apps/github/data/list-labels.ts | 36 - .../src/apps/github/data/list-repos.ts | 23 - packages/backend/src/apps/github/index.ts | 35 +- packages/backend/src/apps/github/info.json | 747 ------------------ packages/backend/src/apps/github/triggers.ts | 46 -- .../src/apps/github/triggers/new-branch.ts | 42 - .../apps/github/triggers/new-collaborator.ts | 44 -- .../github/triggers/new-commit-comment.ts | 59 -- .../src/apps/github/triggers/new-commit.ts | 59 -- .../src/apps/github/triggers/new-issue.ts | 88 --- .../src/apps/github/triggers/new-label.ts | 44 -- .../src/apps/github/triggers/new-milestone.ts | 60 -- .../apps/github/triggers/new-notification.ts | 83 -- .../apps/github/triggers/new-organization.ts | 32 - .../apps/github/triggers/new-pull-request.ts | 63 -- .../src/apps/github/triggers/new-release.ts | 59 -- .../apps/github/triggers/new-repository.ts | 32 - .../src/apps/github/triggers/new-watcher.ts | 45 -- packages/backend/src/apps/github/utils.ts | 10 - .../apps/twitter/auth/is-still-verified.ts | 4 +- packages/backend/src/apps/twitter/index.ts | 3 +- .../src/graphql/queries/get-connected-apps.ts | 7 +- .../backend/src/helpers/global-variable.ts | 2 +- packages/backend/src/models/app.ts | 7 +- packages/backend/tsconfig.json | 1 - packages/docs/pages/.vitepress/config.js | 6 +- packages/types/index.d.ts | 1 + 37 files changed, 398 insertions(+), 1756 deletions(-) create mode 100644 packages/backend/src/apps/github/auth/create-auth-data.ts create mode 100644 packages/backend/src/apps/github/auth/index.ts create mode 100644 packages/backend/src/apps/github/auth/is-still-verified.ts create mode 100644 packages/backend/src/apps/github/auth/verify-credentials.ts delete mode 100644 packages/backend/src/apps/github/authentication.ts create mode 100644 packages/backend/src/apps/github/common/generate-request.ts create mode 100644 packages/backend/src/apps/github/common/get-current-user.ts create mode 100644 packages/backend/src/apps/github/common/get-repo-owner-and-repo.ts delete mode 100644 packages/backend/src/apps/github/data.ts delete mode 100644 packages/backend/src/apps/github/data/list-branches.ts delete mode 100644 packages/backend/src/apps/github/data/list-labels.ts delete mode 100644 packages/backend/src/apps/github/data/list-repos.ts delete mode 100644 packages/backend/src/apps/github/info.json delete mode 100644 packages/backend/src/apps/github/triggers.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-branch.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-collaborator.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-commit-comment.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-commit.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-issue.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-label.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-milestone.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-notification.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-organization.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-pull-request.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-release.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-repository.ts delete mode 100644 packages/backend/src/apps/github/triggers/new-watcher.ts delete mode 100644 packages/backend/src/apps/github/utils.ts diff --git a/packages/backend/src/apps/github/auth/create-auth-data.ts b/packages/backend/src/apps/github/auth/create-auth-data.ts new file mode 100644 index 00000000..ed1c9c95 --- /dev/null +++ b/packages/backend/src/apps/github/auth/create-auth-data.ts @@ -0,0 +1,21 @@ +import { IField, IGlobalVariable } from '@automatisch/types'; +import { URLSearchParams } from 'url'; + +export default async function createAuthData($: IGlobalVariable) { + const scopes = ['read:org', 'repo', 'user']; + const oauthRedirectUrlField = $.app.auth.fields.find( + (field: IField) => field.key == 'oAuthRedirectUrl' + ); + const redirectUri = oauthRedirectUrlField.value as string; + const searchParams = new URLSearchParams({ + client_id: $.auth.data.consumerKey as string, + redirect_uri: redirectUri, + scope: scopes.join(','), + }); + + const url = `${$.app.baseUrl}/login/oauth/authorize?${searchParams.toString()}`; + + await $.auth.set({ + url, + }); +} diff --git a/packages/backend/src/apps/github/auth/index.ts b/packages/backend/src/apps/github/auth/index.ts new file mode 100644 index 00000000..2ceb0c5a --- /dev/null +++ b/packages/backend/src/apps/github/auth/index.ts @@ -0,0 +1,221 @@ +import createAuthData from './create-auth-data'; +import verifyCredentials from './verify-credentials'; +import isStillVerified from './is-still-verified'; + +export default { + fields: [ + { + key: 'oAuthRedirectUrl', + label: 'OAuth Redirect URL', + type: 'string', + required: true, + readOnly: true, + value: '{WEB_APP_URL}/app/github/connections/add', + placeholder: null, + description: 'When asked to input an OAuth callback or redirect URL in Github OAuth, enter the URL above.', + docUrl: 'https://automatisch.io/docs/github#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/github#client-id', + clickToCopy: false + }, + { + key: 'consumerSecret', + label: 'Client Secret', + type: 'string', + required: true, + readOnly: false, + value: null, + placeholder: null, + description: null, + docUrl: 'https://automatisch.io/docs/github#client-secret', + clickToCopy: false + } + ], + authenticationSteps: [ + { + step: 1, + type: 'mutation', + name: 'createConnection', + arguments: [ + { + name: 'key', + value: '{key}' + }, + { + name: 'formattedData', + 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: 'formattedData', + 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: 'formattedData', + 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: 'formattedData', + value: null, + properties: [ + { + name: 'oauthVerifier', + value: '{openAuthPopup.code}' + } + ] + } + ] + }, + { + step: 6, + type: 'mutation', + name: 'verifyConnection', + arguments: [ + { + name: 'id', + value: '{connection.id}' + } + ] + } + ], + + createAuthData, + verifyCredentials, + isStillVerified, +}; diff --git a/packages/backend/src/apps/github/auth/is-still-verified.ts b/packages/backend/src/apps/github/auth/is-still-verified.ts new file mode 100644 index 00000000..8f578344 --- /dev/null +++ b/packages/backend/src/apps/github/auth/is-still-verified.ts @@ -0,0 +1,13 @@ +import { IGlobalVariable } from '@automatisch/types'; +import getCurrentUser from '../common/get-current-user'; + +const isStillVerified = async ($: IGlobalVariable) => { + try { + const user = await getCurrentUser($); + return !!user; + } catch (error) { + return false; + } +}; + +export default isStillVerified; diff --git a/packages/backend/src/apps/github/auth/verify-credentials.ts b/packages/backend/src/apps/github/auth/verify-credentials.ts new file mode 100644 index 00000000..6c0e7d57 --- /dev/null +++ b/packages/backend/src/apps/github/auth/verify-credentials.ts @@ -0,0 +1,59 @@ +import { IGlobalVariable } from '@automatisch/types'; +import getCurrentUser from '../common/get-current-user'; + +async function getTokenInfo($: IGlobalVariable) { + const basicAuthToken = Buffer.from( + $.auth.data.consumerKey + ':' + $.auth.data.consumerSecret + ).toString('base64'); + + const headers = { + Authorization: `Basic ${basicAuthToken}`, + }; + + const body = { + access_token: $.auth.data.accessToken, + }; + + return await $.http.post( + `${$.app.baseUrl}/applications/${$.auth.data.consumerKey}/token`, + body, + { headers } + ); +} + +const verifyCredentials = async ($: IGlobalVariable) => { + try { + const response = await $.http.post( + `${$.app.baseUrl}/login/oauth/access_token`, + { + client_id: $.auth.data.consumerKey, + client_secret: $.auth.data.consumerSecret, + code: $.auth.data.oauthVerifier, + }, + { + headers: { + Accept: 'application/json' + } + }); + + const data = response.data; + + $.auth.data.accessToken = data.access_token; + + const currentUser = await getCurrentUser($); + + await $.auth.set({ + consumerKey: $.auth.data.consumerKey, + consumerSecret: $.auth.data.consumerSecret, + accessToken: data.access_token, + scope: data.scope, + tokenType: data.token_type, + userId: currentUser.id, + screenName: currentUser.login, + }); + } catch (error) { + throw new Error(error.response.data); + } +}; + +export default verifyCredentials; diff --git a/packages/backend/src/apps/github/authentication.ts b/packages/backend/src/apps/github/authentication.ts deleted file mode 100644 index 64d2f0af..00000000 --- a/packages/backend/src/apps/github/authentication.ts +++ /dev/null @@ -1,95 +0,0 @@ -import type { - IAuthentication, - IApp, - IField, - IJSONObject, -} from '@automatisch/types'; -import createHttpClient, { IHttpClient } from '../../helpers/http-client'; -import { URLSearchParams } from 'url'; - -export default class Authentication implements IAuthentication { - appData: IApp; - connectionData: IJSONObject; - scopes: string[] = ['read:org', 'repo', 'user']; - client: IHttpClient; - - constructor(appData: IApp, connectionData: IJSONObject) { - this.connectionData = connectionData; - this.appData = appData; - this.client = createHttpClient({ baseURL: 'https://github.com' }); - } - - get oauthRedirectUrl(): string { - return this.appData.fields.find( - (field: IField) => field.key == 'oAuthRedirectUrl' - ).value; - } - - async createAuthData(): Promise<{ url: string }> { - const searchParams = new URLSearchParams({ - client_id: this.connectionData.consumerKey as string, - redirect_uri: this.oauthRedirectUrl, - scope: this.scopes.join(','), - }); - - const url = `https://github.com/login/oauth/authorize?${searchParams.toString()}`; - - return { - url, - }; - } - - async verifyCredentials() { - const response = await this.client.post('/login/oauth/access_token', { - client_id: this.connectionData.consumerKey, - client_secret: this.connectionData.consumerSecret, - code: this.connectionData.oauthVerifier, - }); - - const data = Object.fromEntries(new URLSearchParams(response.data)); - - this.connectionData.accessToken = data.access_token; - - const tokenInfo = await this.getTokenInfo(); - - return { - consumerKey: this.connectionData.consumerKey, - consumerSecret: this.connectionData.consumerSecret, - accessToken: data.access_token, - scope: data.scope, - tokenType: data.token_type, - userId: tokenInfo.data.user.id, - screenName: tokenInfo.data.user.login, - }; - } - - async getTokenInfo() { - const basicAuthToken = Buffer.from( - this.connectionData.consumerKey + ':' + this.connectionData.consumerSecret - ).toString('base64'); - - const headers = { - Authorization: `Basic ${basicAuthToken}`, - }; - - const body = { - access_token: this.connectionData.accessToken, - }; - - return await this.client.post( - `https://api.github.com/applications/${this.connectionData.consumerKey}/token`, - body, - { headers } - ); - } - - async isStillVerified() { - try { - await this.getTokenInfo(); - - return true; - } catch { - return false; - } - } -} diff --git a/packages/backend/src/apps/github/common/generate-request.ts b/packages/backend/src/apps/github/common/generate-request.ts new file mode 100644 index 00000000..e78dfc0b --- /dev/null +++ b/packages/backend/src/apps/github/common/generate-request.ts @@ -0,0 +1,28 @@ +import { IGlobalVariable, IJSONObject } from '@automatisch/types'; +import { Method } from 'axios'; + +type IGenereateRequestOptons = { + requestPath: string; + method: string; + data?: IJSONObject; +}; + +const generateRequest = async ( + $: IGlobalVariable, + options: IGenereateRequestOptons +) => { + const { requestPath, method, data } = options; + + const response = await $.http.request({ + url: requestPath, + method: method as Method, + data, + headers: { + Authorization: `Bearer ${$.auth.data.accessToken}` + }, + }); + + return response; +}; + +export default generateRequest; diff --git a/packages/backend/src/apps/github/common/get-current-user.ts b/packages/backend/src/apps/github/common/get-current-user.ts new file mode 100644 index 00000000..00295ebb --- /dev/null +++ b/packages/backend/src/apps/github/common/get-current-user.ts @@ -0,0 +1,14 @@ +import { IGlobalVariable, IJSONObject } from '@automatisch/types'; +import generateRequest from './generate-request'; + +const getCurrentUser = async ($: IGlobalVariable): Promise => { + const response = await generateRequest($, { + requestPath: '/user', + method: 'GET', + }); + + const currentUser = response.data; + return currentUser; +}; + +export default getCurrentUser; diff --git a/packages/backend/src/apps/github/common/get-repo-owner-and-repo.ts b/packages/backend/src/apps/github/common/get-repo-owner-and-repo.ts new file mode 100644 index 00000000..ccd89667 --- /dev/null +++ b/packages/backend/src/apps/github/common/get-repo-owner-and-repo.ts @@ -0,0 +1,13 @@ +type TRepoOwnerAndRepo = { + repoOwner: string; + repo: string; +} + +export function getRepoOwnerAndRepo(repoFullName: string): TRepoOwnerAndRepo { + const [repoOwner, repo] = repoFullName.split('/'); + + return { + repoOwner, + repo + }; +} diff --git a/packages/backend/src/apps/github/data.ts b/packages/backend/src/apps/github/data.ts deleted file mode 100644 index 12bd3afe..00000000 --- a/packages/backend/src/apps/github/data.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IJSONObject } from '@automatisch/types'; -import ListRepos from './data/list-repos'; -import ListBranches from './data/list-branches'; -import ListLabels from './data/list-labels'; - -export default class Data { - listRepos: ListRepos; - listBranches: ListBranches; - listLabels: ListLabels; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - this.listRepos = new ListRepos(connectionData); - this.listBranches = new ListBranches(connectionData, parameters); - this.listLabels = new ListLabels(connectionData, parameters); - } -} diff --git a/packages/backend/src/apps/github/data/list-branches.ts b/packages/backend/src/apps/github/data/list-branches.ts deleted file mode 100644 index f77b6610..00000000 --- a/packages/backend/src/apps/github/data/list-branches.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Octokit } from 'octokit'; -import type { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class ListBranches { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters?: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - }; - } - - async run() { - const branches = await this.client.paginate(this.client.rest.repos.listBranches, this.options); - - return branches.map((branch) => ({ - value: branch.name, - name: branch.name, - })); - } -} diff --git a/packages/backend/src/apps/github/data/list-labels.ts b/packages/backend/src/apps/github/data/list-labels.ts deleted file mode 100644 index bc1ff192..00000000 --- a/packages/backend/src/apps/github/data/list-labels.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Octokit } from 'octokit'; -import type { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class ListLabels { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters?: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - }; - } - - async run() { - const labels = await this.client.paginate(this.client.rest.issues.listLabelsForRepo, this.options); - - return labels.map((label) => ({ - value: label.name, - name: label.name, - })); - } -} diff --git a/packages/backend/src/apps/github/data/list-repos.ts b/packages/backend/src/apps/github/data/list-repos.ts deleted file mode 100644 index 39e3ac44..00000000 --- a/packages/backend/src/apps/github/data/list-repos.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Octokit } from 'octokit'; -import type { IJSONObject } from '@automatisch/types'; - -export default class ListRepos { - client?: Octokit; - - constructor(connectionData: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - } - - async run() { - const repos = await this.client.paginate(this.client.rest.repos.listForAuthenticatedUser); - - return repos.map((repo) => ({ - value: repo.full_name, - name: repo.full_name, - })); - } -} diff --git a/packages/backend/src/apps/github/index.ts b/packages/backend/src/apps/github/index.ts index 72f6b28e..0e4bbf48 100644 --- a/packages/backend/src/apps/github/index.ts +++ b/packages/backend/src/apps/github/index.ts @@ -1,25 +1,10 @@ -import { - IService, - IAuthentication, - IApp, - IJSONObject, -} from '@automatisch/types'; -import Authentication from './authentication'; -import Triggers from './triggers'; -import Data from './data'; - -export default class Github implements IService { - authenticationClient: IAuthentication; - triggers: Triggers; - data: Data; - - constructor( - appData: IApp, - connectionData: IJSONObject, - parameters: IJSONObject - ) { - this.authenticationClient = new Authentication(appData, connectionData); - this.data = new Data(connectionData, parameters); - this.triggers = new Triggers(connectionData, parameters); - } -} +export default { + name: 'Github', + key: 'github', + baseUrl: 'https://github.com', + apiBaseUrl: 'https://api.github.com', + iconUrl: '{BASE_URL}/apps/github/assets/favicon.svg', + authDocUrl: 'https://automatisch.io/docs/connections/github', + primaryColor: '000000', + supportsConnections: true, +}; diff --git a/packages/backend/src/apps/github/info.json b/packages/backend/src/apps/github/info.json deleted file mode 100644 index 8e9db0e2..00000000 --- a/packages/backend/src/apps/github/info.json +++ /dev/null @@ -1,747 +0,0 @@ -{ - "name": "Github", - "key": "github", - "iconUrl": "{BASE_URL}/apps/github/assets/favicon.svg", - "docUrl": "https://automatisch.io/docs/github", - "primaryColor": "000000", - "supportsConnections": true, - "fields": [ - { - "key": "oAuthRedirectUrl", - "label": "OAuth Redirect URL", - "type": "string", - "required": true, - "readOnly": true, - "value": "{WEB_APP_URL}/app/github/connections/add", - "placeholder": null, - "description": "When asked to input an OAuth callback or redirect URL in Github OAuth, enter the URL above.", - "docUrl": "https://automatisch.io/docs/github#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/github#client-id", - "clickToCopy": false - }, - { - "key": "consumerSecret", - "label": "Client Secret", - "type": "string", - "required": true, - "readOnly": false, - "value": null, - "placeholder": null, - "description": null, - "docUrl": "https://automatisch.io/docs/github#client-secret", - "clickToCopy": false - } - ], - "authenticationSteps": [ - { - "step": 1, - "type": "mutation", - "name": "createConnection", - "arguments": [ - { - "name": "key", - "value": "{key}" - }, - { - "name": "formattedData", - "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": "formattedData", - "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": "formattedData", - "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": "formattedData", - "value": null, - "properties": [ - { - "name": "oauthVerifier", - "value": "{openAuthPopup.code}" - } - ] - } - ] - }, - { - "step": 6, - "type": "mutation", - "name": "verifyConnection", - "arguments": [ - { - "name": "id", - "value": "{connection.id}" - } - ] - } - ], - "triggers": [ - { - "name": "New repository", - "key": "newRepository", - "description": "Triggers when a new repository is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New organization", - "key": "newOrganization", - "description": "Triggers when a new organization is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New branch", - "key": "newBranch", - "description": "Triggers when a new branch is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New notification", - "key": "newNotification", - "description": "Triggers when a new notification is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": false, - "variables": false, - "description": "If blank, we will retrieve all notifications.", - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New pull request", - "key": "newPullRequest", - "description": "Triggers when a new pull request is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New watcher", - "key": "newWatcher", - "description": "Triggers when a new watcher is added to a repo", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New milestone", - "key": "newMilestone", - "description": "Triggers when a new milestone is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New commit comment", - "key": "newCommitComment", - "description": "Triggers when a new commit comment is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New label", - "key": "newLabel", - "description": "Triggers when a new label is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New collaborator", - "key": "newCollaborator", - "description": "Triggers when a new collaborator is added to a repo", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New release", - "key": "newRelease", - "description": "Triggers when a new release is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New commit", - "key": "newCommit", - "description": "Triggers when a new commit is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": true, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - }, - { - "label": "Head", - "key": "head", - "type": "dropdown", - "description": "Branch to pull commits from. If unspecified, will use the repository's default branch (usually main or develop).", - "required": false, - "variables": false, - "dependsOn": ["parameters.repo"], - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listBranches" - }, - { - "name": "parameters.repo", - "value": "{parameters.repo}" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - }, - { - "name": "New issue", - "key": "newIssue", - "description": "Triggers when a new issue is created", - "substeps": [ - { - "key": "chooseConnection", - "name": "Choose connection" - }, - { - "key": "chooseTrigger", - "name": "Set up a trigger", - "arguments": [ - { - "label": "Repo", - "key": "repo", - "type": "dropdown", - "required": false, - "variables": false, - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listRepos" - } - ] - } - }, - { - "label": "Which types of issues should this trigger on?", - "key": "issueType", - "type": "dropdown", - "description": "Defaults to any issue you can see.", - "required": true, - "variables": false, - "value": "all", - "options": [ - { - "label": "Any issue you can see", - "value": "all" - }, - { - "label": "Only issues assigned to you", - "value": "assigned" - }, - { - "label": "Only issues created by you", - "value": "created" - }, - { - "label": "Only issues you're mentioned in", - "value": "mentioned" - }, - { - "label": "Only issues you're subscribed to", - "value": "subscribed" - } - ] - }, - { - "label": "Label", - "key": "label", - "type": "dropdown", - "description": "Only trigger on issues when this label is added.", - "required": false, - "variables": false, - "dependsOn": ["parameters.repo"], - "source": { - "type": "query", - "name": "getData", - "arguments": [ - { - "name": "key", - "value": "listLabels" - }, - { - "name": "parameters.repo", - "value": "{parameters.repo}" - } - ] - } - } - ] - }, - { - "key": "testStep", - "name": "Test trigger" - } - ] - } - ] -} diff --git a/packages/backend/src/apps/github/triggers.ts b/packages/backend/src/apps/github/triggers.ts deleted file mode 100644 index 8a49f6e1..00000000 --- a/packages/backend/src/apps/github/triggers.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { IJSONObject } from '@automatisch/types'; -import NewRepository from './triggers/new-repository'; -import NewOrganization from './triggers/new-organization'; -import NewBranch from './triggers/new-branch'; -import NewNotification from './triggers/new-notification'; -import NewPullRequest from './triggers/new-pull-request'; -import NewWatcher from './triggers/new-watcher'; -import NewMilestone from './triggers/new-milestone'; -import NewCommit from './triggers/new-commit'; -import NewCommitComment from './triggers/new-commit-comment'; -import NewLabel from './triggers/new-label'; -import NewCollaborator from './triggers/new-collaborator'; -import NewRelease from './triggers/new-release'; -import NewIssue from './triggers/new-issue'; - -export default class Triggers { - newRepository: NewRepository; - newOrganization: NewOrganization; - newBranch: NewBranch; - newNotification: NewNotification; - newPullRequest: NewPullRequest; - newWatcher: NewWatcher; - newMilestone: NewMilestone; - newCommit: NewCommit; - newCommitComment: NewCommitComment; - newLabel: NewLabel; - newCollaborator: NewCollaborator; - newRelease: NewRelease; - newIssue: NewIssue; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - this.newRepository = new NewRepository(connectionData); - this.newOrganization = new NewOrganization(connectionData); - this.newBranch = new NewBranch(connectionData, parameters); - this.newNotification = new NewNotification(connectionData, parameters); - this.newPullRequest = new NewPullRequest(connectionData, parameters); - this.newWatcher = new NewWatcher(connectionData, parameters); - this.newMilestone = new NewMilestone(connectionData, parameters); - this.newCommit = new NewCommit(connectionData, parameters); - this.newCommitComment = new NewCommitComment(connectionData, parameters); - this.newLabel = new NewLabel(connectionData, parameters); - this.newCollaborator = new NewCollaborator(connectionData, parameters); - this.newRelease = new NewRelease(connectionData, parameters); - this.newIssue = new NewIssue(connectionData, parameters); - } -} diff --git a/packages/backend/src/apps/github/triggers/new-branch.ts b/packages/backend/src/apps/github/triggers/new-branch.ts deleted file mode 100644 index d434877d..00000000 --- a/packages/backend/src/apps/github/triggers/new-branch.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Octokit } from 'octokit'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewBranch { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - }; - } - - async run() { - // TODO: implement pagination on undated entires - return await this.client.paginate(this.client.rest.repos.listBranches, this.options); - } - - async testRun() { - const options = { - ...this.options, - per_page: 1, - }; - const { data: branches } = await this.client.rest.repos.listBranches(options); - - return branches; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-collaborator.ts b/packages/backend/src/apps/github/triggers/new-collaborator.ts deleted file mode 100644 index 76329324..00000000 --- a/packages/backend/src/apps/github/triggers/new-collaborator.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Octokit } from 'octokit'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewCollaborator { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - }; - } - - async run() { - // TODO: implement pagination on undated entries - return await this.client.paginate( - this.client.rest.repos.listCollaborators, - this.options - ); - } - - async testRun() { - const options = { - ...this.options, - per_page: 1, - }; - - return (await this.client.rest.repos.listCollaborators(options)).data; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-commit-comment.ts b/packages/backend/src/apps/github/triggers/new-commit-comment.ts deleted file mode 100644 index 73f8d757..00000000 --- a/packages/backend/src/apps/github/triggers/new-commit-comment.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Octokit } from 'octokit'; -import { DateTime } from 'luxon'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewCommitComment { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - }; - } - - async run(startTime: Date) { - const iterator = await this.client.paginate.iterator(this.client.rest.repos.listCommitCommentsForRepo, this.options); - const newCommitComments = []; - - const startTimeDateObject = DateTime.fromJSDate(startTime); - - commitCommentIterator: - for await (const { data: commitComments } of iterator) { - for (const commitComment of commitComments) { - const createdAtDateObject = DateTime.fromISO(commitComment.created_at); - - if (createdAtDateObject < startTimeDateObject) { - break commitCommentIterator; - } - - newCommitComments.push(commitComment); - } - } - - return newCommitComments; - } - - async testRun() { - const options = { - ...this.options, - per_page: 1, - }; - - return (await this.client.rest.repos.listCommitCommentsForRepo(options)).data; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-commit.ts b/packages/backend/src/apps/github/triggers/new-commit.ts deleted file mode 100644 index aeec4f3f..00000000 --- a/packages/backend/src/apps/github/triggers/new-commit.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Octokit } from 'octokit'; -import { DateTime } from 'luxon'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewCommit { - client?: Octokit; - repoOwner?: string; - repo?: string; - head?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - if (parameters?.head) { - this.head = parameters.head as string; - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - const options = { - owner: this.repoOwner, - repo: this.repo, - }; - - if (this.head) { - return { - ...options, - sha: this.head, - }; - } - - return options; - } - - async run(startTime: Date) { - const options = { - ...this.options, - since: DateTime.fromJSDate(startTime).toISO(), - }; - return await this.client.paginate(this.client.rest.repos.listCommits, options); - } - - async testRun() { - const options = { - ...this.options, - per_page: 1, - }; - - return (await this.client.rest.repos.listCommits(options)).data; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-issue.ts b/packages/backend/src/apps/github/triggers/new-issue.ts deleted file mode 100644 index bbcba4bf..00000000 --- a/packages/backend/src/apps/github/triggers/new-issue.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Octokit } from 'octokit'; -import { DateTime } from 'luxon'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewIssue { - client?: Octokit; - connectionData?: IJSONObject; - repoOwner?: string; - repo?: string; - hasRepo?: boolean; - label?: string; - issueType?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - labels: this.label, - } - } - - async listRepoIssues(options = {}, paginate = false) { - const listRepoIssues = this.client.rest.issues.listForRepo; - - const extendedOptions = { - ...this.options, - repo: this.repo, - owner: this.repoOwner, - filter: this.issueType, - ...options, - }; - - if (paginate) { - return await this.client.paginate(listRepoIssues, extendedOptions); - } - - return (await listRepoIssues(extendedOptions)).data; - } - - async listIssues(options = {}, paginate = false) { - const listIssues = this.client.rest.issues.listForAuthenticatedUser; - - const extendedOptions = { - ...this.options, - ...options, - }; - - if (paginate) { - return await this.client.paginate(listIssues, extendedOptions); - } - - return (await listIssues(extendedOptions)).data; - } - - async run(startTime: Date) { - const options = { - since: DateTime.fromJSDate(startTime).toISO(), - }; - - if (this.hasRepo) { - return await this.listRepoIssues(options, true); - } - - return await this.listIssues(options, true); - } - - async testRun() { - const options = { - per_page: 1, - }; - - if (this.hasRepo) { - return await this.listRepoIssues(options, false); - } - - return await this.listIssues(options, false); - } -} diff --git a/packages/backend/src/apps/github/triggers/new-label.ts b/packages/backend/src/apps/github/triggers/new-label.ts deleted file mode 100644 index 061c0bb7..00000000 --- a/packages/backend/src/apps/github/triggers/new-label.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Octokit } from 'octokit'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewLabel { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - }; - } - - async run() { - // TODO: implement pagination on undated entires - return await this.client.paginate( - this.client.rest.issues.listLabelsForRepo, - this.options - ); - } - - async testRun() { - const options = { - ...this.options, - per_page: 1, - }; - - return (await this.client.rest.issues.listLabelsForRepo(options)).data; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-milestone.ts b/packages/backend/src/apps/github/triggers/new-milestone.ts deleted file mode 100644 index a8c017a7..00000000 --- a/packages/backend/src/apps/github/triggers/new-milestone.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Octokit } from 'octokit'; -import { DateTime } from 'luxon'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewMilestone { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - state: 'open' as const, - }; - } - - async run(startTime: Date) { - const iterator = await this.client.paginate.iterator(this.client.rest.issues.listMilestones, this.options); - const newMilestones = []; - - const startTimeDateObject = DateTime.fromJSDate(startTime); - - milestoneIterator: - for await (const { data: milestones } of iterator) { - for (const milestone of milestones) { - const createdAtDateObject = DateTime.fromISO(milestone.created_at); - - if (createdAtDateObject < startTimeDateObject) { - break milestoneIterator; - } - - newMilestones.push(milestone); - } - } - - return newMilestones; - } - - async testRun() { - const options = { - ...this.options, - per_page: 1, - }; - - return (await this.client.rest.issues.listMilestones(options)).data; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-notification.ts b/packages/backend/src/apps/github/triggers/new-notification.ts deleted file mode 100644 index 31f9cb3b..00000000 --- a/packages/backend/src/apps/github/triggers/new-notification.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Octokit } from 'octokit'; -import { DateTime } from 'luxon'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewNotification { - client?: Octokit; - connectionData?: IJSONObject; - repoOwner?: string; - repo?: string; - hasRepo?: boolean; - baseOptions = { - all: true, - participating: false, - }; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - async listRepoNotifications(options = {}, paginate = false) { - const listRepoNotifications = this.client.rest.activity.listRepoNotificationsForAuthenticatedUser; - - const extendedOptions = { - ...this.baseOptions, - repo: this.repo, - owner: this.repoOwner, - ...options, - }; - - if (paginate) { - return await this.client.paginate(listRepoNotifications, extendedOptions); - } - - return (await listRepoNotifications(extendedOptions)).data; - } - - async listNotifications(options = {}, paginate = false) { - const listNotifications = this.client.rest.activity.listNotificationsForAuthenticatedUser; - - const extendedOptions = { - ...this.baseOptions, - ...options, - }; - - if (paginate) { - return await this.client.paginate(listNotifications, extendedOptions); - } - - return (await listNotifications(extendedOptions)).data; - } - - async run(startTime: Date) { - const options = { - since: DateTime.fromJSDate(startTime).toISO(), - }; - - if (this.hasRepo) { - return await this.listRepoNotifications(options, true); - } - - return await this.listNotifications(options, true); - } - - async testRun() { - const options = { - per_page: 1, - }; - - if (this.hasRepo) { - return await this.listRepoNotifications(options, false); - } - - return await this.listNotifications(options, false); - } -} diff --git a/packages/backend/src/apps/github/triggers/new-organization.ts b/packages/backend/src/apps/github/triggers/new-organization.ts deleted file mode 100644 index da5b10f7..00000000 --- a/packages/backend/src/apps/github/triggers/new-organization.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Octokit } from 'octokit'; -import { IJSONObject } from '@automatisch/types'; - -export default class NewOrganization { - client?: Octokit; - baseOptions = { - per_page: 100, - }; - - constructor(connectionData: IJSONObject) { - if ( - connectionData.consumerKey && - connectionData.consumerSecret && - connectionData.accessToken - ) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - } - - async run() { - // TODO: implement pagination on undated entires - return await this.client.paginate(this.client.rest.orgs.listForAuthenticatedUser); - } - - async testRun() { - const { data: orgs } = await this.client.rest.orgs.listForAuthenticatedUser({ per_page: 1 }); - - return orgs; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-pull-request.ts b/packages/backend/src/apps/github/triggers/new-pull-request.ts deleted file mode 100644 index 103ea880..00000000 --- a/packages/backend/src/apps/github/triggers/new-pull-request.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Octokit } from 'octokit'; -import { DateTime } from 'luxon'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewPullRequest { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - sort: 'created' as const, - direction: 'desc' as const, - }; - } - - async run(startTime: Date) { - const iterator = await this.client.paginate.iterator( - this.client.rest.pulls.list, - this.options - ); - const newPullRequests = []; - - const startTimeDateObject = DateTime.fromJSDate(startTime); - - pullRequestIterator: for await (const { data: pullRequests } of iterator) { - for (const pullRequest of pullRequests) { - const createdAtDateObject = DateTime.fromISO(pullRequest.created_at); - - if (createdAtDateObject < startTimeDateObject) { - break pullRequestIterator; - } - - newPullRequests.push(pullRequest); - } - } - - return newPullRequests; - } - - async testRun() { - const options = { - ...this.options, - per_page: 1, - }; - - return (await this.client.rest.pulls.list(options)).data; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-release.ts b/packages/backend/src/apps/github/triggers/new-release.ts deleted file mode 100644 index 1626b2e1..00000000 --- a/packages/backend/src/apps/github/triggers/new-release.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Octokit } from 'octokit'; -import { DateTime } from 'luxon'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewRelease { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - }; - } - - async run(startTime: Date) { - const iterator = await this.client.paginate.iterator(this.client.rest.repos.listReleases, this.options); - const newReleases = []; - - const startTimeDateObject = DateTime.fromJSDate(startTime); - - releaseIterator: - for await (const { data: releases } of iterator) { - for (const release of releases) { - const createdAtDateObject = DateTime.fromISO(release.created_at); - - if (createdAtDateObject < startTimeDateObject) { - break releaseIterator; - } - - newReleases.push(release); - } - } - - return newReleases; - } - - async testRun() { - const options = { - ...this.options, - per_page: 1, - }; - - return (await this.client.rest.repos.listReleases(options)).data; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-repository.ts b/packages/backend/src/apps/github/triggers/new-repository.ts deleted file mode 100644 index 2652de37..00000000 --- a/packages/backend/src/apps/github/triggers/new-repository.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Octokit } from 'octokit'; -import { DateTime } from 'luxon'; -import { IJSONObject } from '@automatisch/types'; - -export default class NewRepository { - client?: Octokit; - connectionData?: IJSONObject; - - constructor(connectionData: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - } - - async run(startTime: Date) { - const options = { - since: DateTime.fromJSDate(startTime).toISO(), - }; - return await this.client.paginate(this.client.rest.repos.listForAuthenticatedUser, options); - } - - async testRun() { - const options = { - per_page: 1, - }; - const { data: repos } = await this.client.rest.repos.listForAuthenticatedUser(options); - - return repos; - } -} diff --git a/packages/backend/src/apps/github/triggers/new-watcher.ts b/packages/backend/src/apps/github/triggers/new-watcher.ts deleted file mode 100644 index 3a6e2f20..00000000 --- a/packages/backend/src/apps/github/triggers/new-watcher.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Octokit } from 'octokit'; -import { IJSONObject } from '@automatisch/types'; - -import { assignOwnerAndRepo } from '../utils'; - -export default class NewWatcher { - client?: Octokit; - repoOwner?: string; - repo?: string; - - constructor(connectionData: IJSONObject, parameters: IJSONObject) { - if (connectionData.accessToken) { - this.client = new Octokit({ - auth: connectionData.accessToken as string, - }); - } - - assignOwnerAndRepo(this, parameters?.repo as string); - } - - get options() { - return { - owner: this.repoOwner, - repo: this.repo, - }; - } - - async run() { - // TODO: implement pagination on undated entries - return await this.client.paginate( - this.client.rest.activity.listWatchersForRepo, - this.options - ); - } - - async testRun() { - return await this.run(); - const options = { - ...this.options, - per_page: 1, - }; - - return (await this.client.rest.activity.listWatchersForRepo(options)).data; - } -} diff --git a/packages/backend/src/apps/github/utils.ts b/packages/backend/src/apps/github/utils.ts deleted file mode 100644 index 6e22ee32..00000000 --- a/packages/backend/src/apps/github/utils.ts +++ /dev/null @@ -1,10 +0,0 @@ -export function assignOwnerAndRepo(object: T, repoFullName: string): T { - if (object && repoFullName) { - const [repoOwner, repo] = repoFullName.split('/'); - object.repoOwner = repoOwner; - object.repo = repo; - object.hasRepo = true; - } - - return object; -} diff --git a/packages/backend/src/apps/twitter/auth/is-still-verified.ts b/packages/backend/src/apps/twitter/auth/is-still-verified.ts index 6ecd7ecd..8f578344 100644 --- a/packages/backend/src/apps/twitter/auth/is-still-verified.ts +++ b/packages/backend/src/apps/twitter/auth/is-still-verified.ts @@ -3,8 +3,8 @@ import getCurrentUser from '../common/get-current-user'; const isStillVerified = async ($: IGlobalVariable) => { try { - await getCurrentUser($); - return true; + const user = await getCurrentUser($); + return !!user; } catch (error) { return false; } diff --git a/packages/backend/src/apps/twitter/index.ts b/packages/backend/src/apps/twitter/index.ts index 0f0ef9a4..d97c5d4b 100644 --- a/packages/backend/src/apps/twitter/index.ts +++ b/packages/backend/src/apps/twitter/index.ts @@ -4,5 +4,6 @@ export default { iconUrl: '{BASE_URL}/apps/twitter/assets/favicon.svg', authDocUrl: 'https://automatisch.io/docs/connections/twitter', supportsConnections: true, - baseUrl: 'https://api.twitter.com', + baseUrl: 'https://twitter.com', + apiBaseUrl: 'https://api.twitter.com', }; diff --git a/packages/backend/src/graphql/queries/get-connected-apps.ts b/packages/backend/src/graphql/queries/get-connected-apps.ts index a988e60c..c1ee32b1 100644 --- a/packages/backend/src/graphql/queries/get-connected-apps.ts +++ b/packages/backend/src/graphql/queries/get-connected-apps.ts @@ -34,10 +34,10 @@ const getConnectedApps = async ( const usedApps = [...new Set([...duplicatedUsedApps, ...connectionKeys])]; apps = apps - .filter((app: IApp) => { + .filter((app) => { return usedApps.includes(app.key); }) - .map((app: IApp) => { + .map((app) => { const connection = connections.find( (connection) => (connection as IConnection).key === app.key ); @@ -54,7 +54,8 @@ const getConnectedApps = async ( }); return app; - }); + }) + .sort((appA, appB) => appA.name.localeCompare(appB.name)); return apps; }; diff --git a/packages/backend/src/helpers/global-variable.ts b/packages/backend/src/helpers/global-variable.ts index e30d212e..7d93644c 100644 --- a/packages/backend/src/helpers/global-variable.ts +++ b/packages/backend/src/helpers/global-variable.ts @@ -41,7 +41,7 @@ const globalVariable = async ( data: connection?.formattedData, }, app: app, - http: createHttpClient({ baseURL: app.baseUrl }), + http: createHttpClient({ baseURL: app.apiBaseUrl }), flow: { id: flow?.id, lastInternalId, diff --git a/packages/backend/src/models/app.ts b/packages/backend/src/models/app.ts index 2a52c810..0aa34d35 100644 --- a/packages/backend/src/models/app.ts +++ b/packages/backend/src/models/app.ts @@ -10,7 +10,12 @@ class App { // Temporaryly restrict the apps we expose until // their actions/triggers are implemented! - static temporaryList = ['slack', 'twitter', 'scheduler']; + static temporaryList = [ + 'github', + 'scheduler', + 'slack', + 'twitter', + ]; static async findAll(name?: string, stripFuncs = true): Promise { if (!name) diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index 0f5693bd..a4be27dd 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -26,7 +26,6 @@ "src/apps/discord", "src/apps/firebase", "src/apps/flickr", - "src/apps/github", "src/apps/gitlab", "src/apps/twitch", "src/apps/typeform" diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index 21ca4903..65f30503 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -29,11 +29,11 @@ export default defineConfig({ text: 'Connections', collapsible: true, items: [ - { text: 'Twitter', link: '/connections/twitter' }, - { text: 'Slack', link: '/connections/slack' }, + { text: 'Github', link: '/connections/github' }, { text: 'Scheduler', link: '/connections/scheduler' }, + { text: 'Slack', link: '/connections/slack' }, + { text: 'Twitter', link: '/connections/twitter' }, // Temporarily disable following pages until we release github and typeform integrations - // { text: 'Github', link: '/connections/github' }, // { text: 'Typeform', link: '/connections/typeform' }, ], }, diff --git a/packages/types/index.d.ts b/packages/types/index.d.ts index a404dd1f..4c771e6d 100644 --- a/packages/types/index.d.ts +++ b/packages/types/index.d.ts @@ -157,6 +157,7 @@ export interface IApp { authDocUrl: string; primaryColor: string; supportsConnections: boolean; + apiBaseUrl: string; baseUrl: string; auth: IAuth; connectionCount: number;