From 3be1d15009fd138543d81dcb84eb73e6561652f3 Mon Sep 17 00:00:00 2001 From: Vincent LI Date: Sat, 7 Oct 2023 16:33:20 +0200 Subject: [PATCH] add gitlab provider --- src/api/repository.controller.ts | 2 +- src/api/repository.provider.ts | 6 ++- src/api/repository.strategy.ts | 5 ++- src/provider/gitlab.provider.ts | 73 ++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/provider/gitlab.provider.ts diff --git a/src/api/repository.controller.ts b/src/api/repository.controller.ts index 6d3793c..465eb43 100644 --- a/src/api/repository.controller.ts +++ b/src/api/repository.controller.ts @@ -12,7 +12,7 @@ interface LocalProviderConfig { } interface RemoteProviderConfig { - type: 'github' /* | 'gitlab' */ + type: 'github' | 'gitlab' repositories: string[] token?: string } diff --git a/src/api/repository.provider.ts b/src/api/repository.provider.ts index 8b46c3a..ed39f63 100644 --- a/src/api/repository.provider.ts +++ b/src/api/repository.provider.ts @@ -1,9 +1,10 @@ import { GithubProvider } from '../provider/github.provider' +import { GitlabProvider } from '../provider/gitlab.provider' import { LocalProvider } from '../provider/local.provider' import { FileSystemManager } from '../utils/fileSystem.utils' import { ProviderConfig } from './repository.controller' -export type Provider = LocalProvider | GithubProvider +export type Provider = LocalProvider | GithubProvider | GitlabProvider export class RepositoryProvider { private provider!: Provider @@ -19,6 +20,9 @@ export class RepositoryProvider { case 'github': this.provider = new GithubProvider(config.repositories, config.token) break + case 'gitlab': + this.provider = new GitlabProvider(config.repositories, config.token) + break default: break } diff --git a/src/api/repository.strategy.ts b/src/api/repository.strategy.ts index 861886e..d93975d 100644 --- a/src/api/repository.strategy.ts +++ b/src/api/repository.strategy.ts @@ -1,7 +1,7 @@ import { ErrorManager } from '../utils/error.utils' import { ProviderConfig } from './repository.controller' -const knownRepositories = ['github.com'] as const +const knownRepositories = ['github.com', 'gitlab.com'] as const export interface ProviderStrategy { isMatch(docLocation: string): boolean @@ -31,9 +31,10 @@ export class RepositoryProviderStrategy implements ProviderStrategy { return { type: this.extractRepositoryName(domain), repositories: [docLocation] } } - private extractRepositoryName(domain: string): 'github' | never { + private extractRepositoryName(domain: string): 'github' | 'gitlab' | never { const type = domain.split('.')[0] if (type === 'github') return type + if (type === 'gitlab') return type ErrorManager.outputError(`Unrecognized repository type: ${type}`) throw new Error(`Unrecognized repository type: ${type}`) diff --git a/src/provider/gitlab.provider.ts b/src/provider/gitlab.provider.ts new file mode 100644 index 0000000..98352bb --- /dev/null +++ b/src/provider/gitlab.provider.ts @@ -0,0 +1,73 @@ +import { Documentation } from '../association.manager' +import { FileSystemManager } from '../utils/fileSystem.utils' +import { ReplacerTextProvider } from '../utils/replacerText.utils' + +interface GitlabResponse { + type: string + name: string + path: string +} + +export class GitlabProvider { + private baseUrl = 'https://gitlab.com/api/v4' + private fileSystem + private repository + private transformImageURL + + constructor(repository: string[], token?: string) { + this.repository = this.getProjectId(repository) + this.fileSystem = new FileSystemManager() + this.transformImageURL = new ReplacerTextProvider(repository[0], token) + } + + public async getDocumentations(): Promise { + const documentations: Documentation[] = [] + const data = await this.getRepoContent( + `/projects/${this.repository.id.replace('/', '%2F')}/repository/tree?ref=main&recursive=true` + ) + + await this.fetchDocumentation(data, documentations) + return documentations + } + + private fetchDocumentation = async ( + repositoryContents: GitlabResponse[], + documentations: Documentation[] + ): Promise => { + for (const repositoryContent of repositoryContents) { + if ( + repositoryContent.type === 'blob' && + this.fileSystem.isFileOfInterest(repositoryContent.name) + ) { + const documentation = await this.getFile(repositoryContent) + documentation.content = this.transformImageURL.replacer(documentation.content) + documentations.push(documentation) + } + } + } + + public async getRepoContent(route: string) { + const response = await fetch(this.baseUrl + route) + return await response.json() + } + + public async getFile(file: GitlabResponse): Promise { + const projectIdEncoded = encodeURIComponent(this.repository.id) + const filePathEncoded = encodeURIComponent(file.path) + const url = `https://gitlab.com/api/v4/projects/${projectIdEncoded}/repository/files/${filePathEncoded}/raw` + const response = await fetch(url) + const content = await response.text() + + return { + type: this.fileSystem.getExtension(file.name)!, + name: file.name, + content: content, + path: `https://gitlab.com/${this.repository.id}/-/blob/main/${file.path}`, + } + } + + public getProjectId(repository: string[]) { + const urlParts = repository[0].split('/') + return { id: `${urlParts[3]}/${urlParts[4]}`, token: repository[1] } + } +}