UNPKG

repository-provider

Version:

abstract interface to git repository providers like github, bitbucket and gitlab

271 lines (233 loc) 7.53 kB
import { matcher } from "matching-iterator"; import { Branch } from "./branch.mjs"; import { Repository } from "./repository.mjs"; import { OwnedObject } from "./owned-object.mjs"; import { asArray, stripBaseName, stripBaseNames } from "./util.mjs"; /** * Mixin to define a class able to handle a collection of repositories. * @param {Object} base to be extendet */ export function RepositoryOwner(base) { return class RepositoryOwner extends base { #repositories = new Map(); /** * Normalizes a repository name. * Strips branch away. * @param {string} name * @param {boolean} forLookup * @return {string} normalized name */ normalizeRepositoryName(name, forLookup) { const i = name.indexOf("#"); if (i >= 0) { name = name.substring(0, i); } const parts = name.split(/\//); if (parts.length >= 2) { if (parts[parts.length - 2] === this.name) { name = parts[parts.length - 1]; } } if (forLookup && !this.areRepositoryNamesCaseSensitive) { return name.toLowerCase(); } return name; } /** * Lookup a repository. * @param {string} [name] of the repository may contain a #branch * @return {Promise<Repository|undefined>} */ async repository(name) { if (name !== undefined) { await this.initializeRepositories(); return this.#repositories.get(this.normalizeRepositoryName(name, true)); } } /** * List repositories for the owner. * @param {string[]|string} [patterns] * @return {AsyncIterable<Repository>} all matching repositories of the owner */ async *repositories(patterns) { patterns = asArray(patterns); for (let p of patterns) { p = stripBaseName(p, this.provider.repositoryBases); const m = p.match(/^(\w+:)/); if (m) { if (!this.supportsBase(m[1])) { return; } } } patterns = patterns.map(p => p.replace(/#.*$/, "")); await this.initializeRepositories(); yield* matcher( this.#repositories.values(), stripBaseNames(patterns, this.provider.repositoryBases), { caseSensitive: this.areRepositoryNamesCaseSensitive, name: "name" } ); } /** * Lookup entity of a given type and name. * @param {string} type * @param {string} [name] * @param {function} [split] * @param {Object} [defaultItem] * @returns {Promise<OwnedObject|undefined>} from a repository */ async lookup(type, name, split, defaultItem) { if (name !== undefined) { await this.initializeRepositories(); name = stripBaseName(name, this.provider.repositoryBases); const [repoName, typeName] = split ? split(name) : name.split("/"); const repository = this.#repositories.get(repoName); if (repository) { if (typeName === undefined && defaultItem) { return defaultItem(repository); } else { return repository[type](typeName); } } } } /** * List entities for a given type and pattern. * @param {string} type * @param {string[]|string} [patterns] * @param {function} [split] * @param {Object} [defaultItem] * @return {AsyncIterable<OwnedObject>} matching type and pattern */ async *list(type, patterns, split, defaultItem) { await this.initializeRepositories(); patterns = stripBaseNames(patterns, this.provider.repositoryBases); for (const pattern of asArray(patterns)) { const [repoPattern, typePattern] = split ? split(pattern) : pattern.split("/"); for (const name of matcher(this.#repositories.keys(), repoPattern, { caseSensitive: this.areRepositoriesCaseSensitive })) { const repository = this.#repositories.get(name); if (typePattern === undefined && defaultItem) { const item = await defaultItem(repository); if (item !== undefined) { yield item; } } else { yield* repository[type](typePattern); } } } } /** * Create a new {@link Repository} in the provider. * If there is already if repository for the given name it will be returned. * @param {string} name * @param {Object} [options] * @return {Promise<Repository>} newly created repository (if not already present) */ async createRepository(name, options) { return this.addRepository(name, options); } /** * Add a {@link Repository} to the group. * Only adds the repository to the in memory representation (does not execute any provider actions). * @param {string} name * @param {Object} [options] * @return {Promise<Repository>} newly created repository */ addRepository(name, options) { return ( this.#repositories.get(this.normalizeRepositoryName(name, true)) || new this.repositoryClass(this, name, options) ); } _addRepository(repository) { this.#repositories.set( this.normalizeRepositoryName(repository.name, true), repository ); } /** * Delete a repository. * @param {string} name * @return {Promise<any>} */ async deleteRepository(name) { this.#repositories.delete(this.normalizeRepositoryName(name, true)); } initializeRepositories() {} /** * Lookup a branch. * First lookup repository then the branch. * If no branch was specified then the default branch will be delivered. * @see {@link Repository#defaultBranch} * @param {string} name with optional branch name as '#myBranchName' * @return {Promise<Branch|undefined>} */ async branch(name) { // @ts-ignore return this.lookup( "branch", name, name => name.split(/#/), repository => repository.defaultBranch ); } /** * List branches for the owner. * @param {string[]|string} [patterns] * @return {AsyncIterable<Branch>} all matching branches of the owner */ async *branches(patterns) { // @ts-ignore yield* this.list( "branches", patterns, pattern => pattern.split(/#/), repository => repository.defaultBranch ); } async tag(name) { return this.lookup("tag", name, name => name.split(/#/)); } async *tags(patterns) { yield* this.list("tags", patterns, pattern => pattern.split(/#/)); } async pullRequest(name) { return this.lookup("pullRequest", name); } async *pullRequests(patterns) { yield* this.list("pullRequests", patterns); } async project(name) { return this.lookup("project", name); } async *projects(patterns) { yield* this.list("projects", patterns); } async application(name) { return this.lookup("application", name); } async *applications(patterns) { yield* this.list("applications", patterns); } async milestone(name) { return this.lookup("milestone", name); } async *milestones(patterns) { yield* this.list("milestones", patterns); } async hook(name) { return this.lookup("hook", name); } async *hooks(patterns) { yield* this.list("hooks", patterns); } }; }