autoforce
Version:
Developer Automation tool for Github / Gitlab and Salesforce projects.
312 lines (311 loc) • 10.6 kB
JavaScript
import { logWarning } from "./color.js";
import { GitHubApi } from "./github-graphql.js";
export class GitHubProjectApi extends GitHubApi {
projectNumber;
constructor(token, owner, repo, projectNumber) {
super(token, owner, repo);
this.projectNumber = projectNumber;
}
async getColumnValueMap() {
const query = `
query getFieldOptions($owner:String!, $repo: String!, $projectNumber: Int!) {
repository(owner: $owner, name: $repo) {
projectV2(number: $projectNumber) {
field(name: "Status") {
... on ProjectV2SingleSelectField {
id
name
options {
name
id
}
}
}
}
}
}
`;
const { repository } = await this.graphqlAuth(query, { projectNumber: this.projectNumber, ...this.repoVar });
const mapValues = {};
for (const option of repository.projectV2.field.options) {
mapValues[option.name] = option.id;
}
return mapValues;
}
async findMilestoneByName(title) {
if (title) {
const milestones = await this.getMilestones();
if (milestones.length > 0) {
for (const milestone of milestones) {
if (milestone.title === title) {
return milestone;
}
}
}
}
return;
}
async findLabelByName(name) {
if (name) {
const labels = await this.getLabels();
if (labels.length > 0) {
for (const label of labels) {
if (label.name === name) {
return label;
}
}
}
}
return;
}
async createIssue(title, state, label, body, milestone) {
const user = await this.getUser();
const repository = await this.getRepository();
const repositoryId = repository.id;
const labelId = label?.startsWith('LA_') ? label : (await this.findLabelByName(label))?.id;
const milestoneId = milestone?.startsWith('MI_') ? milestone : (await this.findMilestoneByName(milestone))?.id;
const projectId = repository.projectV2.id;
const variables = { labelId, body, assignId: user.id, projectId, repositoryId, title, milestoneId: milestoneId ? milestoneId : null };
const mutationIssue = `
mutation createIssue($repositoryId: ID!, $assignId: ID!, $title: String!, $body: String, $milestoneId: ID ${labelId ? ', $labelId: ID!' : ''} ) {
createIssue(
input: {
repositoryId: $repositoryId,
assigneeIds: [$assignId],
title: $title,
milestoneId: $milestoneId,
body: $body
${labelId ? ',labelIds: [$labelId]' : ''}
}
) {
issue {
id
number
url
milestone {
title
dueOn
url
}
}
}
}`;
const { createIssue } = await this.graphqlAuth(mutationIssue, variables);
const issue = createIssue.issue;
if (!state || !issue.number) {
return issue;
}
const mutationItem = `
mutation addProjectV2ItemById($projectId: ID!, $contentId: ID! ) {
addProjectV2ItemById(
input: {
projectId: $projectId
contentId: $contentId
}
) {
clientMutationId,
item {
id
}
}
}`;
const { addProjectV2ItemById } = await this.graphqlAuth(mutationItem, { projectId, contentId: issue.id });
const itemId = addProjectV2ItemById.item.id;
const fieldId = repository.projectV2.field.id;
const mapValues = await this.getColumnValueMap();
const columnValue = mapValues[state];
if (!columnValue) {
logWarning(`No se encontro la columna ${state} en las lista de columnas del proyecto ${Object.keys(mapValues).join(",")}`);
}
else {
const mutationColumn = `
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $columnValue: String!) {
updateProjectV2ItemFieldValue(
input: {
projectId: $projectId,
itemId: $itemId,
fieldId: $fieldId,
value: {singleSelectOptionId: $columnValue}
}
) {
clientMutationId
}
}`;
await this.graphqlAuth(mutationColumn, { projectId, itemId, fieldId, columnValue });
}
return issue;
}
async getIssueState(issueNumber) {
const issue = await this._getIssue(issueNumber);
return issue.projectItems?.nodes[0]?.fieldValueByName?.name;
}
getIssueName(title) {
return title.toLowerCase().replaceAll(' ', '-');
}
async _getIssue(issueNumber) {
const query = `
query getIssue($owner:String!, $repo: String!, $issueNumber: Int!) {
repository(owner: $owner, name: $repo) {
issue(number: $issueNumber) {
title
number,
id
url
body
labels(first:3, orderBy: { field: CREATED_AT, direction: DESC}) {
nodes {
color
name
}
}
projectItems(last: 1) {
nodes{
id,
project {
id
}
fieldValueByName(name: "Status"){
... on ProjectV2ItemFieldSingleSelectValue {
name
id
field {
... on ProjectV2SingleSelectField {
id
}
}
}
}
}
}
linkedBranches(last:1){
nodes {
ref {
id
name
}
}
}
}
}
}
`;
const { repository } = await this.graphqlAuth(query, { issueNumber: Number.parseInt(issueNumber), ...this.repoVar });
return repository.issue;
}
async getIssue(issueNumber) {
const issue = await this._getIssue(issueNumber);
const issueObject = { number: issue.number, title: issue.title, id: issue.id, url: issue.url, body: issue.body };
issueObject.name = this.getIssueName(issue.title);
if (issue.linkedBranches.nodes.length > 0) {
issueObject.branch = issue.linkedBranches.nodes[0].ref.name;
}
if (issue.projectItems.nodes.length > 0) {
issueObject.state = issue.projectItems.nodes[0].fieldValueByName.name;
}
if (issue.labels.nodes.length > 0) {
issueObject.labels = [];
for (const node of issue.labels.nodes) {
issueObject.labels.push(node.name);
}
}
return issueObject;
}
async getIssues() {
return await this.getIssuesWithFilter(`{ states: OPEN }`);
}
async searchIssues(title) {
const query = `query getIssues() {
search(query: "repo:${this.repoVar.owner}/${this.repoVar.repo} in:title ${title}", type: ISSUE, first: 10) {
nodes {
... on Issue {
id
number
title
}
}
}
}`;
const { search } = await this.graphqlAuth(query, this.repoVar);
return search.nodes;
}
async getIssuesWithFilter(filterBy) {
const query = `
query getIssues($owner:String!, $repo: String!) {
repository(owner: $owner, name: $repo) {
issues(last: 10, filterBy: ${filterBy} ) {
nodes {
number
title
body
state
url
milestone {
dueOn
title
}
labels ( last: 3, orderBy: { field: CREATED_AT, direction: DESC} ) {
nodes {
color
name
}
}
assignees ( last: 3 ) {
nodes {
login
}
}
id
}
}
}
}
`;
const { repository } = await this.graphqlAuth(query, this.repoVar);
return repository.issues.nodes;
}
async moveIssue(issueNumber, state) {
const issue = await this._getIssue(issueNumber);
const itemId = issue.projectItems.nodes[0].id;
const projectId = issue.projectItems.nodes[0].project.id;
const fieldId = issue.projectItems.nodes[0].fieldValueByName.field.id;
const mapValues = await this.getColumnValueMap();
const columnValue = mapValues[state];
const mutation = `
mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $columnValue: String!) {
updateProjectV2ItemFieldValue(
input: {
projectId: $projectId,
itemId: $itemId,
fieldId: $fieldId,
value: {singleSelectOptionId: $columnValue}
}
) {
projectV2Item {
id
}
}
}`;
const { updateProjectV2ItemFieldValue } = await this.graphqlAuth(mutation, { projectId, itemId, fieldId, columnValue });
return updateProjectV2ItemFieldValue?.projectV2Item ? true : false;
}
async assignIssueToMe(issueNumber) {
const user = await this.getUser();
const issue = await this._getIssue(issueNumber);
const mutation = `
mutation assignUser( $issueId: ID!, $userId: ID!) {
addAssigneesToAssignable(input: {
assignableId: $issueId
assigneeIds: [ $userId ]
}) {
assignable {
assignees {
totalCount
}
}
}
}
`;
const { addAssigneesToAssignable } = await this.graphqlAuth(mutation, { issueId: issue.id, userId: user.id });
return addAssigneesToAssignable.assignable.assignees.totalCount > 0;
}
}