[!NOTE|label:reference:]
jira
$ jiraName='jira.sample.com'
$ jiraID='STORY-1'
[!NOTE]
- Special headers
X-Atlassian-Token
X-Force-Accept-Language
X-AAccountId
myself
[!NOTE|label:references:]
$ curl -s https://jira.sample.com/rest/api/2/myself
# or via `user?username=<id>`
$ curl -s https://jira.sample.com/rest/api/2/user?username=marslo
get info
# get timezone $ curl -s https://jira.sample.com/rest/api/2/mypreferences?key=jira.user.timezone Asia/Shanghai # get locale $ curl --request GET \ --url 'https://jira.sample.com/rest/api/2/mypreferences/locale' \ --header 'Accept: application/json'
set info
# set timezone $ curl --request PUT \ --url 'https://jira.sample.com/rest/api/2/mypreferences?key={key}' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --data '"<string>"' # set local $ curl --request PUT \ --url 'https://jira.sample.com/rest/api/2/mypreferences/locale' \ --user 'email@example.com:<api_token>' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --data '{ "locale": "en_US" }'
check fields
$ curl -s \
-k \
-X GET https://${jiraName}/rest/api/2/issue/${jiraID} |
jq --raw-output
check attachment
check attachment ID
$ curl -s \ -k \ -X GET https://${jiraName}/rest/api/2/issue/${jiraID}?fields=attachment | jq --raw-output .fields.attachment[].id
get attachments download url
$ curl -s \ -k \ -X GET https://${jiraName}/rest/api/2/issue/${jiraID}?fields=attachment | jq --raw-output .fields.attachment[].content
download all attachments in Jira
-I replace-str
Replace occurrences of replace-str in the initial-arguments with names read from standard in- put. Also, unquoted blanks do not terminate input items; instead the separator is the new- line character. Implies -x and -L 1.
$ curl -s \ -k \ -X GET https://${jiraName}/rest/api/2/issue/${jiraID}?fields=attachment | jq --raw-output .fields.attachment[].content | xargs -I '{}' curl -sgOJL '{}'
list all projects
$ curl -fsSL -XGET https://jira.sample.com/rest/api/2/project |
jq -r '.[] | [.key, .name] | join(" | ")' |
column -s '|' -t
search issue by JQL
[!TIP]
- Search for issues using JQL (GET)
- JQL: What is advanced search in Jira Cloud?
- How to get all issues by project key and issue type using REST api
- JQL pattern rule:
- remove all space
%3D
instead of=
project=abc
->project%3Dabc
%20CONDITION%20
instead ofCONDITION
AND
->%20AND%20
OR
->%20OR%20
- * JQL operators
- REST API - 'order by' param is ignored
- What is advanced search in Jira Cloud?
format JQL
[!TIP]
$ jql='project = abc AND issuetype = release order by updated desc' $ jql=$(printf %s "${jql}" | jq -sRr @uri)
$ jql="$(sed 's/ //g;s/AND/ AND /g;s/OR/ OR /g;s/IN/ IN /g;s/IS/ IS /g' <<< "${jql}")" $ jql="$(printf %s "${jql}" | jq -sRr @uri)" # i.e.: $ jql='project = abc AND issuetype = release' $ jql="$(sed 's/ //g;s/AND/ AND /g;s/OR/ OR /g;s/IN/ IN /g;s/IS/ IS /g' <<< "${jql}")" $ echo $jql project=abc AND issuetype=release $ jql="$(printf %s "${jql}" | jq -sRr @uri)" $ echo $jql project%3Dabc%20AND%20issuetype%3Drelease
api
[!NOTE]
- * iMarslo : bin/jira
- query parameters:
maxResults
:integer
startAt
:integer
validateQuery
:string
fields
:array<string>
expand
:string
properties
:array<string>
fieldsByKeys
:boolean
- sample:
search?jql=${jql}&maxResults=100&startAt=0
$ curl --silent \ --insecure \ --globoff \ --netrc-file ~/.netrc \ -XGET \ "https://jira.sample.com/rest/api/2/search?jql=${jql}" | jq -r ${jqOpt} # i.e.: $ curlOpt='--silent --insecure --globoff --netrc-file ~/.netrc' $ url='https://jira.sample.com/rest/api/2' $ queryParams="startAt=0&maxResults=10" $ jql='project = ABC AND issuetype = Release ORDER BY updated ASC' # copy from Jira website $ jql="$(printf %s "${jql}" | jq -sRr @uri)" $ curl "${curlOpt}" "${url}/search?jql=${jql}&${queryParams}" | jq -r '.issues[]' | jq -r '. | [.key, .fields.summary, .fields.status.name, .fields.issuetype.name, .fields.updated, .fields.created] | join("|")' | while IFS='|' read -r _key _summary _status _issuetype _updated _created; do echo "- [${_key}] - ${_summary}" echo " - status : ${_status}" echo " - issuetype : ${_issuetype}" echo " - created : ${_created}" echo " - updated : ${_updated}" done
$ curl --silent \ --insecure \ --globoff \ --netrc-file ~/.netrc \ -XGET \ "https://jira.sample.com/rest/api/2/search?jql=${jql}" | jq -r ${jqOpt} # i.e.: $ curlOpt='--silent --insecure --globoff --netrc-file ~/.netrc' $ url='https://jira.sample.com/rest/api/2' $ queryParams="startAt=0&maxResults=10" $ jql='project = abc AND issuetype = release' # copy from Jira website $ jql="$(sed 's/ //g;s/AND/ AND /g;s/OR/ OR /g;s/IN/ IN /g;s/IS/ IS /g' <<< "${jql}")" $ jql="$(printf %s "${jql}" | jq -sRr @uri)" $ curl "${curlOpt}" "${url}/search?jql=${jql}&${queryParams}" | jq -r '.issues[]' | jq -r '. | [.key, .fields.summary, .fields.status.name, .fields.issuetype.name, .fields.updated, .fields.created] | join("|")' | while IFS='|' read -r _key _summary _status _issuetype _updated _created; do echo "- [${_key}] - ${_summary}" echo " - status : ${_status}" echo " - issuetype : ${_issuetype}" echo " - created : ${_created}" echo " - updated : ${_updated}" done
get email address
$ curl GET https://jira.sample.com/rest/api/2/user?key=JIRAUSER10100 |
jq -r
api token
[!NOTE|label:references:]
-
[!NOTE]
- to list all tokens for single user:
$ curl -v -XGET \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token/ | jq -r
- to list tokens for all users:
$ curl -v \ -X GET \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/tokensByFilter | jq -r
$ curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r
get id, created, last access, and valid timestamp
function epoch2timestamp() { [[ 0 != $1 ]] && echo $(date -d @$(( $1/1000 )) +%FT%T.%3N%Z) || echo "0"; } $ while read -r _id _created _lastAccess _validUntil; do echo "${_id} $(epoch2timestamp ${_created}) $(epoch2timestamp ${_lastAccess}) $(epoch2timestamp ${_validUntil})"; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .id, .created, .lastAccessed, .validUntil ] | join( "\t" )' ) | column -t 537 2024-01-31T21:32:51.000PST 2024-03-26T23:30:30.000PDT 2024-07-31T21:32:51.000PDT 579 2024-03-26T23:19:16.000PDT 0 2024-09-26T23:19:16.000PDT # with header $ ( echo "ID CREATED LASTACCESS VALIDUNTIL"; while read -r _id _created _lastAccess _validUntil; do echo "${_id} $(epoch2timestamp ${_created}) $(epoch2timestamp ${_lastAccess}) $(epoch2timestamp ${_validUntil})"; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .id, .created, .lastAccessed, .validUntil ] | join( "\t" )' ) ) | column -t ID CREATED LASTACCESS VALIDUNTIL 537 2024-01-31T21:32:51.000PST 2024-03-26T23:33:33.000PDT 2024-07-31T21:32:51.000PDT 579 2024-03-26T23:19:16.000PDT 0 2024-09-26T23:19:16.000PDT
or
$ curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .created, .lastAccessed, .validUntil ] | join("\n")' | xargs -r -I{} bash -c "et=\"{}\"; date -d @\$(( \${et}/1000 )) +%c" Mon 18 Dec 2023 10:25:58 AM PST Tue 26 Mar 2024 10:21:30 PM PDT Tue 18 Jun 2024 10:25:58 AM PDT # or $ while read -r _d; do date -d @$(( ${_d}/1000 )) +%c; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .created, .lastAccessed, .validUntil ] | join("\n")' ) Mon 18 Dec 2023 10:25:58 AM PST Tue 26 Mar 2024 10:14:58 PM PDT Tue 18 Jun 2024 10:25:58 AM PDT
- to list all tokens for single user:
-
[!TIP]
- expiration keywords
tokenValidityTimeInMonths
tokenExpirationDateTime
tokenExpirationDateTimeMillis
$ curl -v -d '{"tokenDescription":"<token-description>"}' \ -X POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token
create new token with expiration time
[!NOTE|label:references:]
- with
tokenValidityTimeInMonths
$ curl -v \ -d '{"tokenDescription":"<token-description>", "tokenValidityTimeInMonths" : 1}' \ -X POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token
- with
tokenExpirationDateTime
$ curl -v \ -d '{"tokenDescription":"Custom expiration", "tokenExpirationDateTime" : "2020-10-19T10:29:00.000+02:00"}' \ -X POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token
$ curl -s \ -d '{"tokenDescription":"marslo-token-api-test", "tokenValidityTimeInMonths" : 6}' \ -X POST \ --header "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | jq -r .plainTextToken NdxEToKfDsjUE7tct1ePP6erE1xdDsEAa64BOT
- with
create token for another users
$ curl -v \ -d '{"tokenDescription":"token for another user", "tokenForUserKey":"JIRAUSER10105"}' \ POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token # or with token validity time $ curl -v \ -d '{"tokenDescription":"token for another user", "tokenForUserKey":"JIRAUSER10105","tokenValidityTimeInMonths":12}' \ POST \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token
- expiration keywords
-
$ curl -v \ -d '{"tokenDescription":"Updated token description"}' \ -X PATCH \ --header "Content-Type: application/json" \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token/<token-id>
-
[!NOTE|label:references:]
$ curl -v \ -X DELETE \ https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/token/<token-id>
# list before token deleted $ ( echo "ID CREATED LASTACCESS VALIDUNTIL"; while read -r _id _created _lastAccess _validUntil; do echo "${_id} $(epoch2timestamp ${_created}) $(epoch2timestamp ${_lastAccess}) $(epoch2timestamp ${_validUntil})"; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .id, .created, .lastAccessed, .validUntil ] | join( "\t" )' ) ) | column -t ID CREATED LASTACCESS VALIDUNTIL 537 2024-01-31T21:32:51.000PST 2024-03-26T23:40:02.000PDT 2024-07-31T21:32:51.000PDT 579 2024-03-26T23:19:16.000PDT 0 2024-09-26T23:19:16.000PDT 580 2024-03-26T23:36:02.000PDT 0 2024-09-26T23:36:02.000PDT 581 2024-03-26T23:36:11.000PDT 0 2024-09-26T23:36:11.000PDT # delete $ curl -s -D- \ -X DELETE \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token/581 HTTP/2 200 date: Wed, 27 Mar 2024 06:41:06 GMT content-type: application/json;charset=UTF-8 x-arequestid: 1421x248002x2 x-anodeid: jiraprod5 referrer-policy: strict-origin-when-cross-origin x-xss-protection: 1; mode=block x-content-type-options: nosniff x-frame-options: SAMEORIGIN content-security-policy: sandbox strict-transport-security: max-age=31536000 set-cookie: JSESSIONID=731380C1F17B2F9A59E211B0442AAFFB; Path=/; Secure; HttpOnly x-seraph-loginreason: OK set-cookie: atlassian.xsrf.token=A8KN-1NAU-M55V-EQSR_1811ae8601112c05430589e686032924599718c9_lin; Path=/; Secure; SameSite=None x-asessionid: k5q153 x-ausername: marslo cache-control: no-cache, no-store, no-transform true # curl without header $ curl -s \ -X DELETE \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token/580 true # verify $ ( echo "ID CREATED LASTACCESS VALIDUNTIL"; while read -r _id _created _lastAccess _validUntil; do echo "${_id} $(epoch2timestamp ${_created}) $(epoch2timestamp ${_lastAccess}) $(epoch2timestamp ${_validUntil})"; done < <( curl -s -D- \ -XGET \ -H "Content-Type: application/json" \ https://jira.sample.com/rest/de.resolution.apitokenauth/latest/user/token | sed '/^\s*$/,$!d;//d' | jq -r '.content[] | [ .id, .created, .lastAccessed, .validUntil ] | join( "\t" )' ) ) | column -t ID CREATED LASTACCESS VALIDUNTIL 537 2024-01-31T21:32:51.000PST 2024-03-26T23:44:32.000PDT 2024-07-31T21:32:51.000PDT 579 2024-03-26T23:19:16.000PDT 0 2024-09-26T23:19:16.000PDT
- delete all tokens for a user
$ curl GET \ https://jira.sample.com/rest/api/2/user?username=some.username | jq -r
- delete all tokens for a user
PARAMETER | VALUE | COMMENT |
---|---|---|
userFilter |
valid user key | read above how to get a user key for a name or by email address; if you want to filter for more than one user, repeat that parameter for as many users you want to filter for |
descriptionFilter |
search term | string to search for in all token descriptions |
notValidAfter |
epoch Unix timestamp | tokens not valid anymore after that date/ time in milliseconds |
tokenScope |
integer | 0 = no scope (all pre 1.5.0 tokens), 1 = read-only, 2 = read/ write |
fromCreated |
epoch Unix timestamp | - |
untilCreated |
epoch Unix timestamp | - |
fromLastUsed |
epoch Unix timestamp | - |
untilLastUsed |
epoch Unix timestamp | - |
fromExpiresDuring |
epoch Unix timestamp | - |
untilExpiresDuring |
epoch Unix timestamp | - |
list token expired after certain time
$ curl "https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/tokensByFilter?notValidAfter=1688918956972" | jq -r # with limit and page $ curl "https://jira-or-confluence.sample.com/rest/de.resolution.apitokenauth/latest/user/tokensByFilter?page=0&limit=1¬ValidAfter=1688918956972" | jq -r
generate OAuth consumer
[!NOTE|label:references:]
$ openssl genrsa -out jira_privatekey.pem 1024
$ openssl req -newkey rsa:1024 -x509 -key jira_privatekey.pem -out jira_publickey.cer -days 365
$ openssl pkcs8 -topk8 -nocrypt -in jira_privatekey.pem -out jira_privatekey.pcks8
$ openssl x509 -pubkey -noout -in jira_publickey.cer > jira_publickey.pem
icons and priority
priority
[!NOTE|label:references:]
$ for _i in "blocker.png" "blocker.svg" "critical.png" "critical.svg" "high.png" "high.svg" "highest.png" "highest.svg" "low.png" "low.svg" "lowest.png" "lowest.svg" "major.png" "major.svg" "medium.png" "medium.svg" "minor.png" "minor.svg" "trivial.png" "trivial.svg"; do
echo "--> ${_i}"
curl -O https://jira-trigger-plugin.atlassian.net/images/icons/priorities/${_i}
done
confluence
$ confluenceName='confluence.domain.com'
$ pageID='143765713'
get page id:
myself
$ curl -s https://${confluenceName}/rest/api/user/current | jq -r
get info
$ curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq --raw-output
get space
$ curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq .space.key
get title
$ curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq .title
get page history
$ curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq .version.number
- get next version
currentVer=$(curl -s -X GET https://${confluenceName}/rest/api/content/${pageID} | jq .version.number) newVer=$((currentVer+1))
- get next version
publish to confluence
[!NOTE|label:references:]
$ url="https://${confluenceName}/rest/api/content/${pageID}"
$ page=$(curl -s ${url})
$ space=$(echo "${page}" | jq .space.key)
$ title=$(echo "${page}" | jq .title)
$ currentVer=$(echo "${page}" | jq .version.number)
$ newVer=$((currentVer+1))
$ cat > a.json << EOF
{
"id": "${pageID}",
"type": "page",
"title": ${title},
"space": {"key": ${space}},
"body": {
"storage": {
"value": "<h1>Hi confluence</h1>",
"representation": "storage"
}
},
"version": {"number":${newVer}}
}
EOF
$ curl -s \
-i \
-X PUT \
-H 'Content-Type: application/json' \
--data "$(cat a.json)" \
https://${confluenceName}/rest/api/content/${pageID}
- result
plugins
Multiexcerpt
create excerpt
include excerpt
result
insert svg
HTML Macro
insert svg image
[!TIP|label:get svg image:]
...
->Attachments
- preview the image and get info from URL :
...&preview=/506105287/513281481/k8s-ha-cluster-stacked-etcd.v3.svg
<img width="800" src="https://ewiki.sample.com/download/attachments/506105287/513281481/k8s-ha-cluster-stacked-etcd.v3.svg">
edit source via
[!TIP|label:references:]
<p><ac:image ac:width="301"><ri:attachment ri:filename="SVG_logo.svg" /></ac:image></p>
CLI
[!NOTE|label:references:]
running with docker images
$ $ docker run -ti bobswiftapps/acli:latest acli -a getClientInfo
# or
$ docker run -ti bobswiftapps/acli:latest /bin/bash
bash-4.4# acli -a getClientInfo
# with env
$ cat .env
examplegear=jira -s https://examplegear.atlassian.net -u anonymous
examplegear_confluence=confluence -s https://examplegear.atlassian.net/wiki -u anonymous
$ docker run --env-file=.env -ti bobswiftapps/acli:latest /bin/bash
bash-5.0# acli $examplegear -a getServerInfo
# or
docker run -e examplegear='jira -s https://examplegear.atlassian.net -u anonymous' -ti bobswiftapps/acli:latest /bin/bash
bash-4.4# acli ${examplegear} -a getServerInfo
# with acli.properties
$ docker run -v ${PWD}/acli.properties:/opt/acli/acli.properties \
-ti bobswiftapps/acli:latest
# or prior to version 11.0.0, use:
$ docker run -v ${PWD}/acli.properties:/opt/atlassian-cli/acli.properties \
-ti bobswiftapps/acli:latest
# or with `ACLI_CONFIG` env
$ docker run -v ${PWD}/acli.properties:/tmp/acli.properties \
-e ACLI_CONFIG=/tmp/acli.properties \
-ti bobswiftapps/acli:latest \
acli -a getServerInfo --outputFormat 2
create .acli.keystore
$ acli system setSecureProperty --name my.secret --secret -
Enter secure value: <secret value prompt>
Secure properties file does not yet exist. Creating...
Enter new secure properties password: <new password prompt>
Confirm secure properties password: <new password prompt>
Secure properties file created.
Value for key 'foo' set in secure properties file.