Skip to content
Snippets Groups Projects
Select Git revision
  • d83a2ee02e47048160bc46d10aeef5a37e480f02
  • main default protected
  • add_export_route
  • add_route_assignments
  • 4.1.0-dev
  • 4.0.0
  • 3.5.3
  • 3.5.3-dev
  • 3.5.2
  • 3.5.2-dev
  • 3.5.1
  • 3.5.1-dev
  • 3.5.0
  • 3.4.2
  • 3.4.1
  • 3.4.0
  • 3.3.0
  • 3.2.0
  • 3.1.3
  • 3.1.2
  • 3.1.1
  • 3.1.0
  • 3.0.1
  • 3.0.0
24 results

GitlabManager.ts

Blame
  • 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();