asciidoctor-jira 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/package.json +45 -0
- package/src/Jira.js +58 -0
- package/src/asciidoctor-jira.js +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Asciidoctor Jira Extension
|
|
2
|
+
|
|
3
|
+
[](https://github.com/uniqueck/asciidoctor-jira/actions/workflows/build-js.yml)
|
|
4
|
+
|
|
5
|
+
A set of macros for [Asciidoctor.js](https://github.com/asciidoctor/asciidoctor.js) to integrate Jira!
|
|
6
|
+
|
|
7
|
+
* [Install](#install)
|
|
8
|
+
+ [Antora Integration](#antora-integration)
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
### Antora Integration
|
|
13
|
+
|
|
14
|
+
If you are using [Antora](https://antora.org/), you can integrate Jira in your documentation site.
|
|
15
|
+
|
|
16
|
+
Install the extension in your playbook project:
|
|
17
|
+
|
|
18
|
+
$ npm i asciidoctor-jira
|
|
19
|
+
|
|
20
|
+
Register the extension in your playbook file:
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
asciidoc:
|
|
24
|
+
extensions:
|
|
25
|
+
- asciidoctor-jira
|
|
26
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "asciidoctor-jira",
|
|
3
|
+
"author": "Constantin Krüger (https://github.com/uniqueck)",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/doctoolchain/asciidoctor-jira/issues"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"asciidoctor",
|
|
14
|
+
"jira",
|
|
15
|
+
"javascript",
|
|
16
|
+
"extension"
|
|
17
|
+
],
|
|
18
|
+
"main": "src/asciidoctor-jira.js",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@antora/site-generator-default": "^2.3.3",
|
|
21
|
+
"@asciidoctor/core": "^2.2.0",
|
|
22
|
+
"chai": "^4.3.4",
|
|
23
|
+
"cheerio": "0.22.0",
|
|
24
|
+
"dotenv": "^10.0.0",
|
|
25
|
+
"jest": "27.2",
|
|
26
|
+
"mocha": "^9.0.0",
|
|
27
|
+
"rimraf": "^3.0.2",
|
|
28
|
+
"shx": "^0.3.3",
|
|
29
|
+
"standard": "^16.0.2",
|
|
30
|
+
"sync-request": "^6.1.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@asciidoctor/core": "^2.2.0"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"test": "npm run test:antora",
|
|
37
|
+
"test:antora": "mocha --reporter json --reporter-option output=test-results.json test/antora/**.spec.js",
|
|
38
|
+
"lint": "standard src/**.js",
|
|
39
|
+
"lint-fix": "npm run lint -- --fix",
|
|
40
|
+
"clean": "shx rm -rf dist/*"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=10"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/Jira.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
const request = require('sync-request')
|
|
3
|
+
|
|
4
|
+
class Jira {
|
|
5
|
+
constructor (doc) {
|
|
6
|
+
this.doc = doc
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
searchIssues (jql) {
|
|
10
|
+
const data = { jql: jql, fields: 'created,resolutiondate,priority,summary,timeoriginalestimate,assignee,issuetype' }
|
|
11
|
+
let issues
|
|
12
|
+
try {
|
|
13
|
+
const jiraBaseUrl = this.doc.getAttribute('jira-host') || process.env.AJE_JIRABASEURL
|
|
14
|
+
const jiraRestApiSearchEndpoint = jiraBaseUrl + '/rest/api/2/search'
|
|
15
|
+
const jiraUserName = this.doc.getAttribute('jira-username') || process.env.AJE_USERNAME
|
|
16
|
+
const jiraApiToken = this.doc.getAttribute('jira-apitoken') || process.env.AJE_APITOKEN || process.env.AJE_PASSWORD
|
|
17
|
+
const auth = 'Basic ' + Buffer.from(jiraUserName + ':' + jiraApiToken).toString('base64')
|
|
18
|
+
const headers = {
|
|
19
|
+
authorization: auth
|
|
20
|
+
}
|
|
21
|
+
const res = request('GET', jiraRestApiSearchEndpoint, {
|
|
22
|
+
headers: headers,
|
|
23
|
+
qs: data
|
|
24
|
+
})
|
|
25
|
+
issues = JSON.parse(res.getBody('utf-8')).issues
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.log(err)
|
|
28
|
+
issues = null
|
|
29
|
+
}
|
|
30
|
+
return issues
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
searchIssue (issueKey) {
|
|
34
|
+
const data = { jql: 'issueKey=' + issueKey, fields: 'created,resolutiondate,priority,summary,timeoriginalestimate,assignee,issuetype' }
|
|
35
|
+
let result
|
|
36
|
+
try {
|
|
37
|
+
const jiraBaseUrl = this.doc.getAttribute('jira-host') || process.env.AJE_JIRABASEURL
|
|
38
|
+
const jiraRestApiSearchEndpoint = jiraBaseUrl + '/rest/api/2/search'
|
|
39
|
+
const jiraUserName = this.doc.getAttribute('jira-username') || process.env.AJE_USERNAME
|
|
40
|
+
const jiraApiToken = this.doc.getAttribute('jira-apitoken') || process.env.AJE_APITOKEN || process.env.AJE_PASSWORD
|
|
41
|
+
const auth = 'Basic ' + Buffer.from(jiraUserName + ':' + jiraApiToken).toString('base64')
|
|
42
|
+
const headers = {
|
|
43
|
+
authorization: auth
|
|
44
|
+
}
|
|
45
|
+
const res = request('GET', jiraRestApiSearchEndpoint, {
|
|
46
|
+
headers: headers,
|
|
47
|
+
qs: data
|
|
48
|
+
})
|
|
49
|
+
result = JSON.parse(res.getBody('utf-8')).issues[0]
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.log(err)
|
|
52
|
+
result = null
|
|
53
|
+
}
|
|
54
|
+
return result
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = Jira
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* global
|
|
2
|
+
Opal
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// @ts-check
|
|
6
|
+
const Jira = require('./Jira.js')
|
|
7
|
+
require('dotenv').config()
|
|
8
|
+
|
|
9
|
+
function jiraIssuesBlockMacro (context) {
|
|
10
|
+
return function () {
|
|
11
|
+
const self = this
|
|
12
|
+
self.named('jira')
|
|
13
|
+
self.positionalAttributes(['jql'])
|
|
14
|
+
self.process((parent, target, attrs) => {
|
|
15
|
+
const doc = parent.getDocument()
|
|
16
|
+
const jql = attrs.jql || "resolution='Unresolved' ORDER BY priority DESC, key ASC, duedate ASC"
|
|
17
|
+
const jiraClient = new Jira(doc)
|
|
18
|
+
const issues = jiraClient.searchIssues(jql)
|
|
19
|
+
|
|
20
|
+
const content = []
|
|
21
|
+
content.push('[options="header",cols="2,1,1,2,6"]')
|
|
22
|
+
content.push('|====')
|
|
23
|
+
content.push('|ID | Priority | Created | Assignee | Summary')
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < issues.length; i++) {
|
|
26
|
+
const issue = issues[i]
|
|
27
|
+
content.push('a|jira:' + issue.key + '[]')
|
|
28
|
+
content.push('|' + issue.fields.priority.name)
|
|
29
|
+
content.push('|' + issue.fields.created)
|
|
30
|
+
const assignee = issue.fields.assignee ? issue.fields.assignee.displayName : 'not assigned'
|
|
31
|
+
content.push('|' + assignee)
|
|
32
|
+
content.push('|' + issue.fields.summary)
|
|
33
|
+
}
|
|
34
|
+
content.push('|====')
|
|
35
|
+
|
|
36
|
+
self.parseContent(parent, content.join('\n'), Opal.hash(attrs))
|
|
37
|
+
|
|
38
|
+
return undefined
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function jiraIssueInlineMacro (context) {
|
|
44
|
+
return function () {
|
|
45
|
+
const self = this
|
|
46
|
+
self.named('jira')
|
|
47
|
+
self.positionalAttributes(['format'])
|
|
48
|
+
self.process((parent, target, attrs) => {
|
|
49
|
+
const doc = parent.getDocument()
|
|
50
|
+
const displayFormat = attrs.format || doc.getAttribute('jira-inline-format') || 'short'
|
|
51
|
+
const issueKey = target
|
|
52
|
+
const jiraClient = new Jira(doc)
|
|
53
|
+
const issue = jiraClient.searchIssue(issueKey)
|
|
54
|
+
let title = issueKey
|
|
55
|
+
if (displayFormat === 'long') {
|
|
56
|
+
title += issue.fields.summary
|
|
57
|
+
}
|
|
58
|
+
const jiraBaseUrl = doc.getAttribute('jira-host') || process.env.AJE_JIRABASEURL
|
|
59
|
+
const issueLink = jiraBaseUrl + '/browse/' + issueKey
|
|
60
|
+
return self.createInline(parent, 'anchor', title, { type: 'link', target: issueLink }).convert()
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports.register = function register (registry, context = {}) {
|
|
66
|
+
if (typeof registry.register === 'function') {
|
|
67
|
+
registry.register(function () {
|
|
68
|
+
this.blockMacro(jiraIssuesBlockMacro(context))
|
|
69
|
+
this.inlineMacro(jiraIssueInlineMacro(context))
|
|
70
|
+
})
|
|
71
|
+
} else if (typeof registry.block === 'function') {
|
|
72
|
+
registry.blockMacro(jiraIssuesBlockMacro(context))
|
|
73
|
+
registry.inlineMacro(jiraIssueInlineMacro(context))
|
|
74
|
+
}
|
|
75
|
+
return registry
|
|
76
|
+
}
|