Complete v1 loop for bot-based integrations

This commit is contained in:
Tuan Dang
2022-12-08 23:22:44 -05:00
parent 46fe724012
commit 1757f0d690
33 changed files with 1085 additions and 638 deletions
+77
View File
@@ -0,0 +1,77 @@
import axios from 'axios';
import * as Sentry from '@sentry/node';
import {
INTEGRATION_HEROKU,
INTEGRATION_HEROKU_APPS_URL
} from '../variables';
/**
* Return list of names of apps for integration named [integration]
* @param {Object} obj
* @param {String} obj.integration - name of integration
* @param {String} obj.accessToken - access token for integration
* @returns {Object[]} apps - names of integration apps
* @returns {String} apps.name - name of integration app
*/
const getApps = async ({
integration,
accessToken
}: {
integration: string;
accessToken: string;
}) => {
let apps;
try {
switch (integration) {
case INTEGRATION_HEROKU:
apps = await getAppsHeroku({
accessToken
});
break;
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to get integration apps');
}
return apps;
}
/**
* Return list of names of apps for Heroku integration
* @param {Object} obj
* @param {String} obj.accessToken - access token for Heroku API
* @returns {Object[]} apps - names of Heroku apps
* @returns {String} apps.name - name of Heroku app
*/
const getAppsHeroku = async ({
accessToken
}: {
accessToken: string;
}) => {
let apps;
try {
const res = await axios.get(INTEGRATION_HEROKU_APPS_URL, {
headers: {
Accept: 'application/vnd.heroku+json; version=3',
Authorization: `Bearer ${accessToken}`
}
});
apps = res.data.map((a: any) => ({
name: a.name
}));
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to get Heroku integration apps');
}
return apps;
}
export {
getApps
}
+5 -6
View File
@@ -2,11 +2,11 @@ import axios from 'axios';
import * as Sentry from '@sentry/node';
import {
INTEGRATION_HEROKU,
INTEGRATION_HEROKU_TOKEN_URL,
ACTION_PUSH_TO_HEROKU
} from '../variables';
import {
OAUTH_CLIENT_SECRET_HEROKU,
OAUTH_TOKEN_URL_HEROKU
OAUTH_CLIENT_SECRET_HEROKU
} from '../config';
/**
@@ -29,13 +29,13 @@ const exchangeCode = async ({
code: string;
}) => {
let obj = {} as any;
try {
switch (integration) {
case INTEGRATION_HEROKU:
obj = await exchangeCodeHeroku({
code
});
obj['action'] = ACTION_PUSH_TO_HEROKU;
break;
}
} catch (err) {
@@ -63,10 +63,10 @@ const exchangeCodeHeroku = async ({
code: string;
}) => {
let res: any;
let accessExpiresAt: any;
let accessExpiresAt = new Date();
try {
res = await axios.post(
OAUTH_TOKEN_URL_HEROKU!,
INTEGRATION_HEROKU_TOKEN_URL,
new URLSearchParams({
grant_type: 'authorization_code',
code: code,
@@ -78,7 +78,6 @@ const exchangeCodeHeroku = async ({
accessExpiresAt.getSeconds() + res.data.expires_in
);
} catch (err) {
console.error('integrationHerokuExchange');
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Heroku');
+5 -1
View File
@@ -1,7 +1,11 @@
import { exchangeCode } from './exchange';
import { exchangeRefresh } from './refresh';
import { getApps } from './apps';
import { syncSecrets } from './sync';
export {
exchangeCode,
exchangeRefresh
exchangeRefresh,
getApps,
syncSecrets
}
+2 -2
View File
@@ -5,7 +5,7 @@ import {
OAUTH_CLIENT_SECRET_HEROKU
} from '../config';
import {
OAUTH_TOKEN_URL_HEROKU
INTEGRATION_HEROKU_TOKEN_URL
} from '../variables';
/**
@@ -55,7 +55,7 @@ const exchangeRefreshHeroku = async ({
let accessToken;
try {
const res = await axios.post(
OAUTH_TOKEN_URL_HEROKU,
INTEGRATION_HEROKU_TOKEN_URL,
new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
+76
View File
@@ -0,0 +1,76 @@
import axios from 'axios';
import * as Sentry from '@sentry/node';
import { INTEGRATION_HEROKU } from '../variables';
/**
* Sync/push [secrets] to [app] in integration named [integration]
* @param {Object} obj
* @param {Object} obj.integration - name of integration
* @param {Object} obj.app - app in integration
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
* @param {String} obj.accessToken - access token for integration
*/
const syncSecrets = async ({
integration,
app,
secrets,
accessToken
}: {
integration: string;
app: string;
secrets: any;
accessToken: string;
}) => {
try {
switch (integration) {
case INTEGRATION_HEROKU:
await syncSecretsHeroku({
app,
secrets,
accessToken
});
break;
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to sync secrets to integration');
}
}
/**
* Sync/push [secrets] to Heroku [app]
* @param {Object} obj
* @param {String} obj.app - app in integration
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
*/
const syncSecretsHeroku = async ({
app,
secrets,
accessToken
}: {
app: string;
secrets: any;
accessToken: string;
}) => {
try {
await axios.patch(
`https://api.heroku.com/apps/${app}/config-vars`,
secrets,
{
headers: {
Accept: 'application/vnd.heroku+json; version=3',
Authorization: 'Bearer ' + accessToken
}
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to sync secrets to Heroku');
}
}
export {
syncSecrets
}