feat: remove try-catch blocks for handling errors in middleware

This commit is contained in:
Spelchure
2023-05-01 17:14:39 +03:00
parent addac63700
commit 21eb1815c4
22 changed files with 2141 additions and 2580 deletions
+371 -453
View File
@@ -1,7 +1,6 @@
import * as Sentry from "@sentry/node";
import { Octokit } from "@octokit/rest";
import { IIntegrationAuth } from "../models";
import request from '../config/request';
import request from "../config/request";
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_AWS_PARAMETER_STORE,
@@ -26,7 +25,7 @@ import {
INTEGRATION_FLYIO_API_URL,
INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_TRAVISCI_API_URL,
INTEGRATION_SUPABASE_API_URL
INTEGRATION_SUPABASE_API_URL,
} from "../variables";
interface App {
@@ -47,87 +46,80 @@ interface App {
const getApps = async ({
integrationAuth,
accessToken,
teamId
teamId,
}: {
integrationAuth: IIntegrationAuth;
accessToken: string;
teamId?: string;
}) => {
let apps: App[] = [];
try {
switch (integrationAuth.integration) {
case INTEGRATION_AZURE_KEY_VAULT:
apps = [];
break;
case INTEGRATION_AWS_PARAMETER_STORE:
apps = [];
break;
case INTEGRATION_AWS_SECRET_MANAGER:
apps = [];
break;
case INTEGRATION_HEROKU:
apps = await getAppsHeroku({
accessToken,
});
break;
case INTEGRATION_VERCEL:
apps = await getAppsVercel({
integrationAuth,
accessToken,
});
break;
case INTEGRATION_NETLIFY:
apps = await getAppsNetlify({
accessToken,
});
break;
case INTEGRATION_GITHUB:
apps = await getAppsGithub({
accessToken,
});
break;
case INTEGRATION_GITLAB:
apps = await getAppsGitlab({
accessToken,
teamId
});
break;
case INTEGRATION_RENDER:
apps = await getAppsRender({
accessToken,
});
break;
case INTEGRATION_RAILWAY:
apps = await getAppsRailway({
accessToken
});
break;
case INTEGRATION_FLYIO:
apps = await getAppsFlyio({
accessToken,
});
break;
case INTEGRATION_CIRCLECI:
apps = await getAppsCircleCI({
accessToken,
});
break;
case INTEGRATION_TRAVISCI:
apps = await getAppsTravisCI({
accessToken,
})
break;
case INTEGRATION_SUPABASE:
apps = await getAppsSupabase({
accessToken
});
break;
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get integration apps");
switch (integrationAuth.integration) {
case INTEGRATION_AZURE_KEY_VAULT:
apps = [];
break;
case INTEGRATION_AWS_PARAMETER_STORE:
apps = [];
break;
case INTEGRATION_AWS_SECRET_MANAGER:
apps = [];
break;
case INTEGRATION_HEROKU:
apps = await getAppsHeroku({
accessToken,
});
break;
case INTEGRATION_VERCEL:
apps = await getAppsVercel({
integrationAuth,
accessToken,
});
break;
case INTEGRATION_NETLIFY:
apps = await getAppsNetlify({
accessToken,
});
break;
case INTEGRATION_GITHUB:
apps = await getAppsGithub({
accessToken,
});
break;
case INTEGRATION_GITLAB:
apps = await getAppsGitlab({
accessToken,
teamId,
});
break;
case INTEGRATION_RENDER:
apps = await getAppsRender({
accessToken,
});
break;
case INTEGRATION_RAILWAY:
apps = await getAppsRailway({
accessToken,
});
break;
case INTEGRATION_FLYIO:
apps = await getAppsFlyio({
accessToken,
});
break;
case INTEGRATION_CIRCLECI:
apps = await getAppsCircleCI({
accessToken,
});
break;
case INTEGRATION_TRAVISCI:
apps = await getAppsTravisCI({
accessToken,
});
break;
case INTEGRATION_SUPABASE:
apps = await getAppsSupabase({
accessToken,
});
break;
}
return apps;
@@ -141,25 +133,18 @@ const getApps = async ({
* @returns {String} apps.name - name of Heroku app
*/
const getAppsHeroku = async ({ accessToken }: { accessToken: string }) => {
let apps;
try {
const res = (
await request.get(`${INTEGRATION_HEROKU_API_URL}/apps`, {
headers: {
Accept: "application/vnd.heroku+json; version=3",
Authorization: `Bearer ${accessToken}`,
},
})
).data;
const res = (
await request.get(`${INTEGRATION_HEROKU_API_URL}/apps`, {
headers: {
Accept: "application/vnd.heroku+json; version=3",
Authorization: `Bearer ${accessToken}`,
},
})
).data;
apps = res.map((a: any) => ({
name: a.name,
}));
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get Heroku integration apps");
}
const apps = res.map((a: any) => ({
name: a.name,
}));
return apps;
};
@@ -178,33 +163,26 @@ const getAppsVercel = async ({
integrationAuth: IIntegrationAuth;
accessToken: string;
}) => {
let apps;
try {
const res = (
await request.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, {
headers: {
Authorization: `Bearer ${accessToken}`,
'Accept-Encoding': 'application/json'
},
...(integrationAuth?.teamId
? {
params: {
teamId: integrationAuth.teamId,
},
}
: {}),
})
).data;
const res = (
await request.get(`${INTEGRATION_VERCEL_API_URL}/v9/projects`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
...(integrationAuth?.teamId
? {
params: {
teamId: integrationAuth.teamId,
},
}
: {}),
})
).data;
apps = res.projects.map((a: any) => ({
name: a.name,
appId: a.id
}));
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get Vercel integration apps");
}
const apps = res.projects.map((a: any) => ({
name: a.name,
appId: a.id,
}));
return apps;
};
@@ -218,43 +196,40 @@ const getAppsVercel = async ({
*/
const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
const apps: any = [];
try {
let page = 1;
const perPage = 10;
let hasMorePages = true;
// paginate through all sites
while (hasMorePages) {
const params = new URLSearchParams({
page: String(page),
per_page: String(perPage)
});
let page = 1;
const perPage = 10;
let hasMorePages = true;
const { data } = await request.get(`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`, {
// paginate through all sites
while (hasMorePages) {
const params = new URLSearchParams({
page: String(page),
per_page: String(perPage),
});
const { data } = await request.get(
`${INTEGRATION_NETLIFY_API_URL}/api/v1/sites`,
{
params,
headers: {
Authorization: `Bearer ${accessToken}`,
'Accept-Encoding': 'application/json'
}
});
data.map((a: any) => {
apps.push({
name: a.name,
appId: a.site_id
});
});
if (data.length < perPage) {
hasMorePages = false;
"Accept-Encoding": "application/json",
},
}
);
page++;
data.map((a: any) => {
apps.push({
name: a.name,
appId: a.site_id,
});
});
if (data.length < perPage) {
hasMorePages = false;
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get Netlify integration apps");
page++;
}
return apps;
@@ -268,67 +243,59 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
* @returns {String} apps.name - name of Github site
*/
const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
let apps;
try {
interface GitHubApp {
id: string;
name: string;
permissions: {
admin: boolean;
};
owner: {
login: string;
interface GitHubApp {
id: string;
name: string;
permissions: {
admin: boolean;
};
owner: {
login: string;
};
}
const octokit = new Octokit({
auth: accessToken,
});
const getAllRepos = async () => {
let repos: GitHubApp[] = [];
let page = 1;
const per_page = 100;
let hasMore = true;
while (hasMore) {
const response = await octokit.request(
"GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}",
{
per_page,
page,
}
);
if (response.data.length > 0) {
repos = repos.concat(response.data);
page++;
} else {
hasMore = false;
}
}
const octokit = new Octokit({
auth: accessToken,
return repos;
};
const repos = await getAllRepos();
const apps = repos
.filter((a: GitHubApp) => a.permissions.admin === true)
.map((a: GitHubApp) => {
return {
appId: a.id,
name: a.name,
owner: a.owner.login,
};
});
const getAllRepos = async () => {
let repos: GitHubApp[] = [];
let page = 1;
const per_page = 100;
let hasMore = true;
while (hasMore) {
const response = await octokit.request(
"GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}",
{
per_page,
page,
}
);
if (response.data.length > 0) {
repos = repos.concat(response.data);
page++;
} else {
hasMore = false;
}
}
return repos;
};
const repos = await getAllRepos();
apps = repos
.filter((a: GitHubApp) => a.permissions.admin === true)
.map((a: GitHubApp) => {
return {
appId: a.id,
name: a.name,
owner: a.owner.login,
};
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get Github repos");
}
return apps;
};
@@ -341,29 +308,20 @@ const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
* @returns {String} apps.appId - id of Render service
*/
const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
let apps: any;
try {
const res = (
await request.get(`${INTEGRATION_RENDER_API_URL}/v1/services`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: 'application/json',
'Accept-Encoding': 'application/json',
},
})
).data;
apps = res
.map((a: any) => ({
name: a.service.name,
appId: a.service.id
}));
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get Render services");
}
const res = (
await request.get(`${INTEGRATION_RENDER_API_URL}/v1/services`, {
headers: {
Authorization: `Bearer ${accessToken}`,
Accept: "application/json",
"Accept-Encoding": "application/json",
},
})
).data;
const apps = res.map((a: any) => ({
name: a.service.name,
appId: a.service.id,
}));
return apps;
};
@@ -376,49 +334,51 @@ const getAppsRender = async ({ accessToken }: { accessToken: string }) => {
* @returns {String} apps.name - name of Railway project
* @returns {String} apps.appId - id of Railway project
*
*/
*/
const getAppsRailway = async ({ accessToken }: { accessToken: string }) => {
let apps: any[] = [];
try {
const query = `
query GetProjects($userId: String, $teamId: String) {
projects(userId: $userId, teamId: $teamId) {
edges {
node {
id
name
}
const query = `
query GetProjects($userId: String, $teamId: String) {
projects(userId: $userId, teamId: $teamId) {
edges {
node {
id
name
}
}
}
`;
}
`;
const variables = {};
const variables = {};
const { data: { data: { projects: { edges }}} } = await request.post(INTEGRATION_RAILWAY_API_URL, {
const {
data: {
data: {
projects: { edges },
},
},
} = await request.post(
INTEGRATION_RAILWAY_API_URL,
{
query,
variables,
}, {
},
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'Accept-Encoding': 'application/json'
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
"Accept-Encoding": "application/json",
},
});
apps = edges.map((e: any) => ({
name: e.node.name,
appId: e.node.id
}));
}
);
const apps = edges.map((e: any) => ({
name: e.node.name,
appId: e.node.id,
}));
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get Railway services");
}
return apps;
}
};
/**
* Return list of apps for Fly.io integration
@@ -428,41 +388,40 @@ const getAppsRailway = async ({ accessToken }: { accessToken: string }) => {
* @returns {String} apps.name - name of Fly.io apps
*/
const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
let apps;
try {
const query = `
query($role: String) {
apps(type: "container", first: 400, role: $role) {
nodes {
id
name
hostname
}
const query = `
query($role: String) {
apps(type: "container", first: 400, role: $role) {
nodes {
id
name
hostname
}
}
`;
}
`;
const res = (await request.post(INTEGRATION_FLYIO_API_URL, {
query,
variables: {
role: null,
const res = (
await request.post(
INTEGRATION_FLYIO_API_URL,
{
query,
variables: {
role: null,
},
},
}, {
headers: {
Authorization: "Bearer " + accessToken,
'Accept': 'application/json',
'Accept-Encoding': 'application/json',
},
})).data.data.apps.nodes;
{
headers: {
Authorization: "Bearer " + accessToken,
Accept: "application/json",
"Accept-Encoding": "application/json",
},
}
)
).data.data.apps.nodes;
apps = res.map((a: any) => ({
name: a.name,
}));
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get Fly.io apps");
}
const apps = res.map((a: any) => ({
name: a.name,
}));
return apps;
};
@@ -475,63 +434,43 @@ const getAppsFlyio = async ({ accessToken }: { accessToken: string }) => {
* @returns {String} apps.name - name of CircleCI apps
*/
const getAppsCircleCI = async ({ accessToken }: { accessToken: string }) => {
let apps: any;
try {
const res = (
await request.get(
`${INTEGRATION_CIRCLECI_API_URL}/v1.1/projects`,
{
headers: {
"Circle-Token": accessToken,
"Accept-Encoding": "application/json",
},
}
)
).data
const res = (
await request.get(`${INTEGRATION_CIRCLECI_API_URL}/v1.1/projects`, {
headers: {
"Circle-Token": accessToken,
"Accept-Encoding": "application/json",
},
})
).data;
const apps = res?.map((a: any) => {
return {
name: a?.reponame,
};
});
apps = res?.map((a: any) => {
return {
name: a?.reponame
}
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get CircleCI projects");
}
return apps;
};
const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => {
let apps: any;
try {
const res = (
await request.get(
`${INTEGRATION_TRAVISCI_API_URL}/repos`,
{
headers: {
"Authorization": `token ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
)
).data;
const res = (
await request.get(`${INTEGRATION_TRAVISCI_API_URL}/repos`, {
headers: {
Authorization: `token ${accessToken}`,
"Accept-Encoding": "application/json",
},
})
).data;
const apps = res?.map((a: any) => {
return {
name: a?.slug?.split("/")[1],
appId: a?.id,
};
});
apps = res?.map((a: any) => {
return {
name: a?.slug?.split("/")[1],
appId: a?.id,
}
});
}catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get TravisCI projects");
}
return apps;
}
};
/**
* Return list of repositories for GitLab integration
@@ -540,112 +479,98 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => {
* @returns {Object[]} apps - names of GitLab sites
* @returns {String} apps.name - name of GitLab site
*/
const getAppsGitlab = async ({
const getAppsGitlab = async ({
accessToken,
teamId
teamId,
}: {
accessToken: string;
teamId?: string;
}) => {
const apps: App[] = [];
let page = 1;
const perPage = 10;
let hasMorePages = true;
try {
if (teamId) {
// case: fetch projects for group with id [teamId] in GitLab
while (hasMorePages) {
const params = new URLSearchParams({
page: String(page),
per_page: String(perPage)
});
if (teamId) {
// case: fetch projects for group with id [teamId] in GitLab
const { data } = (
await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/groups/${teamId}/projects`,
{
params,
headers: {
"Authorization": `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
)
);
while (hasMorePages) {
const params = new URLSearchParams({
page: String(page),
per_page: String(perPage),
});
data.map((a: any) => {
apps.push({
name: a.name,
appId: a.id
});
});
if (data.length < perPage) {
hasMorePages = false;
const { data } = await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/groups/${teamId}/projects`,
{
params,
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
page++;
}
} else {
// case: fetch projects for individual in GitLab
const { id } = (
await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/user`,
{
headers: {
"Authorization": `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
)
).data;
while (hasMorePages) {
const params = new URLSearchParams({
page: String(page),
per_page: String(perPage)
);
data.map((a: any) => {
apps.push({
name: a.name,
appId: a.id,
});
});
const { data } = (
await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/users/${id}/projects`,
{
params,
headers: {
"Authorization": `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
)
);
data.map((a: any) => {
apps.push({
name: a.name,
appId: a.id
});
});
if (data.length < perPage) {
hasMorePages = false;
}
page++;
if (data.length < perPage) {
hasMorePages = false;
}
page++;
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get GitLab projects");
}
return apps;
}
} else {
// case: fetch projects for individual in GitLab
const { id } = (
await request.get(`${INTEGRATION_GITLAB_API_URL}/v4/user`, {
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
})
).data;
while (hasMorePages) {
const params = new URLSearchParams({
page: String(page),
per_page: String(perPage),
});
const { data } = await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/users/${id}/projects`,
{
params,
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
);
data.map((a: any) => {
apps.push({
name: a.name,
appId: a.id,
});
});
if (data.length < perPage) {
hasMorePages = false;
}
page++;
}
}
return apps;
};
/**
* Return list of projects for Supabase integration
@@ -655,30 +580,23 @@ const getAppsGitlab = async ({
* @returns {String} apps.name - name of Supabase app
*/
const getAppsSupabase = async ({ accessToken }: { accessToken: string }) => {
let apps: any;
try {
const { data } = await request.get(
`${INTEGRATION_SUPABASE_API_URL}/v1/projects`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
'Accept-Encoding': 'application/json'
}
}
);
const { data } = await request.get(
`${INTEGRATION_SUPABASE_API_URL}/v1/projects`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
);
const apps = data.map((a: any) => {
return {
name: a.name,
appId: a.id,
};
});
apps = data.map((a: any) => {
return {
name: a.name,
appId: a.id
};
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to get Supabase projects');
}
return apps;
};
+138 -198
View File
@@ -1,5 +1,4 @@
import * as Sentry from '@sentry/node';
import request from '../config/request';
import request from "../config/request";
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_HEROKU,
@@ -12,8 +11,8 @@ import {
INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_GITLAB_TOKEN_URL
} from '../variables';
INTEGRATION_GITLAB_TOKEN_URL,
} from "../variables";
import {
getSiteURL,
getClientIdAzure,
@@ -26,8 +25,8 @@ import {
getClientIdGitHub,
getClientSecretGitHub,
getClientIdGitLab,
getClientSecretGitLab
} from '../config';
getClientSecretGitLab,
} from "../config";
interface ExchangeCodeAzureResponse {
token_type: string;
@@ -93,49 +92,43 @@ interface ExchangeCodeGitlabResponse {
*/
const exchangeCode = async ({
integration,
code
code,
}: {
integration: string;
code: string;
}) => {
let obj = {} as any;
try {
switch (integration) {
case INTEGRATION_AZURE_KEY_VAULT:
obj = await exchangeCodeAzure({
code
});
break;
case INTEGRATION_HEROKU:
obj = await exchangeCodeHeroku({
code
});
break;
case INTEGRATION_VERCEL:
obj = await exchangeCodeVercel({
code
});
break;
case INTEGRATION_NETLIFY:
obj = await exchangeCodeNetlify({
code
});
break;
case INTEGRATION_GITHUB:
obj = await exchangeCodeGithub({
code
});
break;
case INTEGRATION_GITLAB:
obj = await exchangeCodeGitlab({
code
});
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange');
switch (integration) {
case INTEGRATION_AZURE_KEY_VAULT:
obj = await exchangeCodeAzure({
code,
});
break;
case INTEGRATION_HEROKU:
obj = await exchangeCodeHeroku({
code,
});
break;
case INTEGRATION_VERCEL:
obj = await exchangeCodeVercel({
code,
});
break;
case INTEGRATION_NETLIFY:
obj = await exchangeCodeNetlify({
code,
});
break;
case INTEGRATION_GITHUB:
obj = await exchangeCodeGithub({
code,
});
break;
case INTEGRATION_GITLAB:
obj = await exchangeCodeGitlab({
code,
});
}
return obj;
@@ -143,43 +136,33 @@ const exchangeCode = async ({
/**
* Return [accessToken] for Azure OAuth2 code-token exchange
* @param param0
* @param param0
*/
const exchangeCodeAzure = async ({
code
}: {
code: string;
}) => {
const exchangeCodeAzure = async ({ code }: { code: string }) => {
const accessExpiresAt = new Date();
let res: ExchangeCodeAzureResponse;
try {
res = (await request.post(
const res: ExchangeCodeAzureResponse = (
await request.post(
INTEGRATION_AZURE_TOKEN_URL,
new URLSearchParams({
grant_type: 'authorization_code',
grant_type: "authorization_code",
code: code,
scope: 'https://vault.azure.net/.default openid offline_access',
scope: "https://vault.azure.net/.default openid offline_access",
client_id: await getClientIdAzure(),
client_secret: await getClientSecretAzure(),
redirect_uri: `${await getSiteURL()}/integrations/azure-key-vault/oauth2/callback`
redirect_uri: `${await getSiteURL()}/integrations/azure-key-vault/oauth2/callback`,
} as any)
)).data;
)
).data;
accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + res.expires_in
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Azure');
}
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in);
return ({
return {
accessToken: res.access_token,
refreshToken: res.refresh_token,
accessExpiresAt
});
}
accessExpiresAt,
};
};
/**
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Heroku
@@ -191,38 +174,28 @@ const exchangeCodeAzure = async ({
* @returns {String} obj2.refreshToken - refresh token for Heroku API
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
*/
const exchangeCodeHeroku = async ({
code
}: {
code: string;
}) => {
let res: ExchangeCodeHerokuResponse;
const exchangeCodeHeroku = async ({ code }: { code: string }) => {
const accessExpiresAt = new Date();
try {
res = (await request.post(
const res: ExchangeCodeHerokuResponse = (
await request.post(
INTEGRATION_HEROKU_TOKEN_URL,
new URLSearchParams({
grant_type: 'authorization_code',
grant_type: "authorization_code",
code: code,
client_secret: await getClientSecretHeroku()
client_secret: await getClientSecretHeroku(),
} as any)
)).data;
)
).data;
accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + res.expires_in
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Heroku');
}
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in);
return ({
return {
accessToken: res.access_token,
refreshToken: res.refresh_token,
accessExpiresAt
});
}
accessExpiresAt,
};
};
/**
* Return [accessToken], [accessExpiresAt], and [refreshToken] for Vercel
@@ -235,30 +208,23 @@ const exchangeCodeHeroku = async ({
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
*/
const exchangeCodeVercel = async ({ code }: { code: string }) => {
let res: ExchangeCodeVercelResponse;
try {
res = (
await request.post(
INTEGRATION_VERCEL_TOKEN_URL,
new URLSearchParams({
code: code,
client_id: await getClientIdVercel(),
client_secret: await getClientSecretVercel(),
redirect_uri: `${await getSiteURL()}/integrations/vercel/oauth2/callback`
} as any)
)
).data;
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error(`Failed OAuth2 code-token exchange with Vercel [err=${err}]`);
}
const res: ExchangeCodeVercelResponse = (
await request.post(
INTEGRATION_VERCEL_TOKEN_URL,
new URLSearchParams({
code: code,
client_id: await getClientIdVercel(),
client_secret: await getClientSecretVercel(),
redirect_uri: `${await getSiteURL()}/integrations/vercel/oauth2/callback`,
} as any)
)
).data;
return {
accessToken: res.access_token,
refreshToken: null,
accessExpiresAt: null,
teamId: res.team_id
teamId: res.team_id,
};
};
@@ -273,47 +239,39 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => {
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
*/
const exchangeCodeNetlify = async ({ code }: { code: string }) => {
let res: ExchangeCodeNetlifyResponse;
let accountId;
try {
res = (
await request.post(
INTEGRATION_NETLIFY_TOKEN_URL,
new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_id: await getClientIdNetlify(),
client_secret: await getClientSecretNetlify(),
redirect_uri: `${await getSiteURL()}/integrations/netlify/oauth2/callback`
} as any)
)
).data;
const res: ExchangeCodeNetlifyResponse = (
await request.post(
INTEGRATION_NETLIFY_TOKEN_URL,
new URLSearchParams({
grant_type: "authorization_code",
code: code,
client_id: await getClientIdNetlify(),
client_secret: await getClientSecretNetlify(),
redirect_uri: `${await getSiteURL()}/integrations/netlify/oauth2/callback`,
} as any)
)
).data;
const res2 = await request.get('https://api.netlify.com/api/v1/sites', {
const res2 = await request.get("https://api.netlify.com/api/v1/sites", {
headers: {
Authorization: `Bearer ${res.access_token}`,
},
});
const res3 = (
await request.get("https://api.netlify.com/api/v1/accounts", {
headers: {
Authorization: `Bearer ${res.access_token}`
}
});
Authorization: `Bearer ${res.access_token}`,
},
})
).data;
const res3 = (
await request.get('https://api.netlify.com/api/v1/accounts', {
headers: {
Authorization: `Bearer ${res.access_token}`
}
})
).data;
accountId = res3[0].id;
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Netlify');
}
const accountId = res3[0].id;
return {
accessToken: res.access_token,
refreshToken: res.refresh_token,
accountId
accountId,
};
};
@@ -328,33 +286,25 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
*/
const exchangeCodeGithub = async ({ code }: { code: string }) => {
let res: ExchangeCodeGithubResponse;
try {
res = (
await request.get(INTEGRATION_GITHUB_TOKEN_URL, {
params: {
client_id: await getClientIdGitHub(),
client_secret: await getClientSecretGitHub(),
code: code,
redirect_uri: `${await getSiteURL()}/integrations/github/oauth2/callback`
},
headers: {
'Accept': 'application/json',
'Accept-Encoding': 'application/json'
}
})
).data;
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Github');
}
const res: ExchangeCodeGithubResponse = (
await request.get(INTEGRATION_GITHUB_TOKEN_URL, {
params: {
client_id: await getClientIdGitHub(),
client_secret: await getClientSecretGitHub(),
code: code,
redirect_uri: `${await getSiteURL()}/integrations/github/oauth2/callback`,
},
headers: {
Accept: "application/json",
"Accept-Encoding": "application/json",
},
})
).data;
return {
accessToken: res.access_token,
refreshToken: null,
accessExpiresAt: null
accessExpiresAt: null,
};
};
@@ -369,42 +319,32 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
* @returns {Date} obj2.accessExpiresAt - date of expiration for access token
*/
const exchangeCodeGitlab = async ({ code }: { code: string }) => {
let res: ExchangeCodeGitlabResponse;
const accessExpiresAt = new Date();
try {
res = (
await request.post(
INTEGRATION_GITLAB_TOKEN_URL,
new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_id: await getClientIdGitLab(),
client_secret: await getClientSecretGitLab(),
redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback`
} as any),
{
headers: {
"Accept-Encoding": "application/json",
}
}
)
).data;
accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + res.expires_in
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Gitlab');
}
const res: ExchangeCodeGitlabResponse = (
await request.post(
INTEGRATION_GITLAB_TOKEN_URL,
new URLSearchParams({
grant_type: "authorization_code",
code: code,
client_id: await getClientIdGitLab(),
client_secret: await getClientSecretGitLab(),
redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback`,
} as any),
{
headers: {
"Accept-Encoding": "application/json",
},
}
)
).data;
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + res.expires_in);
return {
accessToken: res.access_token,
refreshToken: res.refresh_token,
accessExpiresAt
accessExpiresAt,
};
}
};
export { exchangeCode };
+114 -145
View File
@@ -1,29 +1,24 @@
import * as Sentry from '@sentry/node';
import request from '../config/request';
import request from "../config/request";
import { IIntegrationAuth } from "../models";
import {
IIntegrationAuth
} from '../models';
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_HEROKU,
INTEGRATION_GITLAB,
} from '../variables';
} from "../variables";
import {
INTEGRATION_AZURE_TOKEN_URL,
INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_GITLAB_TOKEN_URL
} from '../variables';
import {
IntegrationService
} from '../services';
INTEGRATION_GITLAB_TOKEN_URL,
} from "../variables";
import { IntegrationService } from "../services";
import {
getSiteURL,
getClientIdAzure,
getClientSecretAzure,
getClientSecretHeroku,
getClientIdGitLab,
getClientSecretGitLab
} from '../config';
getClientSecretGitLab,
} from "../config";
interface RefreshTokenAzureResponse {
token_type: string;
@@ -60,60 +55,57 @@ interface RefreshTokenGitLabResponse {
*/
const exchangeRefresh = async ({
integrationAuth,
refreshToken
refreshToken,
}: {
integrationAuth: IIntegrationAuth;
refreshToken: string;
}) => {
interface TokenDetails {
accessToken: string;
refreshToken: string;
accessExpiresAt: Date;
}
let tokenDetails: TokenDetails;
try {
switch (integrationAuth.integration) {
case INTEGRATION_AZURE_KEY_VAULT:
tokenDetails = await exchangeRefreshAzure({
refreshToken
});
break;
case INTEGRATION_HEROKU:
tokenDetails = await exchangeRefreshHeroku({
refreshToken
});
break;
case INTEGRATION_GITLAB:
tokenDetails = await exchangeRefreshGitLab({
refreshToken
});
break;
default:
throw new Error('Failed to exchange token for incompatible integration');
}
if (tokenDetails?.accessToken && tokenDetails?.refreshToken && tokenDetails?.accessExpiresAt) {
await IntegrationService.setIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString(),
accessId: null,
accessToken: tokenDetails.accessToken,
accessExpiresAt: tokenDetails.accessExpiresAt
switch (integrationAuth.integration) {
case INTEGRATION_AZURE_KEY_VAULT:
tokenDetails = await exchangeRefreshAzure({
refreshToken,
});
await IntegrationService.setIntegrationAuthRefresh({
integrationAuthId: integrationAuth._id.toString(),
refreshToken: tokenDetails.refreshToken
break;
case INTEGRATION_HEROKU:
tokenDetails = await exchangeRefreshHeroku({
refreshToken,
});
}
return tokenDetails.accessToken;
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to get new OAuth2 access token');
break;
case INTEGRATION_GITLAB:
tokenDetails = await exchangeRefreshGitLab({
refreshToken,
});
break;
default:
throw new Error("Failed to exchange token for incompatible integration");
}
if (
tokenDetails?.accessToken &&
tokenDetails?.refreshToken &&
tokenDetails?.accessExpiresAt
) {
await IntegrationService.setIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString(),
accessId: null,
accessToken: tokenDetails.accessToken,
accessExpiresAt: tokenDetails.accessExpiresAt,
});
await IntegrationService.setIntegrationAuthRefresh({
integrationAuthId: integrationAuth._id.toString(),
refreshToken: tokenDetails.refreshToken,
});
}
return tokenDetails.accessToken;
};
/**
@@ -124,38 +116,30 @@ const exchangeRefresh = async ({
* @returns
*/
const exchangeRefreshAzure = async ({
refreshToken
refreshToken,
}: {
refreshToken: string;
}) => {
try {
const accessExpiresAt = new Date();
const { data }: { data: RefreshTokenAzureResponse } = await request.post(
INTEGRATION_AZURE_TOKEN_URL,
new URLSearchParams({
client_id: await getClientIdAzure(),
scope: 'openid offline_access',
refresh_token: refreshToken,
grant_type: 'refresh_token',
client_secret: await getClientSecretAzure()
} as any)
);
accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + data.expires_in
);
const accessExpiresAt = new Date();
const { data }: { data: RefreshTokenAzureResponse } = await request.post(
INTEGRATION_AZURE_TOKEN_URL,
new URLSearchParams({
client_id: await getClientIdAzure(),
scope: "openid offline_access",
refresh_token: refreshToken,
grant_type: "refresh_token",
client_secret: await getClientSecretAzure(),
} as any)
);
return ({
accessToken: data.access_token,
refreshToken: data.refresh_token,
accessExpiresAt
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to get refresh OAuth2 access token for Azure');
}
}
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in);
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
accessExpiresAt,
};
};
/**
* Return new access token by exchanging refresh token [refreshToken] for the
@@ -165,39 +149,31 @@ const exchangeRefreshAzure = async ({
* @returns
*/
const exchangeRefreshHeroku = async ({
refreshToken
refreshToken,
}: {
refreshToken: string;
}) => {
try {
const accessExpiresAt = new Date();
const {
data
}: {
data: RefreshTokenHerokuResponse
} = await request.post(
INTEGRATION_HEROKU_TOKEN_URL,
new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_secret: await getClientSecretHeroku()
} as any)
);
const accessExpiresAt = new Date();
const {
data,
}: {
data: RefreshTokenHerokuResponse;
} = await request.post(
INTEGRATION_HEROKU_TOKEN_URL,
new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_secret: await getClientSecretHeroku(),
} as any)
);
accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + data.expires_in
);
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in);
return ({
accessToken: data.access_token,
refreshToken: data.refresh_token,
accessExpiresAt
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to refresh OAuth2 access token for Heroku');
}
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
accessExpiresAt,
};
};
/**
@@ -208,45 +184,38 @@ const exchangeRefreshHeroku = async ({
* @returns
*/
const exchangeRefreshGitLab = async ({
refreshToken
refreshToken,
}: {
refreshToken: string;
}) => {
try {
const accessExpiresAt = new Date();
const {
data
}: {
data: RefreshTokenGitLabResponse
} = await request.post(
INTEGRATION_GITLAB_TOKEN_URL,
new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_id: await getClientIdGitLab,
client_secret: await getClientSecretGitLab(),
redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback`
} as any),
{
headers: {
"Accept-Encoding": "application/json",
}
});
const accessExpiresAt = new Date();
const {
data,
}: {
data: RefreshTokenGitLabResponse;
} = await request.post(
INTEGRATION_GITLAB_TOKEN_URL,
new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: await getClientIdGitLab,
client_secret: await getClientSecretGitLab(),
redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback`,
} as any),
{
headers: {
"Accept-Encoding": "application/json",
},
}
);
accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + data.expires_in
);
accessExpiresAt.setSeconds(accessExpiresAt.getSeconds() + data.expires_in);
return ({
accessToken: data.access_token,
refreshToken: data.refresh_token,
accessExpiresAt
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to refresh OAuth2 access token for GitLab');
}
return {
accessToken: data.access_token,
refreshToken: data.refresh_token,
accessExpiresAt,
};
};
export { exchangeRefresh };
+20 -27
View File
@@ -1,4 +1,3 @@
import * as Sentry from '@sentry/node';
import {
IIntegrationAuth,
IntegrationAuth,
@@ -22,34 +21,28 @@ const revokeAccess = async ({
accessToken: string;
}) => {
let deletedIntegrationAuth;
try {
// add any integration-specific revocation logic
switch (integrationAuth.integration) {
case INTEGRATION_HEROKU:
break;
case INTEGRATION_VERCEL:
break;
case INTEGRATION_NETLIFY:
break;
case INTEGRATION_GITHUB:
break;
case INTEGRATION_GITLAB:
break;
}
// add any integration-specific revocation logic
switch (integrationAuth.integration) {
case INTEGRATION_HEROKU:
break;
case INTEGRATION_VERCEL:
break;
case INTEGRATION_NETLIFY:
break;
case INTEGRATION_GITHUB:
break;
case INTEGRATION_GITLAB:
break;
}
deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
_id: integrationAuth._id
deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
_id: integrationAuth._id
});
if (deletedIntegrationAuth) {
await Integration.deleteMany({
integrationAuth: deletedIntegrationAuth._id
});
if (deletedIntegrationAuth) {
await Integration.deleteMany({
integrationAuth: deletedIntegrationAuth._id
});
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to delete integration authorization');
}
return deletedIntegrationAuth;
+21 -34
View File
@@ -1,4 +1,3 @@
import * as Sentry from "@sentry/node";
import {
IIntegrationAuth
} from '../models';
@@ -31,21 +30,15 @@ const getTeams = async ({
}) => {
let teams: Team[] = [];
try {
switch (integrationAuth.integration) {
case INTEGRATION_GITLAB:
teams = await getTeamsGitLab({
accessToken
});
break;
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to get integration teams');
switch (integrationAuth.integration) {
case INTEGRATION_GITLAB:
teams = await getTeamsGitLab({
accessToken
});
break;
}
return teams;
}
@@ -63,30 +56,24 @@ const getTeamsGitLab = async ({
accessToken: string;
}) => {
let teams: Team[] = [];
try {
const res = (await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/groups`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
}
const res = (await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/groups`,
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Accept-Encoding": "application/json"
}
)).data;
teams = res.map((t: any) => ({
name: t.name,
teamId: t.id
}));
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error("Failed to get GitLab integration teams");
}
}
)).data;
teams = res.map((t: any) => ({
name: t.name,
teamId: t.id
}));
return teams;
}
export {
getTeams
}
}