Merge pull request #614 from automatisch/github-auth
refactor: clean up github and rewrite its auth
This commit is contained in:
21
packages/backend/src/apps/github/auth/create-auth-data.ts
Normal file
21
packages/backend/src/apps/github/auth/create-auth-data.ts
Normal file
@@ -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,
|
||||
});
|
||||
}
|
221
packages/backend/src/apps/github/auth/index.ts
Normal file
221
packages/backend/src/apps/github/auth/index.ts
Normal file
@@ -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,
|
||||
};
|
13
packages/backend/src/apps/github/auth/is-still-verified.ts
Normal file
13
packages/backend/src/apps/github/auth/is-still-verified.ts
Normal file
@@ -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;
|
59
packages/backend/src/apps/github/auth/verify-credentials.ts
Normal file
59
packages/backend/src/apps/github/auth/verify-credentials.ts
Normal file
@@ -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;
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
28
packages/backend/src/apps/github/common/generate-request.ts
Normal file
28
packages/backend/src/apps/github/common/generate-request.ts
Normal file
@@ -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;
|
14
packages/backend/src/apps/github/common/get-current-user.ts
Normal file
14
packages/backend/src/apps/github/common/get-current-user.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
import generateRequest from './generate-request';
|
||||
|
||||
const getCurrentUser = async ($: IGlobalVariable): Promise<IJSONObject> => {
|
||||
const response = await generateRequest($, {
|
||||
requestPath: '/user',
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
const currentUser = response.data;
|
||||
return currentUser;
|
||||
};
|
||||
|
||||
export default getCurrentUser;
|
@@ -0,0 +1,13 @@
|
||||
type TRepoOwnerAndRepo = {
|
||||
repoOwner: string;
|
||||
repo: string;
|
||||
}
|
||||
|
||||
export function getRepoOwnerAndRepo(repoFullName: string): TRepoOwnerAndRepo {
|
||||
const [repoOwner, repo] = repoFullName.split('/');
|
||||
|
||||
return {
|
||||
repoOwner,
|
||||
repo
|
||||
};
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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,
|
||||
}));
|
||||
}
|
||||
}
|
@@ -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,
|
||||
}));
|
||||
}
|
||||
}
|
@@ -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,
|
||||
}));
|
||||
}
|
||||
}
|
@@ -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,
|
||||
};
|
||||
|
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -1,10 +0,0 @@
|
||||
export function assignOwnerAndRepo<T extends { repoOwner?: string; repo?: string; hasRepo?: boolean; }>(object: T, repoFullName: string): T {
|
||||
if (object && repoFullName) {
|
||||
const [repoOwner, repo] = repoFullName.split('/');
|
||||
object.repoOwner = repoOwner;
|
||||
object.repo = repo;
|
||||
object.hasRepo = true;
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
@@ -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',
|
||||
};
|
||||
|
@@ -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;
|
||||
};
|
||||
|
@@ -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,
|
||||
|
@@ -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<IApp[]> {
|
||||
if (!name)
|
||||
|
@@ -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"
|
||||
|
@@ -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' },
|
||||
],
|
||||
},
|
||||
|
1
packages/types/index.d.ts
vendored
1
packages/types/index.d.ts
vendored
@@ -157,6 +157,7 @@ export interface IApp {
|
||||
authDocUrl: string;
|
||||
primaryColor: string;
|
||||
supportsConnections: boolean;
|
||||
apiBaseUrl: string;
|
||||
baseUrl: string;
|
||||
auth: IAuth;
|
||||
connectionCount: number;
|
||||
|
Reference in New Issue
Block a user