diff --git a/config/user_remap.json b/config/user_remap.json index bb7e31b..b88bf5c 100644 --- a/config/user_remap.json +++ b/config/user_remap.json @@ -1,9 +1,9 @@ { - "alan": "arocull@purdue.edu", - "Will": "montgow@purdue.edu", - "connor": "celswort@purdue.edu", - "shelby": "shockada@purdue.edu", - "tommy": "thochste@purdue.edu", - "matt": "mcschule@purdue.edu", - "patrick": "ryan227@purdue.edu" + "alan": "712020:fb70534b-d978-4df7-b54f-85f948202227", + "Will": "622aee8d49c900007023fccb", + "connor": "60d139e09469280070f87286", + "shelby": "712020:67143da0-4371-437d-a48f-e7400c89649c", + "tommy": "712020:63e73e0a-6680-4546-8034-4a4732b6b383", + "matt": "712020:02aa3220-cfc4-4027-9e67-52387f5fa227", + "patrick": "712020:a18c4048-38d2-48ac-a450-21defd4a005c" } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index f36c806..9dd2049 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,24 +5,63 @@ import _ from 'lodash' import express from 'express' import fetch from 'node-fetch' +import Issue from './issue'; + // Load configurations import CONFIG from '../config/server.json'; import SECRETS from '../config/secrets.json'; -// import uGiteaToJira from '../config/user_remap.json'; +import uGiteaToJira from '../config/user_remap.json'; +import { jiraFetch } from './requests' // BUILD OAUTH -// const OAUTH_JIRA = `${SECRETS['jira-email']}:${SECRETS['jira-token']}`; +const OAUTH_JIRA = `${SECRETS['jira-email']}:${SECRETS['jira-token']}`; const OAUTH_GITEA = `token=${SECRETS['gitea-token']}`; const URL_GITEA = `${CONFIG['gitea']}/api/v1`; + const url = `${URL_GITEA}/repos/${CONFIG['repositories']['bot']}/issues?state=all&${OAUTH_GITEA}` -console.log('Getting: ', url); +const database: Issue[] = []; + +// Copy issues over from Gitea to Jira fetch(url).then((res) => { res.json().then((data) => { - console.log('Got data '); - console.log(data); - console.log(data.labels); + _.forEach(data, (ticket) => { + const issue = Issue.fromGitea(ticket, CONFIG['repository-default']); + database.push(issue); + + console.log(issue); + + console.log(issue.toJira(uGiteaToJira, SECRETS['jira-project'])); + // Now, search Jira issues to make sure this isn't already duplicated + const jiraIssueSearch = `project = "AAA" and summary ~ "${issue.title_jira}"\nORDER BY created DESC` + + jiraFetch(`issue/picker?currentJQL=${jiraIssueSearch}`, 'GET').then((jiraSearch) => { + jiraSearch.json().then((searchJSON) => { + let issueFound: any = null; + + _.forEach(searchJSON['sections'], (searchSection) => { + const issueList: any[] = searchSection['issues'] + if (issueList.length > 0) { // If any issues were found + issueFound = issueList[0]; // Select the first one (probably matches best) + } + }) + + // If the issue does not exist... + if (issueFound === null) { + // ...then create one! + console.log(`Issue "${issue.title_jira}" not found on Jira, creating one...`); + + jiraFetch('issue', 'POST', issue.toJira(uGiteaToJira, SECRETS['jira-project'])).then((res) => { + res.json().then((createdIssue) => { + console.log(createdIssue); + issue.id_jira = createdIssue['id']; + }) + }) + } + }) + }) + }) }) }) diff --git a/src/issue.ts b/src/issue.ts index 0462527..980f68b 100644 --- a/src/issue.ts +++ b/src/issue.ts @@ -1,19 +1,104 @@ +import _ from "lodash"; class Issue { + public id_gitea: number = -1; + public id_jira: number = -1; + constructor( - public title: String, - public desc: String, - public labels: String[], - public assignees: String[], - public dueDate: Date, - public open: boolean, + public repo: string, + public title: string, + protected desc: string = '', + protected labels: string[] = [], + protected reporter: string = '', + protected assignees: string[] = [], + protected open: boolean = true, + protected dueDate: string = '', ) {} /** + * @summary Constructs an issue from a Gitea JSON struct * @param obj JSON Object */ - public static fromGitea(obj: any) { + public static fromGitea(obj: any, repo: string): Issue { + const open = obj['open'] === 'open'; + // Fetch labels + const labels: string[] = []; + + _.forEach(obj['labels'], (label: any) => { + labels.push(label['id']) + }); + + // Fetch assignees + const assignees: string[] = []; + _.forEach(obj['assignees'], (user: any) => { + assignees.push(user['login']) + }); + + // Get due date, if there is one + let due: string = ''; + if (obj['due_date'] !== null) { + due = obj['due_date']; + } + + const issue = new Issue( + repo, + obj['title'], + obj['body'], + labels, + obj['user']['login'], + assignees, + open, + due, + ) + issue.id_gitea = obj['id'] + + return issue; + } + + /** + * @summary Converts this to a Jira JSoN object + * https://developer.atlassian.com/server/jira/platform/jira-rest-api-examples/#creating-an-issue-examples + */ + public toJira(remap: any, parent: string): any { + const obj: any = { + "labels": this.labels, + "summary": this.title_jira, + "description": { + "content": [ + { + "content": [ + { + "text": `${this.desc}\n\nManaged by GiteaJiraBot`, + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1, + }, + "reporter": { + "id": remap[this.reporter] + }, + "parent": { + "key": parent + }, + } + + if (this.assignees.length > 0) { + obj['assignee'] = remap[this.assignees[0]]; + } + if (this.dueDate !== '') { + obj['duedate'] = this.dueDate.slice(0, this.dueDate.indexOf('T')); + } + + return {"fields": obj, "update": {}}; + } + + public get title_jira(): string { + return `${this.repo.toUpperCase()}: ${this.title}`; } } diff --git a/src/requests.ts b/src/requests.ts new file mode 100644 index 0000000..9433b49 --- /dev/null +++ b/src/requests.ts @@ -0,0 +1,24 @@ +import fetch from 'node-fetch' + +import SECRETS from '../config/secrets.json'; + +const OAUTH_JIRA = `${SECRETS['jira-email']}:${SECRETS['jira-token']}`; +const OAUTH_GITEA = `token=${SECRETS['gitea-token']}`; + +function jiraFetch(protocol: string, method: string = 'POST', body: any = null) { + const options: any = { + method: method, + headers: { + 'Authorization': `Basic ${Buffer.from(OAUTH_JIRA).toString('base64')}`, + 'Accept': 'application/json' + } + }; + + if (body !== null) { + options['body'] = JSON.stringify(body); + } + + return fetch(`https://${SECRETS['jira-site']}.atlassian.net/rest/api/3/${protocol}`, options); +} + +export { jiraFetch }; diff --git a/webpack/development.js b/webpack/development.js index 2d792d9..8c77732 100644 --- a/webpack/development.js +++ b/webpack/development.js @@ -68,6 +68,7 @@ const serverConfig = _.defaultsDeep(_.cloneDeep(commonConfig), { if (ps !== null) { console.log('Killing old process ...'); ps.kill(); + startServer(); // Start a new process } else { startServer(); }