import axios from 'axios'; import ora from 'ora'; import GitlabUser from '../shared/types/Gitlab/GitlabUser'; import GitlabRoute from '../shared/types/Gitlab/GitlabRoute'; import SharedConfig from '../shared/config/SharedConfig'; import GitlabRepository from '../shared/types/Gitlab/GitlabRepository'; import fs from 'fs-extra'; import { spawn } from 'child_process'; class GitlabManager { private getApiUrl(route: GitlabRoute): string { return `${ SharedConfig.gitlab.apiURL }${ route }`; } public async testToken(verbose: boolean = true): Promise<[ boolean, boolean ]> { if ( verbose ) { ora('Checking Gitlab token: ').start().info(); } const result: [ boolean, boolean ] = [ false, false ]; type NotificationSettings = { level: string } let notificationSettings: NotificationSettings = { level: 'error' }; // Test read access { const spinnerRead: ora.Ora = ora({ text : `Read access`, indent: 4 }); if ( verbose ) { spinnerRead.start(); } try { notificationSettings = (await this.getNotificationSettings()).data as NotificationSettings; result[0] = true; if ( verbose ) { spinnerRead.succeed(); } } catch ( e ) { if ( verbose ) { spinnerRead.fail(); } } } // Test write access { const spinnerWrite: ora.Ora = ora({ text : `Write access`, indent: 4 }); if ( verbose ) { spinnerWrite.start(); } const someLevelTypes = [ 'disabled', 'participating' ]; try { const oldSettings = notificationSettings; const newSettings = { level: someLevelTypes[someLevelTypes[0] == oldSettings.level ? 1 : 0] }; await this.setNotificationSettings(newSettings); await this.setNotificationSettings(oldSettings); result[1] = true; if ( verbose ) { spinnerWrite.succeed(); } } catch ( e ) { if ( verbose ) { spinnerWrite.fail(); } } } return result; } public getNotificationSettings() { return axios.get(this.getApiUrl(GitlabRoute.NOTIFICATION_SETTINGS)); } public setNotificationSettings(newSettings: Record<string, string>) { return axios.put(this.getApiUrl(GitlabRoute.NOTIFICATION_SETTINGS), { params: new URLSearchParams(newSettings) }); } private async getGitlabUsers(paramsToSearch: Array<string | number>, paramName: string, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> { try { return await Promise.all(paramsToSearch.map(async param => { const spinner: ora.Ora = ora({ text : `Getting user ${ param }`, indent: verboseIndent }); if ( verbose ) { spinner.start(); } const params: { [key: string]: unknown } = {}; params[paramName] = param; const user = await axios.get<Array<GitlabUser>>(this.getApiUrl(GitlabRoute.USERS_GET), { params: params }); if ( user.data[0] ) { const gitlabUser = user.data[0]; if ( verbose ) { spinner.succeed(`${ gitlabUser.username } (${ gitlabUser.id })`); } return gitlabUser; } else { if ( verbose ) { spinner.fail(`${ param }`); } } })); } catch ( e ) { return [ undefined ]; } } public async getUsersById(ids: Array<number>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> { return await this.getGitlabUsers(ids, 'id', verbose, verboseIndent); } public async getUsersByUsername(usernames: Array<string>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> { return await this.getGitlabUsers(usernames, 'search', verbose, verboseIndent); } public async getRepository(repoId: number): Promise<GitlabRepository> { return await axios.get(this.getApiUrl(GitlabRoute.REPOSITORY_GET).replace('{{id}}', repoId.toString())); } public async fetchMembers(options: { members_id?: Array<number>, members_username?: Array<string> }): Promise<Array<GitlabUser> | false> { if ( options.members_id || options.members_username ) { ora('Checking Gitlab members:').start().info(); } let members: Array<GitlabUser> = []; async function getMembers<T>(context: unknown, functionName: string, paramsToSearch: Array<T>): Promise<boolean> { const result = await ((context as { [functionName: string]: (arg: Array<T>, verbose: boolean, verboseIndent: number) => Promise<Array<GitlabUser | undefined>> })[functionName])(paramsToSearch, true, 8); if ( result.every(user => user) ) { members = members.concat(result as Array<GitlabUser>); return true; } else { return false; } } let result = true; if ( options.members_id ) { ora({ text : 'Fetching members by id:', indent: 4 }).start().info(); result = await getMembers(this, 'getUsersById', options.members_id); } if ( options.members_username ) { ora({ text : 'Fetching members by username:', indent: 4 }).start().info(); result = result && await getMembers(this, 'getUsersByUsername', options.members_username); } if ( !result ) { return false; } members = members.removeObjectDuplicates(gitlabUser => gitlabUser.id); return members; } public async cloneRepository(clonePath: string | boolean, repositorySshUrl: string, folderName?: string, verbose: boolean = false, verboseIndent: number = 0) { let path = './'; if ( typeof clonePath === 'string' ) { path = clonePath; fs.mkdirSync(path, { recursive: true }); } let cloningSpinner!: ora.Ora; if ( verbose ) { cloningSpinner = ora({ text : 'Cloning the repository...', indent: verboseIndent }).start(); } try { await new Promise<void>((resolve, reject) => { const gitClone = spawn(`git clone ${ repositorySshUrl } "${ folderName ?? '' }"`, { cwd : path, shell: true }); gitClone.on('exit', (code) => { code !== null && code == 0 ? resolve() : reject(); }); }); if ( verbose ) { cloningSpinner.succeed('Repository cloned'); } } catch ( error ) { if ( verbose ) { cloningSpinner.fail('Error while cloning the repository'); } } } } export default new GitlabManager();