@sleavely/bitbucket
Version:
Client and utils for Bitbucket Cloud REST APIs
270 lines (267 loc) • 11 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
BitbucketClient: () => BitbucketClient
});
module.exports = __toCommonJS(src_exports);
// src/BitbucketClient.ts
var import_form_data = __toESM(require("form-data"));
var import_got = __toESM(require("got"));
var import_path = require("path");
var BitbucketClient = class {
client;
constructor(opts) {
if (!opts.auth) {
throw new Error("Missing auth params");
}
this.client = import_got.default.extend({
prefixUrl: "https://api.bitbucket.org/2.0/",
headers: {
Accept: "application/json"
},
responseType: "json",
...opts.auth
});
}
extend(gotOpts) {
this.client = this.client.extend(gotOpts);
}
// #region User
async getCurrentUser() {
return await this.client("user").json();
}
// #endregion
// #region Workspaces
async listWorkspaces() {
const { values } = await this.client("user/permissions/workspaces").json();
return values.map(({ permission, workspace }) => {
return { ...workspace, permission };
});
}
async getWorkspace(workspace) {
return await this.client(`workspaces/${workspace}}`).json();
}
// #endregion
// #region Projects
async getProject(workspace, projectKey) {
return await this.client(`workspaces/${workspace}/projects/${projectKey}`).json();
}
// #endregion
// #region Repositories
async getRepositoriesByProject(workspace, projectKey, requestPage) {
const res = await this.client(`repositories/${workspace}?q=project.key="${projectKey}"${requestPage ? `&page=${requestPage}` : ""}`).json();
const { values, next, page: currentPage } = res;
if (next) return [...values, ...await this.getRepositoriesByProject(workspace, projectKey, currentPage + 1)];
return values;
}
async getRepository(workspace, repoSlug) {
return await this.client(`repositories/${workspace}/${repoSlug}`).json();
}
/**
* @deprecated This only returns repo-level defaults, not inherited ones. Use getEffectiveDefaultReviewers instead.
* @see getEffectiveDefaultReviewers
*/
async getDefaultReviewers(workspace, repoSlug) {
const { values } = await this.client(`repositories/${workspace}/${repoSlug}/default-reviewers`).json();
return values;
}
/**
* Adds the specified user to the repository's list of default reviewers. This method is idempotent. Adding a user a second time has no effect.
*/
async addDefaultReviewer(workspace, repoSlug, targetUser) {
return await this.client.put(`repositories/${workspace}/${repoSlug}/default-reviewers/${targetUser}`).json();
}
async removeDefaultReviewer(workspace, repoSlug, targetUser) {
await this.client.delete(`repositories/${workspace}/${repoSlug}/default-reviewers/${targetUser}`);
}
/**
* Includes both default reviewers defined at the repository level as well as those inherited from its project.
*/
async getEffectiveDefaultReviewers(workspace, repoSlug) {
const { values } = await this.client(`repositories/${workspace}/${repoSlug}/effective-default-reviewers`).json();
return values.map(({ user, reviewer_type }) => ({ ...user, reviewer_type }));
}
// #endregion
// #region Pipelines
/**
* Gets 10 most recent pipelines that were created
*/
async getPipelines(workspace, repoSlug) {
const { values } = await this.client(`repositories/${workspace}/${repoSlug}/pipelines/?sort=-created_on`).json();
return values;
}
async getPipeline(workspace, repoSlug, pipelineUuid) {
return await this.client(`repositories/${workspace}/${repoSlug}/pipelines/${pipelineUuid}`).json();
}
async getCommitStatuses(workspace, repoSlug, commit) {
const { values } = await this.client(`repositories/${workspace}/${repoSlug}/commit/${commit}/statuses`).json();
return values;
}
/**
* Returns repository level variables
*/
async getRepoVariables(workspace, repoSlug) {
const { values } = await this.client(`repositories/${workspace}/${repoSlug}/pipelines_config/variables`).json();
return values;
}
/**
* Regardless of whether it exists or not
*/
async setRepoVariable(workspace, repoSlug, varName, varValue) {
const repoVars = await this.getRepoVariables(workspace, repoSlug);
const existingVariable = repoVars.find(({ key }) => key === varName);
if (!existingVariable) {
return await this.createRepoVariable(workspace, repoSlug, varName, varValue);
} else {
return await this.updateRepoVariable(workspace, repoSlug, existingVariable.uuid, varName, varValue);
}
}
async createRepoVariable(workspace, repoSlug, varName, varValue) {
return await this.client.post(
`repositories/${workspace}/${repoSlug}/pipelines_config/variables`,
{
json: {
type: "pipeline_variable",
key: varName,
value: varValue,
secured: true
}
}
).json();
}
async updateRepoVariable(workspace, repoSlug, varUuid, varName, varValue) {
return await this.client.put(
`repositories/${workspace}/${repoSlug}/pipelines_config/variables/${varUuid}`,
{
json: {
type: "pipeline_variable",
uuid: varUuid,
key: varName,
value: varValue,
secured: true
}
}
).json();
}
async deleteRepoVariable(workspace, repoSlug, varName) {
const repoVars = await this.getRepoVariables(workspace, repoSlug);
const variableProps = repoVars.find(({ key }) => key === varName);
if (!variableProps) return null;
return await this.client.delete(`repositories/${workspace}/${repoSlug}/pipelines_config/variables/${variableProps.uuid}`);
}
// #endregion
// #region Pull Requests
async createPullRequest(workspace, repoSlug, title, sourceBranch, destinationBranch) {
const currentUser = await this.getCurrentUser();
const defaultReviewers = (await this.getEffectiveDefaultReviewers(workspace, repoSlug)).filter((reviewer) => currentUser.uuid !== reviewer.uuid);
const destination = destinationBranch ? { branch: { name: destinationBranch } } : void 0;
return await this.client.post(
`repositories/${workspace}/${repoSlug}/pullrequests`,
{
json: {
title,
source: { branch: { name: sourceBranch } },
destination,
reviewers: defaultReviewers,
close_source_branch: true
}
}
).json();
}
// #endregion
// #region Files
async getFile(workspace, repoSlug, commitOrRef, filePath) {
const absolutePath = import_path.posix.resolve("/", filePath);
return await this.client.extend({ responseType: "text" })(`repositories/${workspace}/${repoSlug}/src/${commitOrRef}${absolutePath}`).then((res) => res.body).catch((err) => {
if (err.response && err.response.statusCode === 404) return null;
throw err;
});
}
/**
* @param workspace
* @param repoSlug
* @param filePath A file path in the repository
* @param contents
* @param message The commit message
* @param author Should be in format of `Joakim Hedlund <contact@joakimhedlund.com>`. Defaults to the current user.
* @param branch The name of the branch that the new commit should be created on. When omitted, the commit will be created on top of the main branch
* @returns Full commit hash
*/
async commitFile(workspace, repoSlug, filePath, contents, message = "Posted a file via API", author, branch) {
const form = new import_form_data.default();
form.append(filePath, contents);
if (message) form.append("message", message);
if (author) form.append("author", author);
if (branch) form.append("branch", branch);
const res = await this.client.post(`repositories/${workspace}/${repoSlug}/src`, {
body: form
});
const commitHash = res.headers?.location?.split("/")?.at(-1) ?? "<unknown>";
return commitHash;
}
/**
* @param workspace
* @param repoSlug
* @param filePath A file path in the repository
* @param message The commit message
* @param author Should be in format of `Joakim Hedlund <contact@joakimhedlund.com>`. Defaults to the current user.
* @param branch The name of the branch that the new commit should be created on. When omitted, the commit will be created on top of the main branch
* @returns Full commit hash
*/
async removeFile(workspace, repoSlug, filePath, message = "Removed a file via API", author, branch) {
const form = new import_form_data.default();
form.append("files", filePath);
if (message) form.append("message", message);
if (author) form.append("author", author);
if (branch) form.append("branch", branch);
const res = await this.client.post(`repositories/${workspace}/${repoSlug}/src`, {
body: form
});
const commitHash = res.headers?.location?.split("/")?.at(-1) ?? "<unknown>";
return commitHash;
}
async codeSearch(workspace, query, requestPage) {
const queryparams = new URLSearchParams({
search_query: query,
fields: "+values.file.commit.repository"
});
if (requestPage) queryparams.append("page", requestPage.toString());
const res = await this.client(`workspaces/${workspace}/search/code?${queryparams.toString()}`).json();
const { values, next, page: currentPage } = res;
if (next) return [...values, ...await this.codeSearch(workspace, query, currentPage + 1)];
return values;
}
// #endregion
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BitbucketClient
});