diff --git a/src/client/gitlab.ts b/src/client/gitlab.ts index 8a3a5d5..3e85ce9 100644 --- a/src/client/gitlab.ts +++ b/src/client/gitlab.ts @@ -16,6 +16,7 @@ import { Deployment, Environment, GitlabPipelineStates, + GitLabAccessLevels, } from '../types'; import { GitlabHttpMethodError, InvalidConfigFileError } from '../models/errors'; import { INVALID_YAML_ERROR } from '../models/error-messages'; @@ -287,12 +288,17 @@ export const getProjectBranch = async ( return branch; }; -export const getOwnedProjectsBySearchCriteria = async ( +/** + * Get project data that the token role is at least a maintainer of, filtered by search criteria. + * @param search - search criteria i.e. project name + * @param groupToken - Gitlab access token + */ +export const getMaintainedProjectsBySearchCriteria = async ( search: string, groupToken: string, ): Promise => { const params = { - owned: 'true', + min_access_level: GitLabAccessLevels.MAINTAINER.toString(), search, }; diff --git a/src/resolvers/admin-resolvers.ts b/src/resolvers/admin-resolvers.ts index 772c947..cd9afa5 100644 --- a/src/resolvers/admin-resolvers.ts +++ b/src/resolvers/admin-resolvers.ts @@ -51,7 +51,7 @@ resolver.define('groups/connect', async (req): Promise => { }; const groupId = await connectGroup(input); - await setupAndValidateWebhook(groupId, groupRole, webhookId, webhookSecretToken); + await setupAndValidateWebhook(groupId, webhookId, webhookSecretToken); await graphqlGateway.compass.asApp().synchronizeLinkAssociations({ cloudId, diff --git a/src/services/data-provider-link-parser.test.ts b/src/services/data-provider-link-parser.test.ts index e3403ed..63a579b 100644 --- a/src/services/data-provider-link-parser.test.ts +++ b/src/services/data-provider-link-parser.test.ts @@ -5,14 +5,14 @@ mockForgeApi(); import { mocked } from 'jest-mock'; import { generateGitlabProject } from '../__tests__/helpers/gitlab-helper'; -import { getOwnedProjectsBySearchCriteria } from '../client/gitlab'; +import { getMaintainedProjectsBySearchCriteria } from '../client/gitlab'; import { extractProjectInformation, getProjectDataFromUrl } from './data-provider-link-parser'; import { getGroupIds } from '../utils/storage-utils'; jest.mock('../client/gitlab'); jest.mock('../utils/storage-utils'); -const mockedGetOwnedProjectsBySearchCriteria = mocked(getOwnedProjectsBySearchCriteria); +const mockedGetOwnedProjectsBySearchCriteria = mocked(getMaintainedProjectsBySearchCriteria); const mockedGetGroupIds = mocked(getGroupIds); const mockProjectUrl = 'https://gitlab.com/test/repo-name?testParam=test'; diff --git a/src/services/data-provider-link-parser.ts b/src/services/data-provider-link-parser.ts index 599ad7a..dfe7c71 100644 --- a/src/services/data-provider-link-parser.ts +++ b/src/services/data-provider-link-parser.ts @@ -1,7 +1,7 @@ import { storage } from '@forge/api'; import parse from 'url-parse'; -import { getOwnedProjectsBySearchCriteria } from '../client/gitlab'; +import { getMaintainedProjectsBySearchCriteria } from '../client/gitlab'; import { STORAGE_SECRETS } from '../constants'; import { getGroupIds } from '../utils/storage-utils'; import { GitlabAPIProject } from '../types'; @@ -52,7 +52,7 @@ export const getProjectDataFromUrl = async ( const groupTokens = await getAllGroupTokens(); const projectsPromiseResults = await Promise.allSettled( - groupTokens.map((token) => getOwnedProjectsBySearchCriteria(projectName, token)), + groupTokens.map((token) => getMaintainedProjectsBySearchCriteria(projectName, token)), ); const projectsResult = projectsPromiseResults.reduce<{ projects: GitlabAPIProject[]; projectIndex: number | null }>( (result, currentProjectResult, index) => { diff --git a/src/services/webhooks.ts b/src/services/webhooks.ts index 9b16480..08df18c 100644 --- a/src/services/webhooks.ts +++ b/src/services/webhooks.ts @@ -71,24 +71,26 @@ const setupAndValidateForMaintainerToken = async ( export const setupAndValidateWebhook = async ( groupId: number, - tokenRole?: GitLabRoles, webhookId?: number, webhookSecretToken?: string, ): Promise => { console.log('Setting up webhook'); try { - const [existingWebhookResult, groupTokenResult] = await Promise.allSettled([ + const [existingWebhookResult, tokenRoleResult, groupTokenResult] = await Promise.allSettled([ storage.get(`${STORAGE_KEYS.WEBHOOK_KEY_PREFIX}${groupId}`), + storage.get(`${STORAGE_KEYS.TOKEN_ROLE_PREFIX}${groupId}`), storage.getSecret(`${STORAGE_SECRETS.GROUP_TOKEN_KEY_PREFIX}${groupId}`), ]); if ( existingWebhookResult.status === ALL_SETTLED_STATUS.REJECTED || + tokenRoleResult.status === ALL_SETTLED_STATUS.REJECTED || groupTokenResult.status === ALL_SETTLED_STATUS.REJECTED ) { throw new Error( - `Error getting existing webhook or group token: ${getFormattedErrors([ + `Error getting existing webhook, token role or group token: ${getFormattedErrors([ existingWebhookResult, + tokenRoleResult, groupTokenResult, ])}`, ); @@ -96,9 +98,11 @@ export const setupAndValidateWebhook = async ( const existingWebhook = existingWebhookResult.value; const groupToken = groupTokenResult.value; + const tokenRole: string = tokenRoleResult.value; + + console.log('INFO: Setting up webhook', groupId, tokenRole, webhookId, webhookSecretToken); - // One of the pathways calling this is for fetching connectedGroups - // in which case, the tokenRole could be empty. + // Legacy integrations might not have the new tokenRole stored. These are treated as OWNER. if (!tokenRole || tokenRole === GitLabRoles.OWNER) { return setupAndValidateForOwnerToken(groupId, existingWebhook, groupToken); } diff --git a/ui/src/services/invokes.ts b/ui/src/services/invokes.ts index 15d4698..316dbb6 100644 --- a/ui/src/services/invokes.ts +++ b/ui/src/services/invokes.ts @@ -10,7 +10,7 @@ import { FeaturesList, GroupProjectsResponse, } from '../resolverTypes'; -import { TeamsWithMembershipStatus } from '../types'; +import { GitLabRoles, TeamsWithMembershipStatus } from '../types'; export const disconnectGroup = (id: number): Promise => { return invoke('groups/disconnect', { @@ -29,15 +29,17 @@ export const getAllExistingGroups = (): Promise => { return invoke('groups/connect', { groupToken, groupTokenName, groupRole, groupName, + webhookId, webhookSecretToken, }); };