Skip to content

Commit

Permalink
Merge pull request #139 from atlassian-labs/issue/COMPASS-23318-state
Browse files Browse the repository at this point in the history
Enable support for Maintainer token role for integration
  • Loading branch information
subbuvenk-atlas authored Dec 16, 2024
2 parents 572d8fb + 0eb66de commit 55520d5
Show file tree
Hide file tree
Showing 16 changed files with 551 additions and 139 deletions.
2 changes: 1 addition & 1 deletion manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ modules:
function: import-projects
method: import
app:
id: ari:cloud:ecosystem::app/fe7b0913-7421-4c84-b401-041eaab2ef2e
id: ari:cloud:ecosystem::app/60787eba-aec5-49b4-979d-db8e77de32db
runtime:
name: nodejs18.x
features:
Expand Down
17 changes: 15 additions & 2 deletions src/client/gitlab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import {
Deployment,
Environment,
GitlabPipelineStates,
GitLabAccessLevels,
} from '../types';
import { GitlabHttpMethodError, InvalidConfigFileError } from '../models/errors';
import { INVALID_YAML_ERROR } from '../models/error-messages';
import { queryParamsGenerator } from '../utils/url-utils';
import { isGitlabMaintainerTokenEnabled } from '../services/feature-flags';

export enum HttpMethod {
GET = 'GET',
Expand Down Expand Up @@ -102,10 +104,12 @@ export const getGroupsData = async (
groupAccessToken: string,
owned?: string,
minAccessLevel?: number,
name?: string,
): Promise<GitlabAPIGroup[]> => {
const params = {
...(owned ? { owned } : {}),
...(minAccessLevel ? { min_access_level: minAccessLevel.toString() } : {}),
...(name ? { search: name } : {}),
};

const queryParams = queryParamsGenerator(params);
Expand Down Expand Up @@ -285,12 +289,21 @@ export const getProjectBranch = async (
return branch;
};

export const getOwnedProjectsBySearchCriteria = async (
/**
* Get project data 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<GitlabAPIProject[]> => {
const roleFilter = isGitlabMaintainerTokenEnabled()
? { min_access_level: GitLabAccessLevels.MAINTAINER.toString() }
: { owned: 'true' };

const params = {
owned: 'true',
...roleFilter,
search,
};

Expand Down
3 changes: 2 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export const GITLAB_EVENT_WEBTRIGGER = 'gitlab-event-webtrigger';
export const DEFAULT_COMPONENT_TYPE_ID = 'SERVICE';

export const STORAGE_KEYS = {
GROUP_KEY_PREFIX: 'group-',
GROUP_NAME_KEY_PREFIX: 'group-',
WEBHOOK_KEY_PREFIX: 'webhook-id-',
TOKEN_ROLE_PREFIX: 'tokenRole-',
WEBHOOK_SIGNATURE_PREFIX: 'webhook-sign-id-',
LAST_SYNC_TIME: 'lastSyncTime',
CURRENT_IMPORT_TOTAL_PROJECTS: 'currentImportTotalProjects',
Expand Down
1 change: 1 addition & 0 deletions src/resolverTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export enum AuthErrorTypes {
INVALID_GROUP_TOKEN = 'INVALID_GROUP_TOKEN',
INVALID_GROUP_TOKEN_NAME = 'INVALID_GROUP_TOKEN_NAME',
INCORRECT_GROUP_TOKEN_SCOPES = 'INCORRECT_GROUP_TOKEN_SCOPES',
INVALID_GROUP_NAME = 'INVALID_GROUP_NAME',
UNEXPECTED_ERROR = 'UNEXPECTED_ERROR',
}
export enum ImportErrorTypes {
Expand Down
18 changes: 13 additions & 5 deletions src/resolvers/admin-resolvers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Resolver from '@forge/resolver';

import graphqlGateway, { CompassComponentTypeObject } from '@atlassian/forge-graphql';
import { getGroupProjects } from '../services/fetch-projects';
import graphqlGateway from '@atlassian/forge-graphql';
import { AuthErrorTypes, GitlabAPIGroup, ResolverResponse, DefaultErrorTypes, FeaturesList } from '../resolverTypes';
import { connectGroup, InvalidGroupTokenError } from '../services/group';

Expand All @@ -10,6 +9,7 @@ import { disconnectGroup } from '../services/disconnect-group';
import { getForgeAppId } from '../utils/get-forge-app-id';
import { getLastSyncTime } from '../services/last-sync-time';
import { appId, connectedGroupsInfo, getFeatures, groupsAllExisting } from './shared-resolvers';
import { ConnectGroupInput } from '../types';

const resolver = new Resolver();

Expand Down Expand Up @@ -37,13 +37,21 @@ resolver.define('groups/connectedInfo', async (): Promise<ResolverResponse<Gitla

resolver.define('groups/connect', async (req): Promise<ResolverResponse> => {
const {
payload: { groupToken, groupTokenName },
payload: { groupToken, groupTokenName, groupRole, groupName, webhookId, webhookSecretToken },
context: { cloudId },
} = req;
try {
const groupId = await connectGroup(groupToken, groupTokenName);
const input: ConnectGroupInput = {
token: groupToken,
tokenName: groupTokenName,
tokenRole: groupRole,
groupName,
webhookId,
webhookSecretToken,
};
const groupId = await connectGroup(input);

await setupAndValidateWebhook(groupId);
await setupAndValidateWebhook(groupId, webhookId, webhookSecretToken);

await graphqlGateway.compass.asApp().synchronizeLinkAssociations({
cloudId,
Expand Down
3 changes: 2 additions & 1 deletion src/services/clear-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ const clearStorageEntriesForGroup = async (groupId: string): Promise<void> => {
console.log('Clearing storage entries start');

const groupKeys = [
`${STORAGE_KEYS.GROUP_KEY_PREFIX}${groupId}`,
`${STORAGE_KEYS.GROUP_NAME_KEY_PREFIX}${groupId}`,
`${STORAGE_KEYS.TOKEN_ROLE_PREFIX}${groupId}`,
`${STORAGE_KEYS.WEBHOOK_KEY_PREFIX}${groupId}`,
`${STORAGE_KEYS.WEBHOOK_SIGNATURE_PREFIX}${groupId}`,
];
Expand Down
4 changes: 2 additions & 2 deletions src/services/data-provider-link-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
4 changes: 2 additions & 2 deletions src/services/data-provider-link-parser.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion src/services/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const isDocumentComponentLinksDisabled = (defaultValue = false): boolean => {
return process.env.DISABLE_DOCUMENT_COMPONENT_LINKS === 'true' || defaultValue;
};

const isGitlabMaintainerTokenEnabled = (defaultValue = false): boolean => {
export const isGitlabMaintainerTokenEnabled = (defaultValue = false): boolean => {
return process.env.ENABLE_GITLAB_MAINTAINER_TOKEN === 'true' || defaultValue;
};

Expand Down
Loading

0 comments on commit 55520d5

Please sign in to comment.