Finish support for GitLab groups integration

This commit is contained in:
Tuan Dang
2023-03-10 21:25:04 +07:00
parent 3f3516b7ba
commit 0269b58a3c
13 changed files with 319 additions and 181 deletions
@@ -2,7 +2,6 @@ import { Request, Response } from 'express';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { import {
Integration,
IntegrationAuth, IntegrationAuth,
Bot Bot
} from '../../models'; } from '../../models';
@@ -161,7 +160,7 @@ export const getIntegrationAuthApps = async (req: Request, res: Response) => {
let apps; let apps;
try { try {
const teamId = req.query.teamId as string; const teamId = req.query.teamId as string;
apps = await getApps({ apps = await getApps({
integrationAuth: req.integrationAuth, integrationAuth: req.integrationAuth,
accessToken: req.accessToken, accessToken: req.accessToken,
@@ -2,10 +2,7 @@ import { Request, Response } from 'express';
import { Types } from 'mongoose'; import { Types } from 'mongoose';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { import {
Integration, Integration
Workspace,
Bot,
BotKey
} from '../../models'; } from '../../models';
import { EventService } from '../../services'; import { EventService } from '../../services';
import { eventPushSecrets } from '../../events'; import { eventPushSecrets } from '../../events';
@@ -18,6 +15,7 @@ import { eventPushSecrets } from '../../events';
*/ */
export const createIntegration = async (req: Request, res: Response) => { export const createIntegration = async (req: Request, res: Response) => {
let integration; let integration;
try { try {
const { const {
integrationAuthId, integrationAuthId,
@@ -34,19 +32,19 @@ export const createIntegration = async (req: Request, res: Response) => {
// TODO: validate [sourceEnvironment] and [targetEnvironment] // TODO: validate [sourceEnvironment] and [targetEnvironment]
// initialize new integration after saving integration access token // initialize new integration after saving integration access token
integration = await new Integration({ integration = await new Integration({
workspace: req.integrationAuth.workspace._id, workspace: req.integrationAuth.workspace._id,
environment: sourceEnvironment, environment: sourceEnvironment,
isActive, isActive,
app, app,
appId, appId,
targetEnvironment, targetEnvironment,
owner, owner,
path, path,
region, region,
integration: req.integrationAuth.integration, integration: req.integrationAuth.integration,
integrationAuth: new Types.ObjectId(integrationAuthId) integrationAuth: new Types.ObjectId(integrationAuthId)
}).save(); }).save();
if (integration) { if (integration) {
// trigger event - push secrets // trigger event - push secrets
+1 -1
View File
@@ -229,7 +229,7 @@ const getIntegrationAuthAccessHelper = async ({ integrationAuthId }: { integrati
// access token is expired // access token is expired
const refreshToken = await getIntegrationAuthRefreshHelper({ integrationAuthId }); const refreshToken = await getIntegrationAuthRefreshHelper({ integrationAuthId });
accessToken = await exchangeRefresh({ accessToken = await exchangeRefresh({
integration: integrationAuth.integration, integrationAuth,
refreshToken refreshToken
}); });
} }
+101 -38
View File
@@ -24,7 +24,12 @@ import {
INTEGRATION_CIRCLECI_API_URL, INTEGRATION_CIRCLECI_API_URL,
INTEGRATION_TRAVISCI_API_URL, INTEGRATION_TRAVISCI_API_URL,
} from "../variables"; } from "../variables";
import { requireIntegrationAuthorizationAuth } from "../middleware";
interface App {
name: string;
appId?: string;
owner?: string;
}
/** /**
* Return list of names of apps for integration named [integration] * Return list of names of apps for integration named [integration]
@@ -45,12 +50,6 @@ const getApps = async ({
teamId?: string; teamId?: string;
}) => { }) => {
interface App {
name: string;
appId?: string;
owner?: string;
}
let apps: App[] = []; let apps: App[] = [];
try { try {
switch (integrationAuth.integration) { switch (integrationAuth.integration) {
@@ -87,6 +86,7 @@ const getApps = async ({
case INTEGRATION_GITLAB: case INTEGRATION_GITLAB:
apps = await getAppsGitlab({ apps = await getAppsGitlab({
accessToken, accessToken,
teamId
}); });
break; break;
case INTEGRATION_RENDER: case INTEGRATION_RENDER:
@@ -439,44 +439,107 @@ const getAppsTravisCI = async ({ accessToken }: { accessToken: string }) => {
* @returns {Object[]} apps - names of GitLab sites * @returns {Object[]} apps - names of GitLab sites
* @returns {String} apps.name - name of GitLab site * @returns {String} apps.name - name of GitLab site
*/ */
const getAppsGitlab = async ({ accessToken }: {accessToken: string}) => { const getAppsGitlab = async ({
let apps; accessToken,
teamId
}: {
accessToken: string;
teamId?: string;
}) => {
const apps: App[] = [];
let page = 1;
const perPage = 10;
let hasMorePages = true;
try { try {
const { id } = (
await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/user`,
{
headers: {
"Authorization": `Bearer ${accessToken}`,
"Accept-Encoding": "application/json",
},
}
)
).data;
const res = ( if (teamId) {
await request.get( // case: fetch projects for group with id [teamId] in GitLab
`${INTEGRATION_GITLAB_API_URL}/v4/users/${id}/projects`,
{ while (hasMorePages) {
headers: { const params = new URLSearchParams({
"Authorization": `Bearer ${accessToken}`, page: String(page),
"Accept-Encoding": "application/json", per_page: String(perPage)
}, });
}
)
).data;
apps = res?.map((a: any) => { const { data } = (
return { await request.get(
name: a?.name, `${INTEGRATION_GITLAB_API_URL}/v4/groups/${teamId}/projects`,
appId: `${a?.id}`, {
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++;
} }
}); } else {
}catch (err) { // 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++;
}
}
} catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
throw new Error("Failed to get GitLab repos"); throw new Error("Failed to get GitLab projects");
} }
return apps; return apps;
+3 -40
View File
@@ -12,8 +12,7 @@ import {
INTEGRATION_VERCEL_TOKEN_URL, INTEGRATION_VERCEL_TOKEN_URL,
INTEGRATION_NETLIFY_TOKEN_URL, INTEGRATION_NETLIFY_TOKEN_URL,
INTEGRATION_GITHUB_TOKEN_URL, INTEGRATION_GITHUB_TOKEN_URL,
INTEGRATION_GITLAB_TOKEN_URL, INTEGRATION_GITLAB_TOKEN_URL
INTEGRATION_GITLAB_API_URL
} from '../variables'; } from '../variables';
import { import {
SITE_URL, SITE_URL,
@@ -169,7 +168,7 @@ const exchangeCodeAzure = async ({
accessExpiresAt.setSeconds( accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + res.expires_in accessExpiresAt.getSeconds() + res.expires_in
); );
} catch (err: any) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Azure'); throw new Error('Failed OAuth2 code-token exchange with Azure');
@@ -392,46 +391,10 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => {
) )
).data; ).data;
// 1. try getting groups
// https://gitlab.com/api/v4/groups
// const res2 = (await request.get(
// `${INTEGRATION_GITLAB_API_URL}/v4/groups`,
// {
// headers: {
// Authorization: `Bearer ${res.access_token}`,
// "Accept-Encoding": "application/json"
// }
// }
// )).data;
// 2. try getting projects of that group
// const res3 = (await request.get(
// `${INTEGRATION_GITLAB_API_URL}/v4/groups/${res2[0].id}`,
// {
// headers: {
// Authorization: `Bearer ${res.access_token}`,
// "Accept-Encoding": "application/json"
// }
// }
// )).data;
// const res = (
// await request.get(
// `${INTEGRATION_GITLAB_API_URL}/v4/users/${id}/projects`,
// {
// headers: {
// "Authorization": `Bearer ${accessToken}`,
// "Accept-Encoding": "application/json",
// },
// }
// )
// ).data;
accessExpiresAt.setSeconds( accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + res.expires_in accessExpiresAt.getSeconds() + res.expires_in
); );
}catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
throw new Error('Failed OAuth2 code-token exchange with Gitlab'); throw new Error('Failed OAuth2 code-token exchange with Gitlab');
+144 -19
View File
@@ -1,16 +1,29 @@
import request from '../config/request'; import request from '../config/request';
import * as Sentry from '@sentry/node'; import * as Sentry from '@sentry/node';
import { INTEGRATION_AZURE_KEY_VAULT, INTEGRATION_HEROKU } from '../variables'; import {
IIntegrationAuth
} from '../models';
import {
INTEGRATION_AZURE_KEY_VAULT,
INTEGRATION_HEROKU,
INTEGRATION_GITLAB,
} from '../variables';
import { import {
SITE_URL, SITE_URL,
CLIENT_ID_AZURE, CLIENT_ID_AZURE,
CLIENT_ID_GITLAB,
CLIENT_SECRET_AZURE, CLIENT_SECRET_AZURE,
CLIENT_SECRET_HEROKU CLIENT_SECRET_HEROKU,
CLIENT_SECRET_GITLAB
} from '../config'; } from '../config';
import { import {
INTEGRATION_AZURE_TOKEN_URL, INTEGRATION_AZURE_TOKEN_URL,
INTEGRATION_HEROKU_TOKEN_URL INTEGRATION_HEROKU_TOKEN_URL,
INTEGRATION_GITLAB_TOKEN_URL
} from '../variables'; } from '../variables';
import {
IntegrationService
} from '../services';
interface RefreshTokenAzureResponse { interface RefreshTokenAzureResponse {
token_type: string; token_type: string;
@@ -21,6 +34,23 @@ interface RefreshTokenAzureResponse {
refresh_token: string; refresh_token: string;
} }
interface RefreshTokenHerokuResponse {
access_token: string;
expires_in: number;
refresh_token: string;
token_type: string;
user_id: string;
}
interface RefreshTokenGitLabResponse {
token_type: string;
scope: string;
expires_in: number;
access_token: string;
refresh_token: string;
created_at: number;
}
/** /**
* Return new access token by exchanging refresh token [refreshToken] for integration * Return new access token by exchanging refresh token [refreshToken] for integration
* named [integration] * named [integration]
@@ -29,33 +59,61 @@ interface RefreshTokenAzureResponse {
* @param {String} obj.refreshToken - refresh token to use to get new access token for Heroku * @param {String} obj.refreshToken - refresh token to use to get new access token for Heroku
*/ */
const exchangeRefresh = async ({ const exchangeRefresh = async ({
integration, integrationAuth,
refreshToken refreshToken
}: { }: {
integration: string; integrationAuth: IIntegrationAuth;
refreshToken: string; refreshToken: string;
}) => { }) => {
let accessToken;
interface TokenDetails {
accessToken: string;
refreshToken: string;
accessExpiresAt: Date;
}
let tokenDetails: TokenDetails;
try { try {
switch (integration) { switch (integrationAuth.integration) {
case INTEGRATION_AZURE_KEY_VAULT: case INTEGRATION_AZURE_KEY_VAULT:
accessToken = await exchangeRefreshAzure({ tokenDetails = await exchangeRefreshAzure({
refreshToken refreshToken
}); });
break; break;
case INTEGRATION_HEROKU: case INTEGRATION_HEROKU:
accessToken = await exchangeRefreshHeroku({ tokenDetails = await exchangeRefreshHeroku({
refreshToken refreshToken
}); });
break; 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;
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
throw new Error('Failed to get new OAuth2 access token'); throw new Error('Failed to get new OAuth2 access token');
} }
return accessToken;
}; };
/** /**
@@ -71,7 +129,8 @@ const exchangeRefreshAzure = async ({
refreshToken: string; refreshToken: string;
}) => { }) => {
try { try {
const res: RefreshTokenAzureResponse = (await request.post( const accessExpiresAt = new Date();
const { data }: { data: RefreshTokenAzureResponse } = await request.post(
INTEGRATION_AZURE_TOKEN_URL, INTEGRATION_AZURE_TOKEN_URL,
new URLSearchParams({ new URLSearchParams({
client_id: CLIENT_ID_AZURE, client_id: CLIENT_ID_AZURE,
@@ -80,9 +139,17 @@ const exchangeRefreshAzure = async ({
grant_type: 'refresh_token', grant_type: 'refresh_token',
client_secret: CLIENT_SECRET_AZURE client_secret: CLIENT_SECRET_AZURE
} as any) } as any)
)).data; );
return res.access_token; accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + data.expires_in
);
return ({
accessToken: data.access_token,
refreshToken: data.refresh_token,
accessExpiresAt
});
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
@@ -102,10 +169,13 @@ const exchangeRefreshHeroku = async ({
}: { }: {
refreshToken: string; refreshToken: string;
}) => { }) => {
let accessToken;
try { try {
const res = await request.post( const accessExpiresAt = new Date();
const {
data
}: {
data: RefreshTokenHerokuResponse
} = await request.post(
INTEGRATION_HEROKU_TOKEN_URL, INTEGRATION_HEROKU_TOKEN_URL,
new URLSearchParams({ new URLSearchParams({
grant_type: 'refresh_token', grant_type: 'refresh_token',
@@ -114,14 +184,69 @@ const exchangeRefreshHeroku = async ({
} as any) } as any)
); );
accessToken = res.data.access_token; accessExpiresAt.setSeconds(
accessExpiresAt.getSeconds() + data.expires_in
);
return ({
accessToken: data.access_token,
refreshToken: data.refresh_token,
accessExpiresAt
});
} catch (err) { } catch (err) {
Sentry.setUser(null); Sentry.setUser(null);
Sentry.captureException(err); Sentry.captureException(err);
throw new Error('Failed to refresh OAuth2 access token for Heroku'); throw new Error('Failed to refresh OAuth2 access token for Heroku');
} }
};
return accessToken; /**
* Return new access token by exchanging refresh token [refreshToken] for the
* GitLab integration
* @param {Object} obj
* @param {String} obj.refreshToken - refresh token to use to get new access token for GitLab
* @returns
*/
const exchangeRefreshGitLab = async ({
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: CLIENT_ID_GITLAB,
client_secret: CLIENT_SECRET_GITLAB,
redirect_uri: `${SITE_URL}/integrations/gitlab/oauth2/callback`
} as any),
{
headers: {
"Accept-Encoding": "application/json",
}
});
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');
}
}; };
export { exchangeRefresh }; export { exchangeRefresh };
+3 -4
View File
@@ -172,7 +172,6 @@ const syncSecretsAzureKeyVault = async ({
accessToken: string; accessToken: string;
}) => { }) => {
try { try {
interface GetAzureKeyVaultSecret { interface GetAzureKeyVaultSecret {
id: string; // secret URI id: string; // secret URI
attributes: { attributes: {
@@ -1512,7 +1511,7 @@ const syncSecretsGitLab = async ({
) )
).data; ).data;
for (const key of Object.keys(secrets)) { for await (const key of Object.keys(secrets)) {
const existingSecret = getSecretsRes.find((s: any) => s.key == key); const existingSecret = getSecretsRes.find((s: any) => s.key == key);
if (!existingSecret) { if (!existingSecret) {
await request.post( await request.post(
@@ -1533,7 +1532,7 @@ const syncSecretsGitLab = async ({
}, },
} }
) )
}else { } else {
// udpate secret // udpate secret
await request.put( await request.put(
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${existingSecret.key}`, `${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${existingSecret.key}`,
@@ -1553,7 +1552,7 @@ const syncSecretsGitLab = async ({
} }
// delete secrets // delete secrets
for (const sec of getSecretsRes) { for await (const sec of getSecretsRes) {
if (!(sec.key in secrets)) { if (!(sec.key in secrets)) {
await request.delete( await request.delete(
`${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${sec.key}`, `${INTEGRATION_GITLAB_API_URL}/v4/projects/${integration?.appId}/variables/${sec.key}`,
+23 -5
View File
@@ -8,6 +8,20 @@ import {
} from '../variables'; } from '../variables';
import request from '../config/request'; import request from '../config/request';
interface Team {
name: string;
teamId: string;
}
/**
* Return list of teams for integration authorization [integrationAuth]
* @param {Object} obj
* @param {String} obj.integrationAuth - integration authorization to get teams for
* @param {String} obj.accessToken - access token for integration authorization
* @returns {Object[]} teams - teams of integration authorization
* @returns {String} teams.name - name of team
* @returns {String} teams.teamId - id of team
*/
const getTeams = async ({ const getTeams = async ({
integrationAuth, integrationAuth,
accessToken accessToken
@@ -15,10 +29,6 @@ const getTeams = async ({
integrationAuth: IIntegrationAuth; integrationAuth: IIntegrationAuth;
accessToken: string; accessToken: string;
}) => { }) => {
interface Team {
name: string;
teamId: string;
}
let teams: Team[] = []; let teams: Team[] = [];
try { try {
@@ -39,12 +49,20 @@ const getTeams = async ({
return teams; return teams;
} }
/**
* Return list of teams for GitLab integration
* @param {Object} obj
* @param {String} obj.accessToken - access token for GitLab API
* @returns {Object[]} teams - teams that user is part of in GitLab
* @returns {String} teams.name - name of team
* @returns {String} teams.teamId - id of team
*/
const getTeamsGitLab = async ({ const getTeamsGitLab = async ({
accessToken accessToken
}: { }: {
accessToken: string; accessToken: string;
}) => { }) => {
let teams = []; let teams: Team[] = [];
try { try {
const res = (await request.get( const res = (await request.get(
`${INTEGRATION_GITLAB_API_URL}/v4/groups`, `${INTEGRATION_GITLAB_API_URL}/v4/groups`,
-2
View File
@@ -62,13 +62,11 @@ const integrationSchema = new Schema<IIntegration>(
default: null, default: null,
}, },
appId: { appId: {
// (new)
// id of app in provider // id of app in provider
type: String, type: String,
default: null, default: null,
}, },
targetEnvironment: { targetEnvironment: {
// (new)
// target environment // target environment
type: String, type: String,
default: null, default: null,
+1 -4
View File
@@ -64,9 +64,6 @@ router.post(
integrationAuthController.saveIntegrationAccessToken integrationAuthController.saveIntegrationAccessToken
); );
// this can optionally accept a teamId?
// IF teamId is passed in then it probably means that we want to get
// the apps for that team
router.get( router.get(
'/:integrationAuthId/apps', '/:integrationAuthId/apps',
requireAuth({ requireAuth({
@@ -76,7 +73,7 @@ router.get(
acceptedRoles: [ADMIN, MEMBER] acceptedRoles: [ADMIN, MEMBER]
}), }),
param('integrationAuthId'), param('integrationAuthId'),
query('entity'), query('teamId'),
validateRequest, validateRequest,
integrationAuthController.getIntegrationAuthApps integrationAuthController.getIntegrationAuthApps
); );
@@ -7,9 +7,6 @@ import {
setIntegrationAuthAccessHelper, setIntegrationAuthAccessHelper,
} from '../helpers/integration'; } from '../helpers/integration';
// should sync stuff be here too? Probably.
// TODO: move bot functions to IntegrationService.
/** /**
* Class to handle integrations * Class to handle integrations
*/ */
@@ -25,7 +25,6 @@ const fetchIntegrationAuthApps = async ({
integrationAuthId: string; integrationAuthId: string;
teamId?: string; teamId?: string;
}) => { }) => {
console.log('fetchIntegrationAuthApps: ', integrationAuthId);
const searchParams = new URLSearchParams(teamId ? { teamId } : undefined); const searchParams = new URLSearchParams(teamId ? { teamId } : undefined);
const { data } = await apiRequest.get<{ apps: App[] }>( const { data } = await apiRequest.get<{ apps: App[] }>(
`/api/v1/integration-auth/${integrationAuthId}/apps`, `/api/v1/integration-auth/${integrationAuthId}/apps`,
@@ -23,8 +23,6 @@ const gitLabEntities = [
{ name: 'Group', value: 'group' } { name: 'Group', value: 'group' }
] ]
// TODO: flesh out the data flow...
export default function GitLabCreateIntegrationPage() { export default function GitLabCreateIntegrationPage() {
const router = useRouter(); const router = useRouter();
@@ -33,59 +31,51 @@ export default function GitLabCreateIntegrationPage() {
const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? ''); const { data: workspace } = useGetWorkspaceById(localStorage.getItem('projectData.id') ?? '');
const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? ''); const { data: integrationAuth } = useGetIntegrationAuthById(integrationAuthId as string ?? '');
const [targetEntity, setTargetEntity] = useState(gitLabEntities[0].value); const [targetTeamId, setTargetTeamId] = useState<string | null>(null);
const [targetTeam, setTargetTeam] = useState('aa'); // ?
const [targetTeamId] = useState(undefined);
const { data: integrationAuthApps } = useGetIntegrationAuthApps({ const { data: integrationAuthApps } = useGetIntegrationAuthApps({
integrationAuthId: integrationAuthId as string ?? '', integrationAuthId: integrationAuthId as string ?? '',
...(targetTeamId ? { teamId: targetTeamId } : {}) ...(targetTeamId ? { teamId: targetTeamId } : {})
}); });
const { data: integrationAuthTeams } = useGetIntegrationAuthTeams(integrationAuthId as string ?? ''); const { data: integrationAuthTeams } = useGetIntegrationAuthTeams(integrationAuthId as string ?? '');
console.log('integrationAuthTeams: ', integrationAuthTeams);
const [targetEntity, setTargetEntity] = useState(gitLabEntities[0].value);
const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState(''); const [selectedSourceEnvironment, setSelectedSourceEnvironment] = useState('');
const [targetApp, setTargetApp] = useState(''); const [targetAppId, setTargetAppId] = useState('');
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
useEffect(() => { useEffect(() => {
if (workspace) { if (workspace) {
setSelectedSourceEnvironment(workspace.environments[0].slug); setSelectedSourceEnvironment(workspace.environments[0].slug);
} }
}, [workspace]); }, [workspace]);
useEffect(() => { useEffect(() => {
if (integrationAuthApps) { if (integrationAuthApps) {
if (integrationAuthApps.length > 0) { if (integrationAuthApps.length > 0) {
console.log('AA'); setTargetAppId(integrationAuthApps[0].appId as string);
setTargetApp(integrationAuthApps[0].name);
} else { } else {
console.log('BB'); setTargetAppId('none');
setTargetApp('none');
} }
} }
}, [integrationAuthApps]); }, [integrationAuthApps]);
useEffect(() => { useEffect(() => {
// if (targetEntity === 'group' && integrationAuthTeams) { if (targetEntity === 'group' && integrationAuthTeams && integrationAuthTeams.length > 0) {
// if (integrationAuthTeams.length > 0) { if (integrationAuthTeams) {
// setTargetTeam(integrationAuthTeams[0].name); if (integrationAuthTeams.length > 0) {
// } else { // case: user is part of at least 1 group in GitLab
// setTargetTeam('none'); setTargetTeamId(integrationAuthTeams[0].teamId);
// } } else {
// } // case: user is not part of any groups in GitLab
setTargetTeamId('none');
// if (targetEntity === 'group') { }
// if (integrationAuthTeams && integrationAuthTeams.length > 0) { }
// setTargetTeamId(integrationAuthTeams[0].teamId); } else if (targetEntity === 'individual') {
// } else { setTargetTeamId(null);
// setTargetTeamId(''); }
// } }, [targetEntity, integrationAuthTeams]);
// } else {
// }
}, [integrationAuthTeams, integrationAuthApps, targetEntity]);
const handleButtonClick = async () => { const handleButtonClick = async () => {
try { try {
@@ -95,8 +85,8 @@ export default function GitLabCreateIntegrationPage() {
await createIntegration({ await createIntegration({
integrationAuthId: integrationAuth?._id, integrationAuthId: integrationAuth?._id,
isActive: true, isActive: true,
app: targetApp, app: (integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.appId === targetAppId))?.name ?? null,
appId: (integrationAuthApps?.find((integrationAuthApp) => integrationAuthApp.name === targetApp))?.appId ?? null, appId: targetAppId,
sourceEnvironment: selectedSourceEnvironment, sourceEnvironment: selectedSourceEnvironment,
targetEnvironment: null, targetEnvironment: null,
owner: null, owner: null,
@@ -113,15 +103,7 @@ export default function GitLabCreateIntegrationPage() {
} }
} }
console.log('A', (integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps && integrationAuthTeams && targetApp && targetTeam)); return (integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps && integrationAuthTeams && targetAppId) ? (
console.log('B', integrationAuth);
console.log('C', workspace);
console.log('D', selectedSourceEnvironment);
console.log('E', integrationAuthApps);
console.log('F', integrationAuthTeams);
console.log('G', targetApp);
console.log('H', targetTeam);
return (integrationAuth && workspace && selectedSourceEnvironment && integrationAuthApps && integrationAuthTeams && targetApp && targetTeam) ? (
<div className="h-full w-full flex justify-center items-center"> <div className="h-full w-full flex justify-center items-center">
<Card className="max-w-md p-8 rounded-md"> <Card className="max-w-md p-8 rounded-md">
<CardTitle className='text-center'>GitLab Integration</CardTitle> <CardTitle className='text-center'>GitLab Integration</CardTitle>
@@ -165,13 +147,13 @@ export default function GitLabCreateIntegrationPage() {
className='mt-4' className='mt-4'
> >
<Select <Select
value={targetTeam} value={targetTeamId}
onValueChange={(val) => setTargetTeam(val)} onValueChange={(val) => setTargetTeamId(val)}
className='w-full border border-mineshaft-500' className='w-full border border-mineshaft-500'
> >
{integrationAuthTeams.length > 0 ? ( {integrationAuthTeams.length > 0 ? (
integrationAuthTeams.map((integrationAuthTeam) => ( integrationAuthTeams.map((integrationAuthTeam) => (
<SelectItem value={integrationAuthTeam.name} key={`target-team-${integrationAuthTeam.name}`}> <SelectItem value={integrationAuthTeam.teamId} key={`target-team-${integrationAuthTeam.teamId}`}>
{integrationAuthTeam.name} {integrationAuthTeam.name}
</SelectItem> </SelectItem>
)) ))
@@ -188,14 +170,14 @@ export default function GitLabCreateIntegrationPage() {
className='mt-4' className='mt-4'
> >
<Select <Select
value={targetApp} value={targetAppId}
onValueChange={(val) => setTargetApp(val)} onValueChange={(val) => setTargetAppId(val)}
className='w-full border border-mineshaft-500' className='w-full border border-mineshaft-500'
isDisabled={integrationAuthApps.length === 0} isDisabled={integrationAuthApps.length === 0}
> >
{integrationAuthApps.length > 0 ? ( {integrationAuthApps.length > 0 ? (
integrationAuthApps.map((integrationAuthApp) => ( integrationAuthApps.map((integrationAuthApp) => (
<SelectItem value={integrationAuthApp.name} key={`target-app-${integrationAuthApp.name}`}> <SelectItem value={integrationAuthApp.appId as string} key={`target-app-${integrationAuthApp.appId}`}>
{integrationAuthApp.name} {integrationAuthApp.name}
</SelectItem> </SelectItem>
)) ))