UNPKG

@bs-plugins/jira

Version:

Bamboo Shell plugin for JIRA

527 lines (524 loc) 19.1 kB
import { BSPlugin, bs } from '@bs-core/shell'; // imports here // Config consts here // Misc constants here const JiraResources = { session: "/rest/auth/1/session", field: "/rest/api/2/field", project: "/rest/api/2/project", issue: "/rest/api/2/issue", createmeta: "/rest/api/2/issue/createmeta", components: "/rest/api/2/project", search: "/rest/api/2/search", user: "/rest/api/2/user", group: "/rest/api/2/group", }; const SCRIPTRUNNER_DASHBOARDS_N_FILTERS_URL = "rest/scriptrunner/latest/canned/com.onresolve.scriptrunner.canned.jira.admin.ChangeSharedEntityOwnership"; // Jira class here class Jira extends BSPlugin { // Properties here _server; _user; _password; _sessionId; _sessionRefreshPeriod; _timeout; _fieldDict; _sessionHeader; // Used if logged in _basicAuthHeader; // Used if not logged in constructor(name, jiraConfig) { super(name, // NOTE: 1.2.32 is replaced with package.json#version by a // rollup plugin at build time "1.2.32"); let config = { sessionRefreshPeriod: 60, ...jiraConfig, }; this._server = config.server; this._user = config.user; this._password = config.password; this._sessionId = null; this._fieldDict = null; this._sessionRefreshPeriod = config.sessionRefreshPeriod * 60 * 1000; // Convert to ms this._sessionHeader = {}; let token = Buffer.from(`${this._user}:${this._password}`).toString("base64"); this._basicAuthHeader = { Authorization: `Basic ${token}` }; } // Private methods here // Public methods here async login(auth) { let res = await bs.request(this._server, JiraResources.session, { method: "POST", body: { username: auth !== undefined ? auth.username : this._user, password: auth !== undefined ? auth.password : this._password, }, }); let session = res.body; this._sessionId = session.session.value; this._sessionHeader = { cookie: `JSESSIONID=${this._sessionId}` }; // Start a timer to automatically renew the session ID this._timeout = setTimeout(() => { this.info("Refreshing session ID!"); this.login(); }, this._sessionRefreshPeriod); } async logout() { if (this._sessionId === null) { return; } // Stop the timer first! clearInterval(this._timeout); await bs.request(this._server, JiraResources.session, { method: "DELETE", headers: { cookie: `JSESSIONID=${this._sessionId}`, }, }); // Reset the session ID so we know we are not logged in this._sessionId = null; this._sessionHeader = {}; } async getFieldDict(useCurrent = true) { // Check to see if the field dict is populated AND the user // wants to use the current field dict if (this._fieldDict !== null && useCurrent) { return this._fieldDict; } let res = await bs.request(this._server, JiraResources.field, { method: "GET", headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); this._fieldDict = { byId: {}, byName: {} }; if (Array.isArray(res.body)) { for (let field of res.body) { this._fieldDict.byName[field.name] = { id: field.id, type: field.schema !== undefined ? field.schema.type : "Unknown", itemType: field.schema !== undefined ? field.schema.items : "Unknown", }; this._fieldDict.byId[field.id] = { name: field.name, type: field.schema !== undefined ? field.schema.type : "Unknown", itemType: field.schema !== undefined ? field.schema.items : "Unknown", }; } } return this._fieldDict; } async getAllowedFieldValues(projectKey, issueType, fieldName) { let searchParams = { expand: "projects.issuetypes.fields", projectKeys: projectKey, issuetypeNames: issueType, }; let res = await bs.request(this._server, JiraResources.createmeta, { method: "GET", searchParams, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); // Convert field name to field ID let dict = await this.getFieldDict(); let fieldInfo = dict.byName[fieldName]; if (fieldInfo === undefined) { throw Error(`Unknown field ${fieldName}`); } let field = res.body.projects[0].issuetypes[0].fields[fieldInfo.id]; if (field === undefined || field.allowedValues === undefined) { return []; } let allowed = []; for (let info of field.allowedValues) { allowed.push(info.value); } return allowed; } async getComponents(projectKey) { let res = await bs.request(this._server, `${JiraResources.components}/${projectKey}/components`, { method: "GET", headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); let components = {}; for (let component of res.body) { components[component.name] = component.id; } return components; } async getProjects(component) { let res = await bs.request(this._server, JiraResources.project, { method: "GET", headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, searchParams: { expand: "lead" }, }); let projects = res.body; if (component !== undefined) { return projects.filter((el) => el.projectCategory.name === component); } return projects; } // TODO: add getProject async updateProject(project, body) { await bs.request(this._server, `${JiraResources.project}/${project}`, { method: "PUT", headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, body, }); } async updateProjectLead(project, lead) { await this.updateProject(project, { lead }); } async createIssue(projectKey, issueType, component, fields) { let components = await this.getComponents(projectKey); let issue = { fields: { project: { key: projectKey }, issuetype: { name: issueType }, components: [{ id: components[component] }], }, }; // Convert any field names to field IDs let dict = await this.getFieldDict(); for (let fname in fields) { let fid = dict.byName[fname]?.id; if (fid !== undefined) { issue.fields[fid] = fields[fname]; } else { issue.fields[fname] = fields[fname]; } } let res = await bs.request(this._server, JiraResources.issue, { method: "POST", body: issue, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); return res.body.key; } async updateIssue(key, fields, notifyUsers = true) { let issue = { fields: {}, }; // Convert any field names to field IDs let dict = await this.getFieldDict(); for (let fname in fields) { let fid = dict.byName[fname]?.id; if (fid !== undefined) { issue.fields[fid] = fields[fname]; } else { issue.fields[fname] = fields[fname]; } } let res = await bs.request(this._server, `${JiraResources.issue}/${key}`, { method: "PUT", body: issue, searchParams: notifyUsers ? undefined : { notifyUsers: "false" }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); return res.body.key; } async getIssue(idOrKey) { let res = await bs.request(this._server, `${JiraResources.issue}/${idOrKey}`, { method: "GET", headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); let issue = {}; // Convert any field IDs to field name let dict = await this.getFieldDict(); for (let fid in res.body.fields) { let fname = dict.byId[fid]?.name; if (fname !== undefined) { issue[fname] = res.body.fields[fid]; } else { issue[fid] = res.body.fields[fid]; } } // Add id to list of fields issue["id"] = res.body.id; return issue; } async issueReporter(key, reporter, notifyUsers = true) { await this.updateIssue(key, { reporter: { name: reporter } }, notifyUsers); } async assignIssue(key, assignee, notifyUsers = true) { await this.updateIssue(key, { assignee: { name: assignee, }, }, notifyUsers); } async updateLabels(key, action, labels, notifyUsers = true) { let issue = { update: { labels: [], }, }; issue.update.labels = []; for (let label of labels) { issue.update.labels.push({ [action]: label }); } let res = await bs.request(this._server, `${JiraResources.issue}/${key}`, { method: "PUT", body: issue, searchParams: notifyUsers ? undefined : { notifyUsers: "false" }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); return res.body.key; } async addComment(idOrKey, comment) { await bs.request(this._server, `${JiraResources.issue}/${idOrKey}/comment`, { method: "POST", body: { body: comment, }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); } async addWatcher(idOrKey, watcher) { await bs.request(this._server, `${JiraResources.issue}/${idOrKey}/watchers`, { method: "POST", body: watcher, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); } async removeWatcher(idOrKey, watcher) { await bs.request(this._server, `${JiraResources.issue}/${idOrKey}/watchers`, { method: "DELETE", body: watcher, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, searchParams: { username: watcher }, }); } async getTransitions(idOrKey) { let res = await bs.request(this._server, `${JiraResources.issue}/${idOrKey}/transitions`, { method: "GET", headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); let transitions = {}; for (let transition of res.body.transitions) { transitions[transition.name] = transition.id; } return transitions; } async doTransition(idOrKey, transitionIdOrName, fields, comment) { // transition may be the Transition ID or name so check let availableTransitions = await this.getTransitions(idOrKey); let transitionId = availableTransitions[transitionIdOrName]; if (transitionId === undefined) { transitionId = transitionIdOrName; } let dfields = {}; let dict = await this.getFieldDict(); if (fields !== undefined) { // Convert any field names to field IDs await this.getFieldDict(); for (let fname in fields) { let fid = dict.byName[fname]?.id; if (fid !== undefined) { dfields[fid] = { name: fields[fname] }; } else { dfields[fname] = { name: fields[fname] }; } } } let dcomment = { comment: [{ add: { body: comment } }] }; let body = { update: comment === undefined ? undefined : dcomment, fields: fields === undefined || fields.length === 0 ? undefined : dfields, transition: { id: transitionId }, }; await bs.request(this._server, `${JiraResources.issue}/${idOrKey}/transitions`, { method: "POST", body, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); } async runJql(jql) { let issues = []; let startAt = 0; let maxResults = 1000; // 1000 is the max you can get while (true) { let res = await bs .request(this._server, JiraResources.search, { method: "GET", headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, searchParams: { jql, startAt: startAt.toString(), maxResults: maxResults.toString(), fields: "key", }, }) .catch((e) => { this.error(e); }); if (res === undefined) { break; } // Append the results to what we already have let results = res.body; for (let issue of results.issues) { issues.push(issue.key); } // Increment by maxResults startAt += maxResults; // If we are beyond the total then we have everything so break, // otherwise go again if (startAt > results.total) { break; } } return issues; } async getUserDashboardIds(userId) { let res = await bs.request(this._server, `/${SCRIPTRUNNER_DASHBOARDS_N_FILTERS_URL}/params`, { method: "POST", body: { FIELD_FROM_USER_ID: userId, }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); let dashboardIds = []; let data = res.body; for (let obj of data) { if (obj.name === "FIELD_DASHBOARD_IDS") { for (let value of obj.values) { dashboardIds.push(value[0]); } } } return dashboardIds; } async getUserFilterIds(userId) { let res = await bs.request(this._server, `/${SCRIPTRUNNER_DASHBOARDS_N_FILTERS_URL}/params`, { method: "POST", body: { FIELD_FROM_USER_ID: userId, }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); let filterIds = []; let data = res.body; for (let obj of data) { if (obj.name === "FIELD_FILTER_IDS") { for (let value of obj.values) { filterIds.push(value[0].toString()); } } } return filterIds; } async migrateDashboards(fromUserId, toUserId, dashboardIds) { await bs.request(this._server, `/${SCRIPTRUNNER_DASHBOARDS_N_FILTERS_URL}`, { method: "POST", body: { FIELD_FROM_USER_ID: fromUserId, FIELD_TO_USER_ID: toUserId, FIELD_DASHBOARD_IDS: dashboardIds, FIELD_FILTER_IDS: [], }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); } async migrateFilters(fromUserId, toUserId, filterIds) { await bs.request(this._server, `/${SCRIPTRUNNER_DASHBOARDS_N_FILTERS_URL}`, { method: "POST", body: { FIELD_FROM_USER_ID: fromUserId, FIELD_TO_USER_ID: toUserId, FIELD_DASHBOARD_IDS: [], FIELD_FILTER_IDS: filterIds, }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); } async getUser(user, byKey, includeGroups = false) { let searchParams = {}; if (byKey) { searchParams["key"] = user; } else { searchParams["username"] = user; } if (includeGroups) { searchParams["expand"] = "groups"; } let res = await bs.request(this._server, JiraResources.user, { method: "GET", searchParams, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, }); return res.body; } async addUserToGroup(user, group) { let res = await bs.request(this._server, `${JiraResources.group}/user`, { method: "POST", searchParams: { groupname: group }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, body: { name: user }, }); return res.body; } async getUserGroups(user) { let details = await this.getUser(user, false, true); let groups = []; let groupItems = details?.groups?.items; if (groups !== undefined) { for (let group of groupItems) { groups.push(group.name); } } return groups; } async addUserToApplication(user, applicationKey) { await bs .request(this._server, `${JiraResources.user}/application`, { method: "POST", searchParams: { username: user, applicationKey: applicationKey }, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, body: {}, }) .catch((e) => { this.error("Received errors (%j)", e); }); } async restApiCall(method, path, body) { let res = await bs.request(this._server, path, { method, headers: this._sessionId === null ? this._basicAuthHeader : this._sessionHeader, body, }); return res; } } export { Jira, JiraResources }; //# sourceMappingURL=plugin.mjs.map