Select Git revision
GitlabManager.ts
Forked from
Dojo Project (HES-SO) / Projects / Backend / DojoBackendAPI
Source project has a limited visibility.
GitlabManager.ts 11.46 KiB
import axios from 'axios';
import Config from '../config/Config';
import GitlabRepository from '../shared/types/Gitlab/GitlabRepository';
import GitlabAccessLevel from '../shared/types/Gitlab/GitlabAccessLevel';
import GitlabMember from '../shared/types/Gitlab/GitlabMember';
import { StatusCodes } from 'http-status-codes';
import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibility';
import GitlabUser from '../shared/types/Gitlab/GitlabUser';
import GitlabTreeFile from '../shared/types/Gitlab/GitlabTreeFile';
import parseLinkHeader from 'parse-link-header';
import GitlabFile from '../shared/types/Gitlab/GitlabFile';
import express from 'express';
import GitlabRoute from '../shared/types/Gitlab/GitlabRoute';
import SharedConfig from '../shared/config/SharedConfig';
import GitlabProfile from '../shared/types/Gitlab/GitlabProfile';
class GitlabManager {
private getApiUrl(route: GitlabRoute): string {
return `${ SharedConfig.gitlab.apiURL }${ route }`;
}
public async getUserProfile(token: string): Promise<GitlabProfile | undefined> {
try {
return (await axios.get<GitlabProfile>(this.getApiUrl(GitlabRoute.PROFILE_GET), {
headers: {
DojoOverrideAuthorization: true,
DojoAuthorizationHeader : 'Authorization',
DojoAuthorizationValue : `Bearer ${ token }`
}
})).data;
} catch ( e ) {
return undefined;
}
}
public async getUserById(id: number): Promise<GitlabUser | undefined> {
try {
const user = (await axios.get<GitlabUser>(`${ this.getApiUrl(GitlabRoute.USERS_GET) }/${ String(id) }`)).data;
return user.id === id ? user : undefined;
} catch ( e ) {
return undefined;
}
}
public async getUserByUsername(username: string): Promise<GitlabUser | undefined> {
try {
const params: Record<string, string> = {};
params['search'] = username;
const user = (await axios.get<Array<GitlabUser>>(this.getApiUrl(GitlabRoute.USERS_GET), { params: params })).data[0];
return user.username === username ? user : undefined;
} catch ( e ) {
return undefined;
}
}
async getRepository(projectIdOrNamespace: string): Promise<GitlabRepository> {
const response = await axios.get<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_GET).replace('{{id}}', encodeURIComponent(projectIdOrNamespace)));
return response.data;
}
async getRepositoryMembers(idOrNamespace: string): Promise<Array<GitlabMember>> {
const response = await axios.get<Array<GitlabMember>>(this.getApiUrl(GitlabRoute.REPOSITORY_MEMBERS_GET).replace('{{id}}', encodeURIComponent(idOrNamespace)));
return response.data;
}
async createRepository(name: string, description: string, visibility: string, initializeWithReadme: boolean, namespace: number, sharedRunnersEnabled: boolean, wikiEnabled: boolean, import_url: string): Promise<GitlabRepository> {
const response = await axios.post<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_CREATE), {
name : name,
description : description,
import_url : import_url,
initialize_with_readme: initializeWithReadme,
namespace_id : namespace,
shared_runners_enabled: sharedRunnersEnabled,
visibility : visibility,
wiki_enabled : wikiEnabled
});
return response.data;
}
async deleteRepository(repoId: number): Promise<void> {
return await axios.delete(this.getApiUrl(GitlabRoute.REPOSITORY_DELETE).replace('{{id}}', String(repoId)));
}
async forkRepository(forkId: number, name: string, path: string, description: string, visibility: string, namespace: number): Promise<GitlabRepository> {
const response = await axios.post<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_FORK).replace('{{id}}', String(forkId)), {
name : name,
path : path,
description : description,
namespace_id: namespace,
visibility : visibility
});
return response.data;
}
async editRepository(repoId: number, newAttributes: Partial<GitlabRepository>): Promise<GitlabRepository> {
const response = await axios.put<GitlabRepository>(this.getApiUrl(GitlabRoute.REPOSITORY_EDIT).replace('{{id}}', String(repoId)), newAttributes);
return response.data;
}
async changeRepositoryVisibility(repoId: number, visibility: GitlabVisibility): Promise<GitlabRepository> {
return await this.editRepository(repoId, { visibility: visibility.toString() });
}
async addRepositoryMember(repoId: number, userId: number, accessLevel: GitlabAccessLevel): Promise<GitlabMember> {
const response = await axios.post<GitlabMember>(this.getApiUrl(GitlabRoute.REPOSITORY_MEMBER_ADD).replace('{{id}}', String(repoId)), {
user_id : userId,
access_level: accessLevel
});
return response.data;
}
async addRepositoryVariable(repoId: number, key: string, value: string, isProtected: boolean, isMasked: boolean): Promise<GitlabMember> {
const response = await axios.post<GitlabMember>(this.getApiUrl(GitlabRoute.REPOSITORY_VARIABLES_ADD).replace('{{id}}', String(repoId)), {
key : key,
variable_type: 'env_var',
value : value,
protected : isProtected,
masked : isMasked
});
return response.data;
}
async addRepositoryBadge(repoId: number, linkUrl: string, imageUrl: string, name: string): Promise<GitlabMember> {
const response = await axios.post<GitlabMember>(this.getApiUrl(GitlabRoute.REPOSITORY_BADGES_ADD).replace('{{id}}', String(repoId)), {
link_url : linkUrl,
image_url: imageUrl,
name : name
});
return response.data;
}
async checkTemplateAccess(projectIdOrNamespace: string, req: express.Request): Promise<StatusCodes> {
// Get the Gitlab project and check if it have public or internal visibility
try {
const project: GitlabRepository = await this.getRepository(projectIdOrNamespace);
if ( [ GitlabVisibility.PUBLIC.valueOf(), GitlabVisibility.INTERNAL.valueOf() ].includes(project.visibility) ) {
return StatusCodes.OK;
}
} catch ( e ) {
return StatusCodes.NOT_FOUND;
}
// Check if the user and dojo are members (with at least reporter access) of the project
const members = await this.getRepositoryMembers(projectIdOrNamespace);
const isUsersAtLeastReporter = {
user: false,
dojo: false
};
members.forEach(member => {
if ( member.access_level >= GitlabAccessLevel.REPORTER ) {
if ( member.id === req.session.profile.id ) {
isUsersAtLeastReporter.user = true;
} else if ( member.id === Config.gitlab.account.id ) {
isUsersAtLeastReporter.dojo = true;
}
}
});
return isUsersAtLeastReporter.user && isUsersAtLeastReporter.dojo ? StatusCodes.OK : StatusCodes.UNAUTHORIZED;
}
async protectBranch(repoId: number, branchName: string, allowForcePush: boolean, allowedToMerge: GitlabAccessLevel, allowedToPush: GitlabAccessLevel, allowedToUnprotect: GitlabAccessLevel): Promise<GitlabMember> {
const response = await axios.post<GitlabMember>(this.getApiUrl(GitlabRoute.REPOSITORY_BRANCHES_PROTECT).replace('{{id}}', String(repoId)), {
name : branchName,
allow_force_push : allowForcePush,
merge_access_level : allowedToMerge.valueOf(),
push_access_level : allowedToPush.valueOf(),
unprotect_access_level: allowedToUnprotect.valueOf()
});
return response.data;
}
async getRepositoryTree(repoId: number, recursive: boolean = true, branch: string = 'main'): Promise<Array<GitlabTreeFile>> {
const address: string | undefined = this.getApiUrl(GitlabRoute.REPOSITORY_TREE).replace('{{id}}', String(repoId));
let params: Partial<parseLinkHeader.Link | { recursive: boolean, per_page: number }> | undefined = {
pagination: 'keyset',
recursive : recursive,
per_page : 100,
ref : branch
};
const results: Array<GitlabTreeFile> = [];
while ( params !== undefined ) {
const response = await axios.get<Array<GitlabTreeFile>>(address, {
params: params
});
results.push(...response.data);
if ( 'link' in response.headers ) {
params = parseLinkHeader(response.headers['link'])?.next ?? undefined;
} else {
params = undefined;
}
}
return results;
}
async getFile(repoId: number, filePath: string, branch: string = 'main'): Promise<GitlabFile> {
const response = await axios.get<GitlabFile>(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
params: {
ref: branch
}
});
return response.data;
}
private async createUpdateFile(create: boolean, repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
const axiosFunction = create ? axios.post : axios.put;
await axiosFunction(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
encoding : 'base64',
branch : branch,
commit_message: commitMessage,
content : fileBase64,
author_name : authorName,
author_email : authorMail
});
}
async createFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
return this.createUpdateFile(true, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail);
}
async updateFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
return this.createUpdateFile(false, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail);
}
async deleteFile(repoId: number, filePath: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined) {
await axios.delete(this.getApiUrl(GitlabRoute.REPOSITORY_FILE).replace('{{id}}', String(repoId)).replace('{{filePath}}', encodeURIComponent(filePath)), {
data: {
branch : branch,
commit_message: commitMessage,
author_name : authorName,
author_email : authorMail
}
});
}
}
export default new GitlabManager();