diff --git a/src/__tests__/fixtures/gitlab-data.ts b/src/__tests__/fixtures/gitlab-data.ts index 22aa558..b7113c1 100644 --- a/src/__tests__/fixtures/gitlab-data.ts +++ b/src/__tests__/fixtures/gitlab-data.ts @@ -4,6 +4,7 @@ import { CommitFileDiff, MergeRequest } from '../../types'; export const TEST_COMPONENT_ID = 'ari:cloud:compass:4958bb5d-3970-4a13-bebc-62bbca57f370:component/5ce8c075-7b72-4455-9be9-7f0a1c6e6db4/23b718b0-26a9-4654-9a48-4390a3e811dd'; +export const BASE_URL = 'https://gitlab.com'; export const TEST_TOKEN = 'glpat-geTHYDSDGHJJ'; export const TEST_GET_PROJECT_BY_ID_RESPONSE = { diff --git a/src/client/gitlab.ts b/src/client/gitlab.ts index 01a4c16..5e143b6 100644 --- a/src/client/gitlab.ts +++ b/src/client/gitlab.ts @@ -1,7 +1,6 @@ -import { fetch } from '@forge/api'; +import { fetch, storage } from '@forge/api'; import yaml from 'js-yaml'; -import { BASE_URL } from '../constants'; import { GitlabAPIGroup, GroupAccessToken, @@ -20,6 +19,7 @@ import { import { GitlabHttpMethodError, InvalidConfigFileError } from '../models/errors'; import { INVALID_YAML_ERROR } from '../models/error-messages'; import { queryParamsGenerator } from '../utils/url-utils'; +import { STORAGE_KEYS } from '../constants'; export enum HttpMethod { GET = 'GET', @@ -45,7 +45,7 @@ export enum GitLabHeaders { export type GitlabPaginatedFetch = ( page: number, perPage: number, - fetchFnParameters: Record<'groupToken', string> & P, + fetchFnParameters: Record<'baseUrl', string> & Record<'groupToken', string> & P, ) => Promise<{ data: D[]; headers: Headers }>; export enum MergeRequestWorkInProgressFilterOptions { @@ -64,13 +64,14 @@ function isTextBody(config?: CallGitLabConfig) { export const callGitlab = async ( apiOperation: string, - path: string, + url: string, authToken: string, config?: CallGitLabConfig, body?: string, ): Promise => { console.log(`Calling gitlab to ${apiOperation}`); - const resp = await fetch(`${BASE_URL}${path}`, { + + const resp = await fetch(`${url}`, { method: config?.method || HttpMethod.GET, headers: { 'PRIVATE-TOKEN': authToken, @@ -98,6 +99,7 @@ export const callGitlab = async ( }; export const getGroupsData = async ( + baseUrl: string, groupAccessToken: string, owned?: string, minAccessLevel?: number, @@ -111,7 +113,7 @@ export const getGroupsData = async ( const { data } = await callGitlab( `getGroupsData - Query params: ${queryParams}`, - `/api/v4/groups?${queryParams}`, + `${baseUrl}/api/v4/groups?${queryParams}`, groupAccessToken, ); @@ -119,12 +121,12 @@ export const getGroupsData = async ( }; export const registerGroupWebhook = async (payload: RegisterWebhookPayload): Promise => { - const { groupId, token: groupToken, url, signature } = payload; + const { groupId, baseUrl, token: groupToken, url, signature } = payload; const { data: { id }, } = await callGitlab( `register webhook`, - `/api/v4/groups/${groupId}/hooks`, + `${baseUrl}/api/v4/groups/${groupId}/hooks`, groupToken, { method: HttpMethod.POST }, JSON.stringify({ @@ -140,9 +142,14 @@ export const registerGroupWebhook = async (payload: RegisterWebhookPayload): Pro return id; }; -export const deleteGroupWebhook = async (groupId: number, hookId: number, groupToken: string): Promise => { +export const deleteGroupWebhook = async ( + groupId: number, + hookId: number, + baseUrl: string, + groupToken: string, +): Promise => { try { - await callGitlab('delete webhook', `/api/v4/groups/${groupId}/hooks/${hookId}`, groupToken, { + await callGitlab('delete webhook', `${baseUrl}/api/v4/groups/${groupId}/hooks/${hookId}`, groupToken, { method: HttpMethod.DELETE, }); } catch (e) { @@ -156,10 +163,15 @@ export const deleteGroupWebhook = async (groupId: number, hookId: number, groupT export const getGroupWebhook = async ( groupId: number, hookId: number, + baseUrl: string, groupToken: string, ): Promise<{ id: number } | null> => { try { - const { data: webhook } = await callGitlab('get webhook', `/api/v4/groups/${groupId}/hooks/${hookId}`, groupToken); + const { data: webhook } = await callGitlab( + 'get webhook', + `${baseUrl}/api/v4/groups/${groupId}/hooks/${hookId}`, + groupToken, + ); return webhook; } catch (e) { @@ -170,20 +182,29 @@ export const getGroupWebhook = async ( } }; -export const getGroupAccessTokens = async (groupToken: string, groupId: number): Promise => { +export const getGroupAccessTokens = async ( + baseUrl: string, + groupToken: string, + groupId: number, +): Promise => { const { data: groupAccessTokenList } = await callGitlab( 'get group access tokens', - `/api/v4/groups/${groupId}/access_tokens`, + `${baseUrl}/api/v4/groups/${groupId}/access_tokens`, groupToken, ); return groupAccessTokenList; }; -export const getCommitDiff = async (groupToken: string, projectId: number, sha: string): Promise => { +export const getCommitDiff = async ( + baseUrl: string, + groupToken: string, + projectId: number, + sha: string, +): Promise => { const { data: diff } = await callGitlab( 'get commit diff', - `/api/v4/projects/${projectId}/repository/commits/${sha}/diff`, + `${baseUrl}/api/v4/projects/${projectId}/repository/commits/${sha}/diff`, groupToken, ); @@ -191,6 +212,7 @@ export const getCommitDiff = async (groupToken: string, projectId: number, sha: }; export const getFileContent = async ( + baseUrl: string, groupToken: string, projectId: number, filePath: string, @@ -203,7 +225,7 @@ export const getFileContent = async ( const queryParams = queryParamsGenerator(params); const fileRaw = await callGitlab( 'get file contents', - `/api/v4/projects/${projectId}/repository/files/${encodeURIComponent(filePath)}/raw?${queryParams}`, + `${baseUrl}/api/v4/projects/${projectId}/repository/files/${encodeURIComponent(filePath)}/raw?${queryParams}`, groupToken, { contentType: GitLabContentType.RAW }, ); @@ -217,6 +239,7 @@ export const getFileContent = async ( }; export const getProjects = async ( + baseUrl: string, groupToken: string, groupId: number, page: number, @@ -233,23 +256,31 @@ export const getProjects = async ( const queryParams = queryParamsGenerator(params); const { data, headers } = await callGitlab( 'get projects', - `/api/v4/groups/${groupId}/projects?${queryParams}`, + `${baseUrl}/api/v4/groups/${groupId}/projects?${queryParams}`, groupToken, ); return { data, headers }; }; -export const getProjectById = async (groupToken: string, projectId: number): Promise => { - const { data: project } = await callGitlab('get project by id', `/api/v4/projects/${projectId}`, groupToken); +export const getProjectById = async ( + baseUrl: string, + groupToken: string, + projectId: number, +): Promise => { + const { data: project } = await callGitlab( + 'get project by id', + `${baseUrl}/api/v4/projects/${projectId}`, + groupToken, + ); return project; }; -export const getProjectLanguages = async (groupToken: string, projectId: number) => { +export const getProjectLanguages = async (baseUrl: string, groupToken: string, projectId: number) => { const { data: languages } = await callGitlab( 'get project languages', - `/api/v4/projects/${projectId}/languages`, + `${baseUrl}/api/v4/projects/${projectId}/languages`, groupToken, ); @@ -257,6 +288,7 @@ export const getProjectLanguages = async (groupToken: string, projectId: number) }; export const getProjectVariable = async ( + baseUrl: string, groupToken: string, projectId: number, variable: string, @@ -265,20 +297,21 @@ export const getProjectVariable = async ( data: { value }, } = await callGitlab( `get variable ${variable}. It is normal for the request to 404 if the variable does not exist.`, - `/api/v4/projects/${projectId}/variables/${variable}`, + `${baseUrl}/api/v4/projects/${projectId}/variables/${variable}`, groupToken, ); return value; }; export const getProjectBranch = async ( + baseUrl: string, groupToken: string, projectId: number, branchName: string, ): Promise => { const { data: branch } = await callGitlab( 'get project branch', - `/api/v4/projects/${projectId}/repository/branches/${branchName}`, + `${baseUrl}/api/v4/projects/${projectId}/repository/branches/${branchName}`, groupToken, ); return branch; @@ -286,6 +319,7 @@ export const getProjectBranch = async ( export const getOwnedProjectsBySearchCriteria = async ( search: string, + baseUrl: string, groupToken: string, ): Promise => { const params = { @@ -296,7 +330,7 @@ export const getOwnedProjectsBySearchCriteria = async ( const queryParams = queryParamsGenerator(params); const { data } = await callGitlab( 'get owned projects by search criteria', - `/api/v4/projects?${queryParams}`, + `${baseUrl}/api/v4/projects?${queryParams}`, groupToken, ); @@ -312,7 +346,7 @@ export const getProjectRecentDeployments: GitlabPaginatedFetch< dateBefore?: string; // in iso date time format, e.g. 2019-03-15T08:00:00Z } > = async (page, perPage, fetchParameters) => { - const { groupToken, projectId, dateAfter, dateBefore, environmentName } = fetchParameters; + const { baseUrl, groupToken, projectId, dateAfter, dateBefore, environmentName } = fetchParameters; const params = { updated_after: dateAfter, order_by: 'updated_at', @@ -323,9 +357,9 @@ export const getProjectRecentDeployments: GitlabPaginatedFetch< }; const queryParams = queryParamsGenerator(params); - const path = `/api/v4/projects/${projectId}/deployments?${queryParams}`; + const url = `${baseUrl}/api/v4/projects/${projectId}/deployments?${queryParams}`; - const { data, headers } = await callGitlab("get project's recent deployments", path, groupToken); + const { data, headers } = await callGitlab("get project's recent deployments", url, groupToken); return { data, headers }; }; @@ -333,6 +367,7 @@ export const getProjectRecentDeployments: GitlabPaginatedFetch< export const getMergeRequests: GitlabPaginatedFetch< MergeRequest, { + baseUrl: string; groupToken: string; projectId: number; state: MergeRequestState; @@ -344,7 +379,7 @@ export const getMergeRequests: GitlabPaginatedFetch< sourceBranch?: string; } > = async (page, perPage, fetchParameters) => { - const { state, scope, targetBranch, orderBy, wip, isSimpleView, projectId, groupToken, sourceBranch } = + const { state, scope, targetBranch, orderBy, wip, isSimpleView, projectId, baseUrl, groupToken, sourceBranch } = fetchParameters; const params = { @@ -360,27 +395,36 @@ export const getMergeRequests: GitlabPaginatedFetch< }; const queryParams = queryParamsGenerator(params); - const path = `/api/v4/projects/${projectId}/merge_requests?${queryParams}`; + const url = `${baseUrl}/api/v4/projects/${projectId}/merge_requests?${queryParams}`; - const { data, headers } = await callGitlab('get merge requests', path, groupToken); + const { data, headers } = await callGitlab('get merge requests', url, groupToken); return { data, headers }; }; -export const getProjectDeploymentById = async (projectId: number, deploymentId: number, groupToken: string) => { +export const getProjectDeploymentById = async ( + projectId: number, + deploymentId: number, + baseUrl: string, + groupToken: string, +) => { const { data } = await callGitlab( 'get project deployment by id', - `/api/v4/projects/${projectId}/deployments/${deploymentId}`, + `${baseUrl}/api/v4/projects/${projectId}/deployments/${deploymentId}`, groupToken, ); return data; }; -export const getEnvironments = async (projectId: number, groupToken: string): Promise => { +export const getEnvironments = async ( + projectId: number, + baseUrl: string, + groupToken: string, +): Promise => { const { data } = await callGitlab( "get project's environments", - `/api/v4/projects/${projectId}/environments`, + `${baseUrl}/api/v4/projects/${projectId}/environments`, groupToken, ); @@ -396,7 +440,7 @@ export const getProjectRecentPipelines: GitlabPaginatedFetch< status?: GitlabPipelineStates; } > = async (page, perPage, fetchParameters) => { - const { groupToken, projectId, dateAfter, branchName, status } = fetchParameters; + const { baseUrl, groupToken, projectId, dateAfter, branchName, status } = fetchParameters; const params = { ref: branchName, page: page.toString(), @@ -406,13 +450,14 @@ export const getProjectRecentPipelines: GitlabPaginatedFetch< }; const queryParams = queryParamsGenerator(params); - const path = `/api/v4/projects/${projectId}/pipelines?${queryParams}`; + const url = `${baseUrl}/api/v4/projects/${projectId}/pipelines?${queryParams}`; - const { data, headers } = await callGitlab("get project's recent pipelines", path, groupToken); + const { data, headers } = await callGitlab("get project's recent pipelines", url, groupToken); return { data, headers }; }; export const createFileInProject = async ( + baseUrl: string, groupToken: string, projectId: number, filePath: string, @@ -422,11 +467,9 @@ export const createFileInProject = async ( content: string, commitMessage: string, ) => { - const path = `/api/v4/projects/${projectId}/repository/files/${filePath}`; - const { data } = await callGitlab( 'create file in project', - path, + `${baseUrl}/api/v4/projects/${projectId}/repository/files/${filePath}`, groupToken, { method: HttpMethod.POST }, JSON.stringify({ @@ -442,6 +485,7 @@ export const createFileInProject = async ( }; export const createMergeRequest = async ( + baseUrl: string, groupToken: string, projectId: number, sourceBranch: string, @@ -450,11 +494,9 @@ export const createMergeRequest = async ( description: string, removeSourceBranch: boolean, ) => { - const path = `/api/v4/projects/${projectId}/merge_requests`; - const { data } = await callGitlab( 'create merge request', - path, + `${baseUrl}/api/v4/projects/${projectId}/merge_requests`, groupToken, { method: HttpMethod.POST }, JSON.stringify({ diff --git a/src/constants.ts b/src/constants.ts index d61628a..7bd3795 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -5,6 +5,7 @@ export const GITLAB_EVENT_WEBTRIGGER = 'gitlab-event-webtrigger'; export const DEFAULT_COMPONENT_TYPE_ID = 'SERVICE'; export const STORAGE_KEYS = { + BASE_URL: 'baseUrl', GROUP_KEY_PREFIX: 'group-', WEBHOOK_KEY_PREFIX: 'webhook-id-', WEBHOOK_SIGNATURE_PREFIX: 'webhook-sign-id-', diff --git a/src/entry/data-provider/index.ts b/src/entry/data-provider/index.ts index 144bbac..2fdf8ae 100644 --- a/src/entry/data-provider/index.ts +++ b/src/entry/data-provider/index.ts @@ -27,6 +27,7 @@ export const dataProvider = async ( const { project: { id: projectId, default_branch: defaultBranch, name: projectName }, + baseUrl, groupToken, } = await getProjectDataFromUrl(request.url); @@ -35,7 +36,7 @@ export const dataProvider = async ( return null; } - const trackingBranch = await getTrackingBranchName(groupToken, projectId, defaultBranch); + const trackingBranch = await getTrackingBranchName(baseUrl, groupToken, projectId, defaultBranch); const backfillData: BackfillData = { builds: [], @@ -51,7 +52,7 @@ export const dataProvider = async ( builds, deployments, metrics: { mrCycleTime, openMergeRequestsCount }, - } = await getBackfillData(groupToken, projectId, projectName, trackingBranch); + } = await getBackfillData(baseUrl, groupToken, projectId, projectName, trackingBranch); backfillData.builds = builds; backfillData.deployments = deployments; diff --git a/src/entry/data-provider/test.ts b/src/entry/data-provider/test.ts index 296ab0b..1553784 100644 --- a/src/entry/data-provider/test.ts +++ b/src/entry/data-provider/test.ts @@ -69,7 +69,8 @@ const MOCK_EVENTS_RESPONSE: { openMergeRequestsCount: 3, }, }; -const MOCK_PROJECT_URL = 'https://gitlab.com/test/repo-name?testParam=test'; +const MOCK_BASE_URL = 'https://gitlab.com/test/repo-name?testParam=test'; +const MOCK_PROJECT_URL = `${MOCK_BASE_URL}/test/repo-name?testParam=test`; const MOCK_PROJECT: GitlabAPIProject = { id: 1, description: 'description', @@ -91,6 +92,7 @@ describe('dataProvider module', () => { getEventsSpy.mockResolvedValue(MOCK_EVENTS_RESPONSE); projectDataSpy.mockResolvedValue({ project: MOCK_PROJECT, + baseUrl: MOCK_BASE_URL, groupToken: 'mock-group-token', }); @@ -116,6 +118,7 @@ describe('dataProvider module', () => { getEventsSpy.mockRejectedValue(new GitlabHttpMethodError(InvocationStatusCode.INTERNAL_SERVER_ERROR, error)); projectDataSpy.mockResolvedValue({ + baseUrl: MOCK_BASE_URL, project: MOCK_PROJECT, groupToken: 'mock-group-token', }); diff --git a/src/entry/webtriggers/gitlab-event-handlers/handle-deployment-event.test.ts b/src/entry/webtriggers/gitlab-event-handlers/handle-deployment-event.test.ts index 071f2d4..5a8c5f8 100644 --- a/src/entry/webtriggers/gitlab-event-handlers/handle-deployment-event.test.ts +++ b/src/entry/webtriggers/gitlab-event-handlers/handle-deployment-event.test.ts @@ -10,7 +10,7 @@ import { CompassDeploymentEventState, } from '@atlassian/forge-graphql'; import { generateDeploymentEvent, generateEnvironmentEvent } from '../../../__tests__/helpers/gitlab-helper'; -import { MOCK_CLOUD_ID, TEST_TOKEN } from '../../../__tests__/fixtures/gitlab-data'; +import { BASE_URL, MOCK_CLOUD_ID, TEST_TOKEN } from '../../../__tests__/fixtures/gitlab-data'; import { getEnvironments } from '../../../client/gitlab'; import { getDeployment } from '../../../services/deployment'; import { sendEventToCompass } from '../../../services/send-compass-events'; @@ -88,7 +88,7 @@ describe('GitLab deployment event', () => { mockedGetEnvironments.mockResolvedValue([MOCK_ENVIRONMENTS_EVENT]); mockedGetDeployment.mockResolvedValue(MOCK_DEPLOYMENT_EVENT_INPUT); - await handleDeploymentEvent(MOCK_DEPLOYMENT_EVENT, TEST_TOKEN, MOCK_CLOUD_ID); + await handleDeploymentEvent(MOCK_DEPLOYMENT_EVENT, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(mockedSendEventsToCompass).toHaveBeenCalledWith(MOCK_DEPLOYMENT_EVENT_INPUT); }); @@ -97,7 +97,7 @@ describe('GitLab deployment event', () => { const MOCK_STAGING_ENVIRONMENTS_EVENT = generateEnvironmentEvent(EnvironmentTier.STAGING); mockedGetEnvironments.mockResolvedValue([MOCK_STAGING_ENVIRONMENTS_EVENT]); - await handleDeploymentEvent(MOCK_DEPLOYMENT_EVENT, TEST_TOKEN, MOCK_CLOUD_ID); + await handleDeploymentEvent(MOCK_DEPLOYMENT_EVENT, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(mockedSendEventsToCompass).not.toHaveBeenCalled(); }); @@ -108,7 +108,7 @@ describe('GitLab deployment event', () => { mockedGetEnvironments.mockResolvedValue([MOCK_PRODUCTION_ENVIRONMENT_EVENT]); - await expect(handleDeploymentEvent(MOCK_PRD_DEPLOYMENT_EVENT, TEST_TOKEN, MOCK_CLOUD_ID)).rejects.toThrow( + await expect(handleDeploymentEvent(MOCK_PRD_DEPLOYMENT_EVENT, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID)).rejects.toThrow( 'Environment with name "prd" not found', ); }); @@ -131,7 +131,7 @@ describe('GitLab deployment event', () => { mockedGetEnvironments.mockResolvedValue([MOCK_STAGING_ENVIRONMENTS_EVENT]); mockedGetDeployment.mockResolvedValue(MOCK_STAGING_DEPLOYMENT_EVENT_INPUT); - await handleDeploymentEvent(MOCK_STAGING_DEPLOYMENT_EVENT, TEST_TOKEN, MOCK_CLOUD_ID); + await handleDeploymentEvent(MOCK_STAGING_DEPLOYMENT_EVENT, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(mockedSendEventsToCompass).toHaveBeenCalledWith(MOCK_STAGING_DEPLOYMENT_EVENT_INPUT); }); @@ -141,7 +141,7 @@ describe('GitLab deployment event', () => { const MOCK_TESTING_DEPLOYMENT_EVENT = generateDeploymentEvent({ environment: 'testing' }); mockedGetEnvironments.mockResolvedValue([MOCK_TESTING_ENVIRONMENTS_EVENT]); - await handleDeploymentEvent(MOCK_TESTING_DEPLOYMENT_EVENT, TEST_TOKEN, MOCK_CLOUD_ID); + await handleDeploymentEvent(MOCK_TESTING_DEPLOYMENT_EVENT, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(mockedGetDeployment).not.toHaveBeenCalled(); expect(mockedSendEventsToCompass).not.toHaveBeenCalled(); diff --git a/src/entry/webtriggers/gitlab-event-handlers/handle-deployment-event.ts b/src/entry/webtriggers/gitlab-event-handlers/handle-deployment-event.ts index 52ce5a5..020e0db 100644 --- a/src/entry/webtriggers/gitlab-event-handlers/handle-deployment-event.ts +++ b/src/entry/webtriggers/gitlab-event-handlers/handle-deployment-event.ts @@ -6,6 +6,7 @@ import { isSendStagingEventsEnabled } from '../../../services/feature-flags'; export const handleDeploymentEvent = async ( event: DeploymentEvent, + baseUrl: string, groupToken: string, cloudId: string, ): Promise => { @@ -13,7 +14,7 @@ export const handleDeploymentEvent = async ( environment, project: { id: projectId }, } = event; - const environments = await getProjectEnvironments(projectId, groupToken); + const environments = await getProjectEnvironments(projectId, baseUrl, groupToken); const environmentTier = await getEnvironmentTier(environments, environment); @@ -21,7 +22,7 @@ export const handleDeploymentEvent = async ( environmentTier === EnvironmentTier.PRODUCTION || (isSendStagingEventsEnabled() && environmentTier === EnvironmentTier.STAGING) ) { - const deployment = await getDeployment(event, groupToken, environmentTier, cloudId); + const deployment = await getDeployment(event, baseUrl, groupToken, environmentTier, cloudId); await sendEventToCompass(deployment); } }; diff --git a/src/entry/webtriggers/gitlab-event-handlers/handle-merge-request-event.test.ts b/src/entry/webtriggers/gitlab-event-handlers/handle-merge-request-event.test.ts index 6732c55..8a46bdc 100644 --- a/src/entry/webtriggers/gitlab-event-handlers/handle-merge-request-event.test.ts +++ b/src/entry/webtriggers/gitlab-event-handlers/handle-merge-request-event.test.ts @@ -9,7 +9,7 @@ import { BuiltinMetricDefinitions } from '@atlassian/forge-graphql'; import { insertMetricValues } from '../../../services/insert-metric-values'; import { getLastMergedMergeRequests, getOpenMergeRequests } from '../../../services/mergeRequest'; import { getTrackingBranchName } from '../../../services/get-tracking-branch'; -import { mergeRequests, MOCK_CLOUD_ID, TEST_TOKEN } from '../../../__tests__/fixtures/gitlab-data'; +import { BASE_URL, mergeRequests, MOCK_CLOUD_ID, TEST_TOKEN } from '../../../__tests__/fixtures/gitlab-data'; import { handleMergeRequestEvent } from './handle-merge-request-event'; import { generateMergeRequestEvent, @@ -41,7 +41,7 @@ describe('Gitlab merge request', () => { mockedGetLastMergedMergeRequests.mockResolvedValue(mergeRequests); mockedGetOpenMergeRequests.mockResolvedValue(mergeRequests); - await handleMergeRequestEvent(MOCK_MERGE_REQUEST_EVENT, TEST_TOKEN, MOCK_CLOUD_ID); + await handleMergeRequestEvent(MOCK_MERGE_REQUEST_EVENT, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(mockedInsertMetricValues).toHaveBeenCalledWith(MOCK_METRIC_INPUT, MOCK_CLOUD_ID); }); diff --git a/src/entry/webtriggers/gitlab-event-handlers/handle-merge-request-event.ts b/src/entry/webtriggers/gitlab-event-handlers/handle-merge-request-event.ts index 4b958d9..3b90742 100644 --- a/src/entry/webtriggers/gitlab-event-handlers/handle-merge-request-event.ts +++ b/src/entry/webtriggers/gitlab-event-handlers/handle-merge-request-event.ts @@ -6,6 +6,7 @@ import { getMRCycleTime, getOpenMergeRequestsCount } from '../../../services/com export const handleMergeRequestEvent = async ( event: MergeRequestEvent, + baseUrl: string, groupToken: string, cloudId: string, ): Promise => { @@ -14,12 +15,12 @@ export const handleMergeRequestEvent = async ( project: { id, default_branch: defaultBranch }, object_attributes: { target_branch: targetBranch }, } = event; - const trackingBranch = await getTrackingBranchName(groupToken, id, defaultBranch); + const trackingBranch = await getTrackingBranchName(baseUrl, groupToken, id, defaultBranch); if (trackingBranch === targetBranch) { const [cycleTime, openMergeRequestsCount] = await Promise.all([ - getMRCycleTime(groupToken, id, trackingBranch), - getOpenMergeRequestsCount(groupToken, id, trackingBranch), + getMRCycleTime(baseUrl, groupToken, id, trackingBranch), + getOpenMergeRequestsCount(baseUrl, groupToken, id, trackingBranch), ]); const metricInput = { diff --git a/src/entry/webtriggers/gitlab-event-handlers/handle-pipeline-event.test.ts b/src/entry/webtriggers/gitlab-event-handlers/handle-pipeline-event.test.ts index ea0b4e8..3cf03a4 100644 --- a/src/entry/webtriggers/gitlab-event-handlers/handle-pipeline-event.test.ts +++ b/src/entry/webtriggers/gitlab-event-handlers/handle-pipeline-event.test.ts @@ -9,7 +9,7 @@ import { generatePipelineEvent } from '../../../__tests__/helpers/gitlab-helper' import { handlePipelineEvent } from './handle-pipeline-event'; import { getTrackingBranchName } from '../../../services/get-tracking-branch'; import { sendEventToCompass } from '../../../services/send-compass-events'; -import { TEST_TOKEN, MOCK_CLOUD_ID } from '../../../__tests__/fixtures/gitlab-data'; +import { TEST_TOKEN, MOCK_CLOUD_ID, BASE_URL } from '../../../__tests__/fixtures/gitlab-data'; import { webhookPipelineEventToCompassBuildEvent } from '../../../services/builds'; import { insertMetricValues } from '../../../services/insert-metric-values'; @@ -39,7 +39,7 @@ describe('Gitlab events', () => { it('ignores event if the branch is not default and non-default branch wasn`t set', async () => { getTrackingBranchNameMock.mockResolvedValue(eventWithIncorrectRef.project.default_branch); - await handlePipelineEvent(eventWithIncorrectRef, TEST_TOKEN, MOCK_CLOUD_ID); + await handlePipelineEvent(eventWithIncorrectRef, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(sendEventToCompassMock).not.toBeCalled(); expect(insertMetricValuesMock).not.toBeCalled(); @@ -48,7 +48,7 @@ describe('Gitlab events', () => { it('ingests build event for main branch', async () => { getTrackingBranchNameMock.mockResolvedValue(event.project.default_branch); - await handlePipelineEvent(event, TEST_TOKEN, MOCK_CLOUD_ID); + await handlePipelineEvent(event, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(sendEventToCompassMock).toBeCalledTimes(1); expect(sendEventToCompassMock).toBeCalledWith(webhookPipelineEventToCompassBuildEvent(event, MOCK_CLOUD_ID)); @@ -65,7 +65,7 @@ describe('Gitlab events', () => { }, }); - await handlePipelineEvent(nonDefaultBranchEvent, TEST_TOKEN, MOCK_CLOUD_ID); + await handlePipelineEvent(nonDefaultBranchEvent, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(sendEventToCompassMock).toBeCalledTimes(1); expect(sendEventToCompassMock).toBeCalledWith( diff --git a/src/entry/webtriggers/gitlab-event-handlers/handle-pipeline-event.ts b/src/entry/webtriggers/gitlab-event-handlers/handle-pipeline-event.ts index 7d6dc35..39835e9 100644 --- a/src/entry/webtriggers/gitlab-event-handlers/handle-pipeline-event.ts +++ b/src/entry/webtriggers/gitlab-event-handlers/handle-pipeline-event.ts @@ -7,13 +7,18 @@ export const isEventForTrackingBranch = (event: PipelineEvent, trackingBranch: s return event.object_attributes.ref === trackingBranch; }; -export const handlePipelineEvent = async (event: PipelineEvent, groupToken: string, cloudId: string): Promise => { +export const handlePipelineEvent = async ( + event: PipelineEvent, + baseUrl: string, + groupToken: string, + cloudId: string, +): Promise => { const { project: { id: projectId, default_branch: defaultBranch }, object_attributes: { ref }, } = event; - const trackingBranch = await getTrackingBranchName(groupToken, projectId, defaultBranch); + const trackingBranch = await getTrackingBranchName(baseUrl, groupToken, projectId, defaultBranch); if (!isEventForTrackingBranch(event, trackingBranch)) { console.log({ diff --git a/src/entry/webtriggers/gitlab-event-handlers/handle-push-event.test.ts b/src/entry/webtriggers/gitlab-event-handlers/handle-push-event.test.ts index 3a31bed..137894d 100644 --- a/src/entry/webtriggers/gitlab-event-handlers/handle-push-event.test.ts +++ b/src/entry/webtriggers/gitlab-event-handlers/handle-push-event.test.ts @@ -9,7 +9,7 @@ import { generatePushEvent } from '../../../__tests__/helpers/gitlab-helper'; import { handlePushEvent } from './handle-push-event'; import { findConfigAsCodeFileChanges, syncComponent } from '../../../services/sync-component-with-file'; import { getTrackingBranchName } from '../../../services/get-tracking-branch'; -import { MOCK_CLOUD_ID, TEST_TOKEN } from '../../../__tests__/fixtures/gitlab-data'; +import { BASE_URL, MOCK_CLOUD_ID, TEST_TOKEN } from '../../../__tests__/fixtures/gitlab-data'; import { ComponentSyncDetails } from '../../../types'; import { EXTERNAL_SOURCE } from '../../../constants'; @@ -62,7 +62,7 @@ describe('Gitlab push events', () => { it('ignores event if the branch is not tracking', async () => { getNonDefaultBranchNameMock.mockResolvedValue(eventWithIncorrectRef.project.default_branch); - await handlePushEvent(eventWithIncorrectRef, TEST_TOKEN, MOCK_CLOUD_ID); + await handlePushEvent(eventWithIncorrectRef, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(syncs).not.toBeCalled(); expect(removals).not.toBeCalled(); @@ -71,7 +71,7 @@ describe('Gitlab push events', () => { it('ignores event if no config as code file updates present', async () => { getNonDefaultBranchNameMock.mockResolvedValue(event.project.default_branch); findConfigChanges.mockResolvedValue({ componentsToCreate: [], componentsToUpdate: [], componentsToUnlink: [] }); - await handlePushEvent(event, TEST_TOKEN, MOCK_CLOUD_ID); + await handlePushEvent(event, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); expect(syncs).not.toBeCalled(); expect(removals).not.toBeCalled(); @@ -101,9 +101,10 @@ describe('Gitlab push events', () => { componentsToUnlink: mockComponentsToUnlink, }); - await handlePushEvent(event, TEST_TOKEN, MOCK_CLOUD_ID); + await handlePushEvent(event, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); const expectedComponentSyncDetails: ComponentSyncDetails = { + baseUrl: BASE_URL, token: TEST_TOKEN, event, trackingBranch: event.project.default_branch, @@ -150,9 +151,10 @@ describe('Gitlab push events', () => { componentsToUnlink: mockComponentsToUnlink, }); - await handlePushEvent(pushEvent, TEST_TOKEN, MOCK_CLOUD_ID); + await handlePushEvent(pushEvent, BASE_URL, TEST_TOKEN, MOCK_CLOUD_ID); const expectedComponentSyncDetails: ComponentSyncDetails = { + baseUrl: BASE_URL, token: TEST_TOKEN, event: pushEvent, trackingBranch: BRANCH_NAME, diff --git a/src/entry/webtriggers/gitlab-event-handlers/handle-push-event.ts b/src/entry/webtriggers/gitlab-event-handlers/handle-push-event.ts index e0b8791..a6babe0 100644 --- a/src/entry/webtriggers/gitlab-event-handlers/handle-push-event.ts +++ b/src/entry/webtriggers/gitlab-event-handlers/handle-push-event.ts @@ -6,8 +6,18 @@ import { getTrackingBranchName } from '../../../services/get-tracking-branch'; import { unlinkComponentFromFile } from '../../../client/compass'; import { EXTERNAL_SOURCE } from '../../../constants'; -export const handlePushEvent = async (event: PushEvent, groupToken: string, cloudId: string): Promise => { - const trackingBranch = await getTrackingBranchName(groupToken, event.project.id, event.project.default_branch); +export const handlePushEvent = async ( + event: PushEvent, + baseUrl: string, + groupToken: string, + cloudId: string, +): Promise => { + const trackingBranch = await getTrackingBranchName( + baseUrl, + groupToken, + event.project.id, + event.project.default_branch, + ); if (!isEventForTrackingBranch(event, trackingBranch)) { console.log('Received push event for non-tracking branch. Ignoring event'); @@ -18,6 +28,7 @@ export const handlePushEvent = async (event: PushEvent, groupToken: string, clou const { componentsToCreate, componentsToUpdate, componentsToUnlink } = await findConfigAsCodeFileChanges( event, + baseUrl, groupToken, ); @@ -33,6 +44,7 @@ export const handlePushEvent = async (event: PushEvent, groupToken: string, clou }); const componentSyncDetails: ComponentSyncDetails = { + baseUrl, token: groupToken, event, trackingBranch, diff --git a/src/entry/webtriggers/process-gitlab-event.ts b/src/entry/webtriggers/process-gitlab-event.ts index 6e2d6ec..a169510 100644 --- a/src/entry/webtriggers/process-gitlab-event.ts +++ b/src/entry/webtriggers/process-gitlab-event.ts @@ -37,6 +37,7 @@ export const processGitlabEvent = async (event: WebtriggerRequest, context: Cont const { installContext } = context; const cloudId = parse(installContext).resourceId; const groupId = event.queryParameters.groupId[0]; + const baseUrl = await storage.get(`${STORAGE_KEYS.BASE_URL}`); const groupToken = await storage.getSecret(`${STORAGE_SECRETS.GROUP_TOKEN_KEY_PREFIX}${groupId}`); const eventPayload = event.body; let parsedEvent: GitlabEvent; @@ -58,19 +59,19 @@ export const processGitlabEvent = async (event: WebtriggerRequest, context: Cont } if (parsedEvent.object_kind === 'push') { - await handlePushEvent(parsedEvent as PushEvent, groupToken, cloudId); + await handlePushEvent(parsedEvent as PushEvent, baseUrl, groupToken, cloudId); } if (parsedEvent.object_kind === 'merge_request') { - await handleMergeRequestEvent(parsedEvent as MergeRequestEvent, groupToken, cloudId); + await handleMergeRequestEvent(parsedEvent as MergeRequestEvent, baseUrl, groupToken, cloudId); } if (parsedEvent.object_kind === 'pipeline') { - await handlePipelineEvent(parsedEvent as PipelineEvent, groupToken, cloudId); + await handlePipelineEvent(parsedEvent as PipelineEvent, baseUrl, groupToken, cloudId); } if (parsedEvent.object_kind === 'deployment') { - await handleDeploymentEvent(parsedEvent as DeploymentEvent, groupToken, cloudId); + await handleDeploymentEvent(parsedEvent as DeploymentEvent, baseUrl, groupToken, cloudId); } return serverResponse('Processed webhook event'); diff --git a/src/resolvers.ts b/src/resolvers.ts index 65a86d9..a5be0b6 100644 --- a/src/resolvers.ts +++ b/src/resolvers.ts @@ -67,11 +67,11 @@ resolver.define('groups/connectedInfo', async (): Promise => { const { - payload: { groupToken, groupTokenName }, + payload: { baseUrl, groupToken, groupTokenName }, context: { cloudId }, } = req; try { - const groupId = await connectGroup(groupToken, groupTokenName); + const groupId = await connectGroup(baseUrl, groupToken, groupTokenName); await setupAndValidateWebhook(groupId); diff --git a/src/services/clear-storage.ts b/src/services/clear-storage.ts index ef3ad87..b30c1fc 100644 --- a/src/services/clear-storage.ts +++ b/src/services/clear-storage.ts @@ -21,6 +21,7 @@ const clearStorageEntriesForGroup = async (groupId: string): Promise => { console.log('Clearing storage entries start'); const groupKeys = [ + `${STORAGE_KEYS.BASE_URL}`, `${STORAGE_KEYS.GROUP_KEY_PREFIX}${groupId}`, `${STORAGE_KEYS.WEBHOOK_KEY_PREFIX}${groupId}`, `${STORAGE_KEYS.WEBHOOK_SIGNATURE_PREFIX}${groupId}`, diff --git a/src/services/compute-event-and-metrics/get-mr-cycle-time.ts b/src/services/compute-event-and-metrics/get-mr-cycle-time.ts index 934c1ce..dbfb0e1 100644 --- a/src/services/compute-event-and-metrics/get-mr-cycle-time.ts +++ b/src/services/compute-event-and-metrics/get-mr-cycle-time.ts @@ -2,11 +2,12 @@ import { mergeRequestCycleTime } from '../metric-calculations/merge-request-cycl import { getLastMergedMergeRequests } from '../mergeRequest'; export const getMRCycleTime = async ( + baseUrl: string, groupToken: string, projectId: number, trackingBranch: string, ): Promise => { - const mergeRequests = await getLastMergedMergeRequests(groupToken, projectId, trackingBranch); + const mergeRequests = await getLastMergedMergeRequests(baseUrl, groupToken, projectId, trackingBranch); return mergeRequestCycleTime(mergeRequests); }; diff --git a/src/services/compute-event-and-metrics/get-open-merge-requests.ts b/src/services/compute-event-and-metrics/get-open-merge-requests.ts index bc84481..e32dc74 100644 --- a/src/services/compute-event-and-metrics/get-open-merge-requests.ts +++ b/src/services/compute-event-and-metrics/get-open-merge-requests.ts @@ -1,11 +1,12 @@ import { getOpenMergeRequests } from '../mergeRequest'; export const getOpenMergeRequestsCount = async ( + baseUrl: string, groupToken: string, projectId: number, trackingBranch: string, ): Promise => { - const openMergeRequests = await getOpenMergeRequests(groupToken, projectId, trackingBranch); + const openMergeRequests = await getOpenMergeRequests(baseUrl, groupToken, projectId, trackingBranch); return openMergeRequests.length; }; diff --git a/src/services/compute-event-and-metrics/get-recent-builds.ts b/src/services/compute-event-and-metrics/get-recent-builds.ts index c664bf1..cfdd6a1 100644 --- a/src/services/compute-event-and-metrics/get-recent-builds.ts +++ b/src/services/compute-event-and-metrics/get-recent-builds.ts @@ -5,12 +5,14 @@ import { getDateInThePast } from '../../utils/time-utils'; import { fetchPaginatedData } from '../../utils/fetchPaginatedData'; export const getProjectBuildsFor28Days = async ( + baseUrl: string, groupToken: string, projectId: number, projectName: string, branchName: string, ): Promise => { const allPipelines = await fetchPaginatedData(getProjectRecentPipelines, { + baseUrl, groupToken, projectId, dateAfter: getDateInThePast(), diff --git a/src/services/compute-event-and-metrics/get-recent-deployments.test.ts b/src/services/compute-event-and-metrics/get-recent-deployments.test.ts index c8ed060..45c08fa 100644 --- a/src/services/compute-event-and-metrics/get-recent-deployments.test.ts +++ b/src/services/compute-event-and-metrics/get-recent-deployments.test.ts @@ -14,6 +14,7 @@ import { Deployment, EnvironmentTier } from '../../types'; import { getRecentDeployments, gitlabAPiDeploymentToCompassDataProviderDeploymentEvent } from '../deployment'; import { getDeploymentsForEnvironmentTiers } from './index'; import * as featureFlagService from '../feature-flags'; +import { BASE_URL } from '../../__tests__/fixtures/gitlab-data'; jest.mock('../environment'); jest.mock('../deployment'); @@ -55,7 +56,7 @@ describe('getDeploymentsForEnvironmentTiers', () => { const mockDeployment = getMockDeployment(); mockedGetRecentDeployments.mockResolvedValue([mockDeployment]); - const deployments = await getDeploymentsForEnvironmentTiers('groupToken', 1, 'projectName'); + const deployments = await getDeploymentsForEnvironmentTiers(BASE_URL, 'groupToken', 1, 'projectName'); expect(mockedGitlabAPiDeploymentToCompassDataProviderDeploymentEvent).toHaveBeenCalledTimes(1); expect(mockedGitlabAPiDeploymentToCompassDataProviderDeploymentEvent).toHaveBeenCalledWith( @@ -69,7 +70,7 @@ describe('getDeploymentsForEnvironmentTiers', () => { it('ignores non-Production events', async () => { mockedGetProjectEnvironments.mockResolvedValue([generateEnvironmentEvent(EnvironmentTier.STAGING)]); - const deployments = await getDeploymentsForEnvironmentTiers('groupToken', 1, 'projectName'); + const deployments = await getDeploymentsForEnvironmentTiers(BASE_URL, 'groupToken', 1, 'projectName'); expect(mockedGetRecentDeployments).not.toHaveBeenCalled(); expect(mockedGitlabAPiDeploymentToCompassDataProviderDeploymentEvent).not.toHaveBeenCalled(); @@ -83,7 +84,7 @@ describe('getDeploymentsForEnvironmentTiers', () => { mockedGetRecentDeployments.mockResolvedValue([mockDeployment]); mockedGitlabAPiDeploymentToCompassDataProviderDeploymentEvent.mockReturnValue(null); - const deployments = await getDeploymentsForEnvironmentTiers('groupToken', 1, 'projectName'); + const deployments = await getDeploymentsForEnvironmentTiers(BASE_URL, 'groupToken', 1, 'projectName'); expect(mockedGitlabAPiDeploymentToCompassDataProviderDeploymentEvent).toHaveBeenCalledTimes(1); expect(mockedGitlabAPiDeploymentToCompassDataProviderDeploymentEvent).toHaveBeenCalledWith( mockDeployment, @@ -129,7 +130,7 @@ describe('getDeploymentsForEnvironmentTiers', () => { return null; }); - const deployments = await getDeploymentsForEnvironmentTiers('groupToken', 1, 'projectName', [ + const deployments = await getDeploymentsForEnvironmentTiers(BASE_URL, 'groupToken', 1, 'projectName', [ EnvironmentTier.PRODUCTION, EnvironmentTier.STAGING, ]); @@ -150,7 +151,7 @@ describe('getDeploymentsForEnvironmentTiers', () => { generateEnvironmentEvent(EnvironmentTier.OTHER, 'otherEnvName'), ]); - const deployments = await getDeploymentsForEnvironmentTiers('groupToken', 1, 'projectName', [ + const deployments = await getDeploymentsForEnvironmentTiers(BASE_URL, 'groupToken', 1, 'projectName', [ EnvironmentTier.PRODUCTION, EnvironmentTier.STAGING, ]); diff --git a/src/services/compute-event-and-metrics/get-recent-deployments.ts b/src/services/compute-event-and-metrics/get-recent-deployments.ts index 425f77a..a6eac05 100644 --- a/src/services/compute-event-and-metrics/get-recent-deployments.ts +++ b/src/services/compute-event-and-metrics/get-recent-deployments.ts @@ -7,19 +7,26 @@ import { getDateInThePast } from '../../utils/time-utils'; import { isSendStagingEventsEnabled } from '../feature-flags'; const newGetDeploymentsForEnvironments = async ( + baseUrl: string, groupToken: string, projectId: number, projectName: string, environmentTiers: EnvironmentTier[], ): Promise => { // get all project environments - const projectEnvironments = await getProjectEnvironments(projectId, groupToken); + const projectEnvironments = await getProjectEnvironments(projectId, baseUrl, groupToken); // get deploymentEvents for each projectEnvironment that matches the requested environmentTiers const deploymentEvents = projectEnvironments .filter((projectEnv) => environmentTiers.includes(projectEnv.tier)) .map(async (projectEnv) => { - const recentDeployments = await getRecentDeployments(groupToken, projectId, getDateInThePast(), projectEnv.name); + const recentDeployments = await getRecentDeployments( + baseUrl, + groupToken, + projectId, + getDateInThePast(), + projectEnv.name, + ); const dataProviderDeploymentEvents = recentDeployments .map((deployment) => gitlabAPiDeploymentToCompassDataProviderDeploymentEvent(deployment, projectName, projectEnv.tier), @@ -39,21 +46,22 @@ const newGetDeploymentsForEnvironments = async ( }; export const getDeploymentsForEnvironmentTiers = async ( + baseUrl: string, groupToken: string, projectId: number, projectName: string, environmentTiers?: EnvironmentTier[], ): Promise => { if (isSendStagingEventsEnabled() && environmentTiers) { - return newGetDeploymentsForEnvironments(groupToken, projectId, projectName, environmentTiers); + return newGetDeploymentsForEnvironments(baseUrl, groupToken, projectId, projectName, environmentTiers); } - const environments = await getProjectEnvironments(projectId, groupToken); + const environments = await getProjectEnvironments(projectId, baseUrl, groupToken); const getDeploymentsPromises = environments.reduce[]>( (deploymentsPromises, currentEnvironment) => { if (currentEnvironment.tier === EnvironmentTier.PRODUCTION) { deploymentsPromises.push( - getRecentDeployments(groupToken, projectId, getDateInThePast(), currentEnvironment.name), + getRecentDeployments(baseUrl, groupToken, projectId, getDateInThePast(), currentEnvironment.name), ); } diff --git a/src/services/create-mr-with-compass-yml.ts b/src/services/create-mr-with-compass-yml.ts index b9316d5..5bf9172 100644 --- a/src/services/create-mr-with-compass-yml.ts +++ b/src/services/create-mr-with-compass-yml.ts @@ -2,7 +2,14 @@ import { storage } from '@forge/api'; import { getComponent } from '../client/compass'; import { ImportableProject } from '../types'; -import { COMMIT_MESSAGE, COMPASS_YML_BRANCH, MR_DESCRIPTION, MR_TITLE, STORAGE_SECRETS } from '../constants'; +import { + COMMIT_MESSAGE, + COMPASS_YML_BRANCH, + MR_DESCRIPTION, + MR_TITLE, + STORAGE_KEYS, + STORAGE_SECRETS, +} from '../constants'; import { getTrackingBranchName } from './get-tracking-branch'; import { createCompassYml, generateCompassYamlData } from '../utils/create-compass-yaml'; import { createFileInProject, createMergeRequest } from '../client/gitlab'; @@ -18,13 +25,15 @@ export const createMRWithCompassYML = async (project: ImportableProject, compone options: { includeCustomFields: true, includeLinks: true }, }); + const baseUrl = await storage.get(STORAGE_KEYS.BASE_URL); const groupToken = await storage.getSecret(`${STORAGE_SECRETS.GROUP_TOKEN_KEY_PREFIX}${groupId}`); - const trackingBranch = await getTrackingBranchName(groupToken, id, defaultBranch); + const trackingBranch = await getTrackingBranchName(baseUrl, groupToken, id, defaultBranch); const compassYamlData = generateCompassYamlData(component); const content = createCompassYml(compassYamlData); await createFileInProject( + baseUrl, groupToken, id, FILE_PATH, @@ -35,5 +44,5 @@ export const createMRWithCompassYML = async (project: ImportableProject, compone COMMIT_MESSAGE, ); - await createMergeRequest(groupToken, id, COMPASS_YML_BRANCH, trackingBranch, MR_TITLE, MR_DESCRIPTION, true); + await createMergeRequest(baseUrl, groupToken, id, COMPASS_YML_BRANCH, trackingBranch, MR_TITLE, MR_DESCRIPTION, true); }; diff --git a/src/services/data-provider-link-parser.ts b/src/services/data-provider-link-parser.ts index 508d4da..5988677 100644 --- a/src/services/data-provider-link-parser.ts +++ b/src/services/data-provider-link-parser.ts @@ -2,7 +2,7 @@ import { storage } from '@forge/api'; import parse from 'url-parse'; import { getOwnedProjectsBySearchCriteria } from '../client/gitlab'; -import { STORAGE_SECRETS } from '../constants'; +import { STORAGE_KEYS, STORAGE_SECRETS } from '../constants'; import { getGroupIds } from '../utils/storage-utils'; import { GitlabAPIProject } from '../types'; @@ -37,13 +37,14 @@ function doesURLMatch(projectUrl: string, path: string, name: string) { export const getProjectDataFromUrl = async ( url: string, -): Promise<{ project: GitlabAPIProject; groupToken: string }> => { +): Promise<{ project: GitlabAPIProject; baseUrl: string; groupToken: string }> => { try { const { projectName, pathName } = extractProjectInformation(url); + const baseUrl = await storage.get(STORAGE_KEYS.BASE_URL); const groupTokens = await getAllGroupTokens(); const projectsPromiseResults = await Promise.allSettled( - groupTokens.map((token) => getOwnedProjectsBySearchCriteria(projectName, token)), + groupTokens.map((token) => getOwnedProjectsBySearchCriteria(projectName, baseUrl, token)), ); const projectsResult = projectsPromiseResults.reduce<{ projects: GitlabAPIProject[]; projectIndex: number | null }>( (result, currentProjectResult, index) => { @@ -68,7 +69,7 @@ export const getProjectDataFromUrl = async ( throw new Error('Project not found'); } console.log(`[getProjectDataFromUrl] project_id: ${project.id}`); - return { project, groupToken }; + return { project, baseUrl, groupToken }; } catch (e) { console.log('Data provider link parser failed', e.message); return null; diff --git a/src/services/deployment.test.ts b/src/services/deployment.test.ts index c5f0dd4..a9bb7a6 100644 --- a/src/services/deployment.test.ts +++ b/src/services/deployment.test.ts @@ -12,7 +12,7 @@ import { getDeploymentAfter28Days, } from './deployment'; import { EnvironmentTier } from '../types'; -import { MOCK_CLOUD_ID } from '../__tests__/fixtures/gitlab-data'; +import { BASE_URL, MOCK_CLOUD_ID } from '../__tests__/fixtures/gitlab-data'; import { mocked } from 'jest-mock'; import { getEnvironments, getProjectRecentDeployments } from '../client/gitlab'; import { generateDeployment, generateEnvironmentEvent } from '../__tests__/helpers/gitlab-helper'; @@ -169,10 +169,9 @@ describe('getDeploymentAfter28Days', () => { }); }); - expect(await getDeploymentAfter28Days('123', 1, '2021-01-20T00:32:51.059Z', '2022-01-20T00:32:51.059Z')).toEqual([ - prodDeployment, - productionDeployment, - ]); + expect( + await getDeploymentAfter28Days(BASE_URL, '123', 1, '2021-01-20T00:32:51.059Z', '2022-01-20T00:32:51.059Z'), + ).toEqual([prodDeployment, productionDeployment]); expect(mockedGetProjectRecentDeployments).toHaveBeenCalledTimes(2); }); @@ -183,9 +182,9 @@ describe('getDeploymentAfter28Days', () => { generateEnvironmentEvent(EnvironmentTier.DEVELOPMENT, 'development'), ]); - expect(await getDeploymentAfter28Days('123', 1, '2021-01-20T00:32:51.059Z', '2022-01-20T00:32:51.059Z')).toEqual( - [], - ); + expect( + await getDeploymentAfter28Days(BASE_URL, '123', 1, '2021-01-20T00:32:51.059Z', '2022-01-20T00:32:51.059Z'), + ).toEqual([]); expect(mockedGetProjectRecentDeployments).not.toHaveBeenCalled(); }); @@ -226,11 +225,9 @@ describe('getDeploymentAfter28Days', () => { }); }); - expect(await getDeploymentAfter28Days('123', 1, '2021-01-20T00:32:51.059Z', '2021-01-20T00:32:51.059Z')).toEqual([ - productionDeployment, - stagingDeployment, - stgDeployment, - ]); + expect( + await getDeploymentAfter28Days(BASE_URL, '123', 1, '2021-01-20T00:32:51.059Z', '2021-01-20T00:32:51.059Z'), + ).toEqual([productionDeployment, stagingDeployment, stgDeployment]); expect(mockedGetProjectRecentDeployments).toHaveBeenCalledTimes(3); }); @@ -241,9 +238,9 @@ describe('getDeploymentAfter28Days', () => { generateEnvironmentEvent(EnvironmentTier.OTHER, 'other'), ]); - expect(await getDeploymentAfter28Days('123', 1, '2021-01-20T00:32:51.059Z', '2021-01-20T00:32:51.059Z')).toEqual( - [], - ); + expect( + await getDeploymentAfter28Days(BASE_URL, '123', 1, '2021-01-20T00:32:51.059Z', '2021-01-20T00:32:51.059Z'), + ).toEqual([]); expect(mockedGetProjectRecentDeployments).toHaveBeenCalledTimes(0); }); }); diff --git a/src/services/deployment.ts b/src/services/deployment.ts index 895eb64..2cf5f21 100644 --- a/src/services/deployment.ts +++ b/src/services/deployment.ts @@ -91,11 +91,12 @@ export const gitlabApiDeploymentToCompassDeploymentEvent = ( export const getDeployment = async ( event: DeploymentEvent, + baseUrl: string, groupToken: string, environmentTier: EnvironmentTier, cloudId: string, ): Promise => { - const deployment = await getProjectDeploymentById(event.project.id, event.deployment_id, groupToken); + const deployment = await getProjectDeploymentById(event.project.id, event.deployment_id, baseUrl, groupToken); return gitlabApiDeploymentToCompassDeploymentEvent( deployment as Deployment, @@ -107,13 +108,20 @@ export const getDeployment = async ( }; export const getRecentDeployments = async ( + baseUrl: string, groupToken: string, projectId: number, dateAfter: string, environmentName: string, ) => { try { - return fetchPaginatedData(getProjectRecentDeployments, { groupToken, projectId, dateAfter, environmentName }); + return fetchPaginatedData(getProjectRecentDeployments, { + baseUrl, + groupToken, + projectId, + dateAfter, + environmentName, + }); } catch (err) { const ERROR_MESSAGE = 'Error while fetching recent deployments from Gitlab!'; @@ -123,6 +131,7 @@ export const getRecentDeployments = async ( }; export const getDeploymentAfter28Days = async ( + baseUrl: string, groupToken: string, projectId: number, dateAfter: string, @@ -130,7 +139,7 @@ export const getDeploymentAfter28Days = async ( ): Promise => { const PAGE = 1; const PER_PAGE = 1; - const environments = await getProjectEnvironments(projectId, groupToken); + const environments = await getProjectEnvironments(projectId, baseUrl, groupToken); const getDeploymentsPromises = environments.reduce[]>( (deploymentsPromises, currentEnvironment) => { if ( @@ -139,6 +148,7 @@ export const getDeploymentAfter28Days = async ( ) { deploymentsPromises.push( getProjectRecentDeployments(PAGE, PER_PAGE, { + baseUrl, groupToken, projectId, environmentName: currentEnvironment.name, diff --git a/src/services/environment.ts b/src/services/environment.ts index 9e666f8..b68f8ab 100644 --- a/src/services/environment.ts +++ b/src/services/environment.ts @@ -1,8 +1,12 @@ import { getEnvironments } from '../client/gitlab'; import { Environment, EnvironmentTier } from '../types'; -export const getProjectEnvironments = (projectId: number, groupToken: string): Promise => { - return getEnvironments(projectId, groupToken); +export const getProjectEnvironments = ( + projectId: number, + baseUrl: string, + groupToken: string, +): Promise => { + return getEnvironments(projectId, baseUrl, groupToken); }; export const getEnvironmentTier = async ( diff --git a/src/services/fetch-projects.ts b/src/services/fetch-projects.ts index 26c1b05..51f9cc2 100644 --- a/src/services/fetch-projects.ts +++ b/src/services/fetch-projects.ts @@ -2,7 +2,7 @@ import { CreateLinkInput, Link } from '@atlassian/forge-graphql'; import { storage } from '@forge/api'; import { getComponentByExternalAlias } from '../client/compass'; -import { COMPASS_YML_BRANCH, EXTERNAL_SOURCE, STORAGE_SECRETS } from '../constants'; +import { COMPASS_YML_BRANCH, EXTERNAL_SOURCE, STORAGE_KEYS, STORAGE_SECRETS } from '../constants'; import { getMergeRequests, getProjects, GitLabHeaders } from '../client/gitlab'; import { GroupProjectsResponse, MergeRequestState, Project, ProjectReadyForImport } from '../types'; import { getProjectLabels } from './get-labels'; @@ -13,6 +13,7 @@ const mapComponentLinks = (links: Link[] = []): CreateLinkInput[] => }); const fetchProjects = async ( + baseUrl: string, groupToken: string, groupId: number, page: number, @@ -20,11 +21,11 @@ const fetchProjects = async ( ): Promise<{ total: number; projects: Project[] }> => { try { const PER_PAGE = 10; - const { data: projects, headers } = await getProjects(groupToken, groupId, page, PER_PAGE, search); + const { data: projects, headers } = await getProjects(baseUrl, groupToken, groupId, page, PER_PAGE, search); const generatedProjectsWithLanguages = await Promise.all( projects.map(async (project) => { - const labels = await getProjectLabels(project.id, groupToken, project.topics); + const labels = await getProjectLabels(project.id, baseUrl, groupToken, project.topics); return { id: project.id, @@ -49,7 +50,12 @@ const fetchProjects = async ( } }; -const compareProjectWithExistingComponent = async (cloudId: string, projectId: number, groupToken: string) => { +const compareProjectWithExistingComponent = async ( + cloudId: string, + projectId: number, + baseUrl: string, + groupToken: string, +) => { try { const [{ component }, { data: mergeRequestWithCompassYML }] = await Promise.all([ getComponentByExternalAlias({ @@ -60,6 +66,7 @@ const compareProjectWithExistingComponent = async (cloudId: string, projectId: n }), getMergeRequests(1, 1, { projectId, + baseUrl, groupToken, scope: 'all', sourceBranch: COMPASS_YML_BRANCH, @@ -106,13 +113,14 @@ export const getGroupProjects = async ( groupTokenId: number, search?: string, ): Promise => { + const baseUrl = await storage.get(STORAGE_KEYS.BASE_URL); const groupToken = await storage.getSecret(`${STORAGE_SECRETS.GROUP_TOKEN_KEY_PREFIX}${groupTokenId}`); - const { projects, total } = await fetchProjects(groupToken, groupId, page, search); + const { projects, total } = await fetchProjects(baseUrl, groupToken, groupId, page, search); const checkedDataWithExistingComponents = await Promise.all( projects.map(({ id: projectId }) => { - return compareProjectWithExistingComponent(cloudId, projectId, groupToken); + return compareProjectWithExistingComponent(cloudId, projectId, baseUrl, groupToken); }), ).catch((err) => { throw new Error(err); diff --git a/src/services/get-backfill-data.ts b/src/services/get-backfill-data.ts index a664dd9..c87a024 100644 --- a/src/services/get-backfill-data.ts +++ b/src/services/get-backfill-data.ts @@ -10,6 +10,7 @@ import { EnvironmentTier } from '../types'; import { isSendStagingEventsEnabled } from './feature-flags'; export const getBackfillData = async ( + baseUrl: string, groupToken: string, projectId: number, projectName: string, @@ -23,16 +24,17 @@ export const getBackfillData = async ( }; }> => { const [allBuildsFor28Days, mrCycleTime, deployments, openMergeRequestsCount] = await Promise.all([ - getProjectBuildsFor28Days(groupToken, projectId, projectName, branchName), - getMRCycleTime(groupToken, projectId, branchName), + getProjectBuildsFor28Days(baseUrl, groupToken, projectId, projectName, branchName), + getMRCycleTime(baseUrl, groupToken, projectId, branchName), getDeploymentsForEnvironmentTiers( + baseUrl, groupToken, projectId, projectName, isSendStagingEventsEnabled ? [EnvironmentTier.PRODUCTION, EnvironmentTier.STAGING] : undefined, ), - getOpenMergeRequestsCount(groupToken, projectId, branchName), - hasDeploymentAfter28Days(projectId, groupToken), + getOpenMergeRequestsCount(baseUrl, groupToken, projectId, branchName), + hasDeploymentAfter28Days(projectId, baseUrl, groupToken), ]); return { diff --git a/src/services/get-labels.test.ts b/src/services/get-labels.test.ts index 3de7fc9..a4b6723 100644 --- a/src/services/get-labels.test.ts +++ b/src/services/get-labels.test.ts @@ -6,7 +6,7 @@ mockForgeApi(); import { getProjectLanguages } from '../client/gitlab'; import { getProjectLabels } from './get-labels'; -import { TEST_TOKEN } from '../__tests__/fixtures/gitlab-data'; +import { BASE_URL, TEST_TOKEN } from '../__tests__/fixtures/gitlab-data'; jest.mock('../client/gitlab'); const mockGetProjectLanguages = mocked(getProjectLanguages); @@ -21,7 +21,7 @@ describe('get project labels', () => { it('returns correct labels in case getProjectLanguages fails', async () => { mockGetProjectLanguages.mockRejectedValue(new Error('Ooops!')); - const result = await getProjectLabels(MOCK_PROJECT_ID, TEST_TOKEN, MOCK_TOPICS); + const result = await getProjectLabels(MOCK_PROJECT_ID, BASE_URL, TEST_TOKEN, MOCK_TOPICS); expect(result).toEqual(MOCK_TOPICS); }); @@ -33,7 +33,7 @@ describe('get project labels', () => { html: 2, }); - const result = await getProjectLabels(MOCK_PROJECT_ID, TEST_TOKEN, MOCK_TOPICS); + const result = await getProjectLabels(MOCK_PROJECT_ID, BASE_URL, TEST_TOKEN, MOCK_TOPICS); expect(result).toEqual([...MOCK_TOPICS, 'language:javascript']); }); diff --git a/src/services/get-labels.ts b/src/services/get-labels.ts index 3a0bbc1..f196c2e 100644 --- a/src/services/get-labels.ts +++ b/src/services/get-labels.ts @@ -1,8 +1,12 @@ import { getProjectLanguages } from '../client/gitlab'; -const calculatePrimaryProjectLanguage = async (groupToken: string, projectId: number): Promise => { +const calculatePrimaryProjectLanguage = async ( + baseUrl: string, + groupToken: string, + projectId: number, +): Promise => { try { - const languages = await getProjectLanguages(groupToken, projectId); + const languages = await getProjectLanguages(baseUrl, groupToken, projectId); return Object.keys(languages).sort((a, b) => languages[b] - languages[a])[0]; } catch (err) { @@ -11,8 +15,13 @@ const calculatePrimaryProjectLanguage = async (groupToken: string, projectId: nu } }; -export const getProjectLabels = async (projectId: number, groupToken: string, topics: string[]): Promise => { - const language = await calculatePrimaryProjectLanguage(groupToken, projectId); +export const getProjectLabels = async ( + projectId: number, + baseUrl: string, + groupToken: string, + topics: string[], +): Promise => { + const language = await calculatePrimaryProjectLanguage(baseUrl, groupToken, projectId); return [...topics, ...(language ? [`language:${language.toLocaleLowerCase()}`] : [])]; }; diff --git a/src/services/get-tracking-branch.test.ts b/src/services/get-tracking-branch.test.ts index c32d004..957c6fd 100644 --- a/src/services/get-tracking-branch.test.ts +++ b/src/services/get-tracking-branch.test.ts @@ -6,6 +6,7 @@ mockForgeApi(); import { getProjectVariable, getProjectBranch } from '../client/gitlab'; import { getTrackingBranchName } from './get-tracking-branch'; +import { BASE_URL } from '../__tests__/fixtures/gitlab-data'; jest.mock('../client/gitlab'); @@ -20,7 +21,7 @@ describe('getTrackingBranchName', () => { mockGetProjectVariable.mockResolvedValue(MOCK_NON_DEFAULT_BRANCH_NAME); mockGetProjectBranch.mockResolvedValue({ name: MOCK_NON_DEFAULT_BRANCH_NAME }); - expect(await getTrackingBranchName('groupToken', 1234, MOCK_DEFAULT_BRANCH_NAME)).toBe( + expect(await getTrackingBranchName(BASE_URL, 'groupToken', 1234, MOCK_DEFAULT_BRANCH_NAME)).toBe( MOCK_NON_DEFAULT_BRANCH_NAME, ); }); @@ -29,6 +30,8 @@ describe('getTrackingBranchName', () => { mockGetProjectVariable.mockResolvedValue(MOCK_NON_DEFAULT_BRANCH_NAME); mockGetProjectBranch.mockRejectedValue('404 Branch Not Found'); - expect(await getTrackingBranchName('groupToken', 1234, MOCK_DEFAULT_BRANCH_NAME)).toEqual(MOCK_DEFAULT_BRANCH_NAME); + expect(await getTrackingBranchName(BASE_URL, 'groupToken', 1234, MOCK_DEFAULT_BRANCH_NAME)).toEqual( + MOCK_DEFAULT_BRANCH_NAME, + ); }); }); diff --git a/src/services/get-tracking-branch.ts b/src/services/get-tracking-branch.ts index 5d338c4..a87a1bb 100644 --- a/src/services/get-tracking-branch.ts +++ b/src/services/get-tracking-branch.ts @@ -2,13 +2,14 @@ import { getProjectBranch, getProjectVariable } from '../client/gitlab'; import { NON_DEFAULT_BRANCH_VARIABLE_KEY } from '../constants'; export const getTrackingBranchName = async ( + baseUrl: string, groupToken: string, projectId: number, defaultBranch: string, ): Promise => { try { - const branchName = await getProjectVariable(groupToken, projectId, NON_DEFAULT_BRANCH_VARIABLE_KEY); - await getProjectBranch(groupToken, projectId, branchName); + const branchName = await getProjectVariable(baseUrl, groupToken, projectId, NON_DEFAULT_BRANCH_VARIABLE_KEY); + await getProjectBranch(baseUrl, groupToken, projectId, branchName); return branchName; } catch (e) { diff --git a/src/services/group.test.ts b/src/services/group.test.ts index d9c3077..97dabb9 100644 --- a/src/services/group.test.ts +++ b/src/services/group.test.ts @@ -10,6 +10,7 @@ import { getGroupAccessTokens, getGroupsData } from '../client/gitlab'; import { connectGroup, getConnectedGroups, InvalidGroupTokenError } from './group'; import { AuthErrorTypes, GitlabAPIGroup } from '../resolverTypes'; import { GroupAccessToken } from '../types'; +import { BASE_URL } from '../__tests__/fixtures/gitlab-data'; jest.mock('../client/gitlab'); @@ -78,7 +79,7 @@ describe('Group service', () => { mockGetGroupsData.mockResolvedValue([MOCK_GROUP_DATA]); mockGetGroupAccessTokens.mockResolvedValue([mockGroupAccessToken]); - const result = await connectGroup(MOCK_TOKEN, mockGroupAccessToken.name); + const result = await connectGroup(BASE_URL, MOCK_TOKEN, mockGroupAccessToken.name); expect(storage.set).toHaveBeenCalledWith( `${STORAGE_KEYS.GROUP_KEY_PREFIX}${MOCK_GROUP_DATA.id}`, @@ -96,7 +97,7 @@ describe('Group service', () => { const mockGroupAccessToken = generateMockGroupAccessToken(); mockGetGroupsData.mockRejectedValue(undefined); - await expect(connectGroup(MOCK_TOKEN, mockGroupAccessToken.name)).rejects.toThrow( + await expect(connectGroup(BASE_URL, MOCK_TOKEN, mockGroupAccessToken.name)).rejects.toThrow( new InvalidGroupTokenError(AuthErrorTypes.INVALID_GROUP_TOKEN), ); expect(storage.set).not.toHaveBeenCalled(); @@ -107,7 +108,7 @@ describe('Group service', () => { mockGetGroupsData.mockResolvedValue([MOCK_GROUP_DATA]); mockGetGroupAccessTokens.mockResolvedValue([mockGroupAccessToken]); - await expect(connectGroup(MOCK_TOKEN, 'momo')).rejects.toThrow( + await expect(connectGroup(BASE_URL, MOCK_TOKEN, 'momo')).rejects.toThrow( new InvalidGroupTokenError(AuthErrorTypes.INVALID_GROUP_TOKEN_NAME), ); expect(storage.set).not.toHaveBeenCalled(); @@ -118,7 +119,7 @@ describe('Group service', () => { mockGetGroupsData.mockResolvedValue([MOCK_GROUP_DATA]); mockGetGroupAccessTokens.mockResolvedValue([mockGroupAccessToken]); - await expect(connectGroup(MOCK_TOKEN, mockGroupAccessToken.name)).rejects.toThrow( + await expect(connectGroup(BASE_URL, MOCK_TOKEN, mockGroupAccessToken.name)).rejects.toThrow( new InvalidGroupTokenError(AuthErrorTypes.INCORRECT_GROUP_TOKEN_SCOPES), ); expect(storage.set).not.toHaveBeenCalled(); diff --git a/src/services/group.ts b/src/services/group.ts index efe36a8..1146794 100644 --- a/src/services/group.ts +++ b/src/services/group.ts @@ -14,11 +14,12 @@ export class InvalidGroupTokenError extends Error { } const findGroupToken = async ( + baseUrl: string, groupToken: string, groupTokenName: string, groupId: number, ): Promise => { - const groupAccessTokens = await getGroupAccessTokens(groupToken, groupId); + const groupAccessTokens = await getGroupAccessTokens(baseUrl, groupToken, groupId); return groupAccessTokens.find((groupAccessToken) => groupAccessToken.name === groupTokenName); }; @@ -27,17 +28,17 @@ const validateGroupTokenScopes = (requiredScopes: string[], tokenScopes: string[ return requiredScopes.every((requiredScope) => tokenScopes.includes(requiredScope)); }; -export const connectGroup = async (token: string, tokenName: string): Promise => { +export const connectGroup = async (baseUrl: string, token: string, tokenName: string): Promise => { let groupId; let groupName; try { - const [group] = await getGroupsData(token, 'true'); + const [group] = await getGroupsData(baseUrl, token, 'true'); ({ id: groupId, name: groupName } = group); } catch (e) { throw new InvalidGroupTokenError(AuthErrorTypes.INVALID_GROUP_TOKEN); } - const groupToken = await findGroupToken(token, tokenName, groupId); + const groupToken = await findGroupToken(baseUrl, token, tokenName, groupId); if (!groupToken) { throw new InvalidGroupTokenError(AuthErrorTypes.INVALID_GROUP_TOKEN_NAME); } @@ -47,6 +48,7 @@ export const connectGroup = async (token: string, tokenName: string): Promise => { + const baseUrl = await storage.get(STORAGE_KEYS.BASE_URL); + const response = storage.query().where('key', startsWith(STORAGE_KEYS.GROUP_KEY_PREFIX)); const { results: groups } = await response.getMany(); @@ -66,7 +70,7 @@ const getGroups = async (owned?: string, minAccessLevel?: number): Promise getGroupsData(token, owned, minAccessLevel)); + const groupPromises = tokens.map((token: string) => getGroupsData(baseUrl, token, owned, minAccessLevel)); // We need to remove revoked/invalid (on Gitlab side) tokens from storage const groupsResult = await Promise.allSettled(groupPromises); diff --git a/src/services/mergeRequest.test.ts b/src/services/mergeRequest.test.ts index aaa6528..70331ae 100644 --- a/src/services/mergeRequest.test.ts +++ b/src/services/mergeRequest.test.ts @@ -5,7 +5,7 @@ import { mockAgg } from '../__tests__/helpers/mock-agg'; mockAgg(); -import { TEST_TOKEN } from '../__tests__/fixtures/gitlab-data'; +import { BASE_URL, TEST_TOKEN } from '../__tests__/fixtures/gitlab-data'; import { fetchPaginatedData } from '../utils/fetchPaginatedData'; import { getLastMergedMergeRequests, getOpenMergeRequests } from './mergeRequest'; import { getMergeRequests } from '../client/gitlab'; @@ -27,13 +27,17 @@ describe('MergeRequest Service', () => { it('throws error in case of failed open MR fetching', async () => { const errorMsg = 'Error while fetching open merge requests from Gitlab!'; mockedFetchPaginatedData.mockRejectedValue(new Error(errorMsg)); - await expect(getOpenMergeRequests(TEST_TOKEN, MOCK_PROJECT_ID, BRANCH_NAME)).rejects.toThrowError(errorMsg); + await expect(getOpenMergeRequests(BASE_URL, TEST_TOKEN, MOCK_PROJECT_ID, BRANCH_NAME)).rejects.toThrowError( + errorMsg, + ); }); it('throws error in case of failed merged merge requests fetching', async () => { const errorMsg = 'Error while fetching merged merge requests from Gitlab!'; mockedGetMergeRequests.mockRejectedValue(new Error(errorMsg)); - await expect(getLastMergedMergeRequests(TEST_TOKEN, MOCK_PROJECT_ID, BRANCH_NAME)).rejects.toThrowError(errorMsg); + await expect(getLastMergedMergeRequests(BASE_URL, TEST_TOKEN, MOCK_PROJECT_ID, BRANCH_NAME)).rejects.toThrowError( + errorMsg, + ); }); }); diff --git a/src/services/mergeRequest.ts b/src/services/mergeRequest.ts index 1e3104b..a212853 100644 --- a/src/services/mergeRequest.ts +++ b/src/services/mergeRequest.ts @@ -3,6 +3,7 @@ import { getMergeRequests, MergeRequestWorkInProgressFilterOptions } from '../cl import { MergeRequest, MergeRequestOrderBy, MergeRequestState } from '../types'; export const getOpenMergeRequests = async ( + baseUrl: string, groupToken: string, projectId: number, targetBranch: string, @@ -11,6 +12,7 @@ export const getOpenMergeRequests = async ( try { return fetchPaginatedData(getMergeRequests, { + baseUrl, groupToken, projectId, state: MergeRequestState.OPENED, @@ -29,6 +31,7 @@ export const getOpenMergeRequests = async ( }; export const getLastMergedMergeRequests = async ( + baseUrl: string, groupToken: string, projectId: number, targetBranch: string, @@ -39,6 +42,7 @@ export const getLastMergedMergeRequests = async ( try { const { data } = await getMergeRequests(page, numberOfMergeRequests, { + baseUrl, groupToken, projectId, state: MergeRequestState.MERGED, diff --git a/src/services/sync-component-with-file/find-config-file-changes.test.ts b/src/services/sync-component-with-file/find-config-file-changes.test.ts index 75cf98d..6878ddc 100644 --- a/src/services/sync-component-with-file/find-config-file-changes.test.ts +++ b/src/services/sync-component-with-file/find-config-file-changes.test.ts @@ -8,6 +8,7 @@ import { getCommitDiff, getFileContent } from '../../client/gitlab'; import { findConfigAsCodeFileChanges } from './find-config-file-changes'; import { generatePushEvent } from '../../__tests__/helpers/gitlab-helper'; import { CommitFileDiff, CompassYaml, ComponentChanges, PushEvent } from '../../types'; +import { BASE_URL } from '../../__tests__/fixtures/gitlab-data'; jest.mock('../../client/gitlab', () => ({ getCommitDiff: jest.fn(), @@ -84,7 +85,7 @@ describe('findConfigAsCodeFileChanges', () => { componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(baseEvent, 'token'); + const result = await findConfigAsCodeFileChanges(baseEvent, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -108,7 +109,7 @@ describe('findConfigAsCodeFileChanges', () => { componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(baseEvent, 'token'); + const result = await findConfigAsCodeFileChanges(baseEvent, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -133,7 +134,7 @@ describe('findConfigAsCodeFileChanges', () => { ], }; - const result = await findConfigAsCodeFileChanges(baseEvent, 'token'); + const result = await findConfigAsCodeFileChanges(baseEvent, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -156,7 +157,7 @@ describe('findConfigAsCodeFileChanges', () => { componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(baseEvent, 'token'); + const result = await findConfigAsCodeFileChanges(baseEvent, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -179,7 +180,7 @@ describe('findConfigAsCodeFileChanges', () => { componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(baseEvent, 'token'); + const result = await findConfigAsCodeFileChanges(baseEvent, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -202,7 +203,7 @@ describe('findConfigAsCodeFileChanges', () => { componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(baseEvent, 'token'); + const result = await findConfigAsCodeFileChanges(baseEvent, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -230,7 +231,7 @@ describe('findConfigAsCodeFileChanges', () => { ], }; - const result = await findConfigAsCodeFileChanges(baseEvent, 'token'); + const result = await findConfigAsCodeFileChanges(baseEvent, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -261,7 +262,7 @@ describe('findConfigAsCodeFileChanges', () => { componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -283,7 +284,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -305,7 +306,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -327,7 +328,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -355,7 +356,7 @@ describe('findConfigAsCodeFileChanges', () => { }, ], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -383,7 +384,7 @@ describe('findConfigAsCodeFileChanges', () => { }, ], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -406,7 +407,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -428,7 +429,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -461,7 +462,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -493,7 +494,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -516,7 +517,7 @@ describe('findConfigAsCodeFileChanges', () => { componentsToUpdate: [], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -539,7 +540,7 @@ describe('findConfigAsCodeFileChanges', () => { componentsToUpdate: [], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -562,7 +563,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -584,7 +585,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -606,7 +607,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -628,7 +629,7 @@ describe('findConfigAsCodeFileChanges', () => { ], componentsToUnlink: [], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -656,7 +657,7 @@ describe('findConfigAsCodeFileChanges', () => { }, ], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -683,7 +684,7 @@ describe('findConfigAsCodeFileChanges', () => { }, ], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -711,7 +712,7 @@ describe('findConfigAsCodeFileChanges', () => { }, ], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); @@ -738,7 +739,7 @@ describe('findConfigAsCodeFileChanges', () => { }, ], }; - const result = await findConfigAsCodeFileChanges(event, 'token'); + const result = await findConfigAsCodeFileChanges(event, BASE_URL, 'token'); expect(result).toEqual(expectedResult); }); diff --git a/src/services/sync-component-with-file/find-config-file-changes.ts b/src/services/sync-component-with-file/find-config-file-changes.ts index 78b82bc..0c83007 100644 --- a/src/services/sync-component-with-file/find-config-file-changes.ts +++ b/src/services/sync-component-with-file/find-config-file-changes.ts @@ -16,13 +16,14 @@ import { } from './config-file-changes-transformer'; const getRemovedFiles = async ( + baseUrl: string, token: string, compassYmlFilesDiffs: CommitFileDiff[], event: PushEvent, ): Promise => { const settledPromises = await Promise.allSettled( compassYmlFilesDiffs.map((diff: CommitFileDiff) => { - return getFileContent(token, event.project.id, diff.old_path, event.before) + return getFileContent(baseUrl, token, event.project.id, diff.old_path, event.before) .then((componentYaml) => ({ componentYaml, filePath: `/${diff.new_path}`, @@ -40,13 +41,14 @@ const getRemovedFiles = async ( }; const getAddedFiles = async ( + baseUrl: string, token: string, compassYmlFilesDiffs: CommitFileDiff[], event: PushEvent, ): Promise => { const settledPromises = await Promise.allSettled( compassYmlFilesDiffs.map((diff: CommitFileDiff) => - getFileContent(token, event.project.id, diff.new_path, event.after) + getFileContent(baseUrl, token, event.project.id, diff.new_path, event.after) .then((componentYaml) => ({ componentYaml, absoluteFilePath: diff.new_path, @@ -64,14 +66,15 @@ const getAddedFiles = async ( }; const getModifiedFiles = async ( + baseUrl: string, token: string, compassYmlFilesDiffs: CommitFileDiff[], event: PushEvent, ): Promise => { const settledPromises = await Promise.allSettled( compassYmlFilesDiffs.map(async (diff) => { - const oldFilePromise = getFileContent(token, event.project.id, diff.old_path, event.before); - const newFilePromise = getFileContent(token, event.project.id, diff.new_path, event.after); + const oldFilePromise = getFileContent(baseUrl, token, event.project.id, diff.old_path, event.before); + const newFilePromise = getFileContent(baseUrl, token, event.project.id, diff.new_path, event.after); const [oldFileSettled, newFileSettled] = await Promise.allSettled([oldFilePromise, newFilePromise]); let oldFileContents: CompassYaml; @@ -112,10 +115,14 @@ const getModifiedFiles = async ( .map((result) => (result as PromiseFulfilledResult).value); }; -export const findConfigAsCodeFileChanges = async (event: PushEvent, token: string): Promise => { +export const findConfigAsCodeFileChanges = async ( + event: PushEvent, + baseUrl: string, + token: string, +): Promise => { let filesDiffs: CommitFileDiff[] = []; try { - filesDiffs = await getCommitDiff(token, event.project.id, event.checkout_sha); + filesDiffs = await getCommitDiff(baseUrl, token, event.project.id, event.checkout_sha); } catch (e) { console.error({ message: 'Error with commits diff request', @@ -139,9 +146,9 @@ export const findConfigAsCodeFileChanges = async (event: PushEvent, token: strin ); const [createPayload, unlinkPayload, modifiedFiles] = await Promise.all([ - getAddedFiles(token, added, event), - getRemovedFiles(token, removed, event), - getModifiedFiles(token, modified, event), + getAddedFiles(baseUrl, token, added, event), + getRemovedFiles(baseUrl, token, removed, event), + getModifiedFiles(baseUrl, token, modified, event), ]); const componentChanges: ComponentChanges = { diff --git a/src/services/sync-component-with-file/sync-component.test.ts b/src/services/sync-component-with-file/sync-component.test.ts index 0590d23..72bd14a 100644 --- a/src/services/sync-component-with-file/sync-component.test.ts +++ b/src/services/sync-component-with-file/sync-component.test.ts @@ -20,7 +20,12 @@ import { EXTERNAL_SOURCE, IMPORT_LABEL } from '../../constants'; import { reportSyncError } from './report-sync-error'; import { getProjectById } from '../../client/gitlab'; import { getProjectLabels } from '../get-labels'; -import { MOCK_CLOUD_ID, TEST_GET_PROJECT_BY_ID_RESPONSE, TEST_TOKEN } from '../../__tests__/fixtures/gitlab-data'; +import { + BASE_URL, + MOCK_CLOUD_ID, + TEST_GET_PROJECT_BY_ID_RESPONSE, + TEST_TOKEN, +} from '../../__tests__/fixtures/gitlab-data'; jest.mock('../../client/gitlab'); jest.mock('../../services/get-labels'); @@ -71,6 +76,7 @@ const getMockedSyncPayload = ( previousFilePath: '/previousPath/fileName.yaml', }; const mockComponentSyncDetails: ComponentSyncDetails = { + baseUrl: BASE_URL, token: TEST_TOKEN, event, trackingBranch: event.project.default_branch, diff --git a/src/services/sync-component-with-file/sync-component.ts b/src/services/sync-component-with-file/sync-component.ts index 06bc649..db51b95 100644 --- a/src/services/sync-component-with-file/sync-component.ts +++ b/src/services/sync-component-with-file/sync-component.ts @@ -16,7 +16,7 @@ export const syncComponent = async ( componentSyncDetails: ComponentSyncDetails, configFileMetadata: ConfigFileMetadata, ): Promise => { - const { token, event, trackingBranch, cloudId } = componentSyncDetails; + const { baseUrl, token, event, trackingBranch, cloudId } = componentSyncDetails; const { componentYaml, absoluteFilePath } = componentSyncPayload; const startTime = Date.now(); @@ -41,8 +41,8 @@ export const syncComponent = async ( currentComponent = data.component; console.log({ message: `Main sync with file success for component ${currentComponent.id}` }); - const { topics } = await getProjectById(token, event.project.id); - const projectLabels = await getProjectLabels(event.project.id, token, topics); + const { topics } = await getProjectById(baseUrl, token, event.project.id); + const projectLabels = await getProjectLabels(event.project.id, baseUrl, token, topics); const formattedLabels = projectLabels.map((label) => label.split(' ').join('-').toLowerCase()); diff --git a/src/services/webhooks.ts b/src/services/webhooks.ts index 7228fa3..06a0402 100644 --- a/src/services/webhooks.ts +++ b/src/services/webhooks.ts @@ -7,12 +7,14 @@ import { generateSignature } from '../utils/generate-signature-utils'; export const setupAndValidateWebhook = async (groupId: number): Promise => { console.log('Setting up webhook'); try { - const [existingWebhook, groupToken] = await Promise.all([ + const [existingWebhook, baseUrl, groupToken] = await Promise.all([ storage.get(`${STORAGE_KEYS.WEBHOOK_KEY_PREFIX}${groupId}`), + storage.get(STORAGE_KEYS.BASE_URL), storage.getSecret(`${STORAGE_SECRETS.GROUP_TOKEN_KEY_PREFIX}${groupId}`), ]); - const isWebhookValid = existingWebhook && (await getGroupWebhook(groupId, existingWebhook, groupToken)) !== null; + const isWebhookValid = + existingWebhook && (await getGroupWebhook(groupId, existingWebhook, baseUrl, groupToken)) !== null; if (isWebhookValid) { console.log('Using existing webhook'); @@ -25,6 +27,7 @@ export const setupAndValidateWebhook = async (groupId: number): Promise const webhookId = await registerGroupWebhook({ groupId, url: webtriggerURLWithGroupId, + baseUrl, token: groupToken, signature: webhookSignature, }); @@ -43,12 +46,13 @@ export const setupAndValidateWebhook = async (groupId: number): Promise }; export const deleteWebhook = async (groupId: number): Promise => { - const [webhookId, groupToken] = await Promise.all([ + const [webhookId, baseUrl, groupToken] = await Promise.all([ storage.get(`${STORAGE_KEYS.WEBHOOK_KEY_PREFIX}${groupId}`), + storage.get(STORAGE_KEYS.BASE_URL), storage.getSecret(`${STORAGE_SECRETS.GROUP_TOKEN_KEY_PREFIX}${groupId}`), ]); if (webhookId) { - await deleteGroupWebhook(groupId, webhookId, groupToken); + await deleteGroupWebhook(groupId, webhookId, baseUrl, groupToken); } }; diff --git a/src/types.ts b/src/types.ts index 55dabc0..18341de 100644 --- a/src/types.ts +++ b/src/types.ts @@ -168,6 +168,7 @@ type ComponentChanges = { }; type ComponentSyncDetails = { + baseUrl: string; token: string; event: PushEvent; trackingBranch: string; @@ -178,6 +179,7 @@ type RegisterWebhookPayload = { groupId: number; url: string; signature: string; + baseUrl: string; token: string; }; diff --git a/src/utils/fetchPaginatedData.ts b/src/utils/fetchPaginatedData.ts index c45c466..540dd5c 100644 --- a/src/utils/fetchPaginatedData.ts +++ b/src/utils/fetchPaginatedData.ts @@ -2,7 +2,7 @@ import { GitLabHeaders, GitlabPaginatedFetch } from '../client/gitlab'; export const fetchPaginatedData = async ( fetchFn: GitlabPaginatedFetch, - fetchFnParameters: Record<'groupToken', string> & P, + fetchFnParameters: Record<'baseUrl', string> & Record<'groupToken', string> & P, page = 1, perPage = 100, ): Promise => { diff --git a/src/utils/has-deployment-after-28days.ts b/src/utils/has-deployment-after-28days.ts index 39d1cc3..bb99039 100644 --- a/src/utils/has-deployment-after-28days.ts +++ b/src/utils/has-deployment-after-28days.ts @@ -3,11 +3,15 @@ import { getDateInThePast } from './time-utils'; import { DAYS_TO_CALC } from '../constants'; import { getDeploymentAfter28Days } from '../services/deployment'; -export const hasDeploymentAfter28Days = async (projectId: number, groupToken: string): Promise => { +export const hasDeploymentAfter28Days = async ( + projectId: number, + baseUrl: string, + groupToken: string, +): Promise => { const dateBefore = getDateInThePast(DAYS_TO_CALC + 1); - const { created_at: dateAfter } = await getProjectById(groupToken, projectId); - const data = await getDeploymentAfter28Days(groupToken, projectId, dateAfter, dateBefore); + const { created_at: dateAfter } = await getProjectById(baseUrl, groupToken, projectId); + const data = await getDeploymentAfter28Days(baseUrl, groupToken, projectId, dateAfter, dateBefore); return data.length > 0; }; diff --git a/ui/src/components/AuthPage/__tests__/AuthPage.test.tsx b/ui/src/components/AuthPage/__tests__/AuthPage.test.tsx index e6904fc..d12801a 100644 --- a/ui/src/components/AuthPage/__tests__/AuthPage.test.tsx +++ b/ui/src/components/AuthPage/__tests__/AuthPage.test.tsx @@ -26,6 +26,11 @@ const setup = async () => { , ); + await act(async () => { + fireEvent.change(await findByPlaceholderText('Enter your GitLab URL'), { + target: { value: 'https://gitlab.toto.io' }, + }); + }); await act(async () => { fireEvent.change(await findByPlaceholderText('Enter your group access token'), { target: { value: 'koko' } }); }); diff --git a/ui/src/components/AuthPage/index.tsx b/ui/src/components/AuthPage/index.tsx index f5380c5..dcd3626 100644 --- a/ui/src/components/AuthPage/index.tsx +++ b/ui/src/components/AuthPage/index.tsx @@ -16,6 +16,7 @@ import { ForgeLink } from '../ForgeLink'; import { connectGroup } from '../../services/invokes'; import { ErrorMessages } from '../../errorMessages'; import { AuthErrorTypes, ErrorTypes } from '../../resolverTypes'; +import { DEFAULT_GITLAB_URL } from '../../constants'; const SectionMessageWrapper = styled.div` margin-bottom: ${gridSize() * 2}px; @@ -92,6 +93,7 @@ const buildValidationMethod = (errorType: ErrorTypes) => { }; export const AuthPage = () => { + const [gitlabUrl, setGitlabUrl] = useState(DEFAULT_GITLAB_URL); const [tokenName, setTokenName] = useState(''); const [token, setToken] = useState(''); const [isLoadingSubmit, setLoadingSubmit] = useState(false); @@ -108,7 +110,7 @@ export const AuthPage = () => { setLoadingSubmit(true); try { - const { success, errors } = await connectGroup(token.trim(), tokenName); + const { success, errors } = await connectGroup(gitlabUrl.trim(), token.trim(), tokenName); if (success) { handleNavigateToConnectedPage(); @@ -123,6 +125,13 @@ export const AuthPage = () => { } }; + const gitlabUrlOnChange = (e: React.FormEvent) => { + if (errorType) { + setErrorType(null); + } + setGitlabUrl(e.currentTarget.value); + }; + const tokenNameOnChange = (e: React.FormEvent) => { if (errorType) { setErrorType(null); @@ -173,6 +182,17 @@ export const AuthPage = () => { {errorType && buildValidationMethod(errorType)} + + {({ fieldProps }) => ( + + )} + {({ fieldProps }) => ( >('groups/allExisting'); }; -export const connectGroup = (groupToken: string, groupTokenName: string): Promise => { +export const connectGroup = ( + baseUrl: string, + groupToken: string, + groupTokenName: string, +): Promise => { return invoke('groups/connect', { + baseUrl, groupToken, groupTokenName, });