Skip to content

Instantly share code, notes, and snippets.

@mikeananev
Last active July 20, 2022 13:25
Show Gist options
  • Select an option

  • Save mikeananev/fa6c1c7513bd66f9f47159cf5bf5cd86 to your computer and use it in GitHub Desktop.

Select an option

Save mikeananev/fa6c1c7513bd66f9f47159cf5bf5cd86 to your computer and use it in GitHub Desktop.
Update Gantt EDN files by data from Jira
(ns update-task-data-from-jira
"Update task data in Gantt EDN-files by Jira data"
(:require [babashka.curl :as curl]
[babashka.fs :as fs]
[cheshire.core :as json]
[clojure.string :as string]
[rewrite-clj.zip :as z])
(:import (java.net URLEncoder)))
(def login (-> (System/getenv) (get "LOGNAME")))
(def password (string/trim (slurp "/Users/mylogin/.credpwd")))
(def jql-url "https://jit.mycompany.ru/rest/api/2/search?jql=")
(def task-url "https://jit.mycompany.ru/rest/api/2/issue/")
(defn get-raw-issue
"Get raw Jira issue by key using REST API via curl"
([issue-key] (get-raw-issue issue-key task-url))
([issue-key task-url] (get-raw-issue issue-key task-url login password))
([issue-key task-url login password]
(try
(let [resp (curl/get (str task-url issue-key)
{:basic-auth [login password]
:headers {"Accept" "application/json"}})]
(when (= 200 (:status resp))
(-> resp
:body
(json/parse-string true))))
(catch Exception e
(println "Exception message" (.getMessage e) "issue:" issue-key)))))
(defn extract-fields
"Returns map of common attributes from raw Jira issue or nil if error"
[raw-issue]
(some->> raw-issue
((juxt
(fn [x] (-> x :key))
(fn [x] (-> x :fields :summary))
(fn [x] (-> x :fields :description))
(fn [x] (-> x :fields :status :name))
(fn [x] (-> x :fields :resolution :name))
(fn [x] (-> x :fields :labels))
(fn [x] (-> x :fields :created))
(fn [x] (-> x :fields :updated))
(fn [x] (-> x :fields :resolutiondate))
(fn [x] (-> x :fields :customfield_18602))
(fn [x] (-> x :fields :customfield_18603))
(fn [x] (-> x :fields :duedate))
(fn [x] (-> x :fields :assignee :name))))
(zipmap
[:key :summary :description :status :resolution :labels
:created-at :updated-at :resolved-at :planned-start-at
:planned-end-at :duedate :assignee])))
(defn jira-rest-search
"Make search request to Jira using REST API via curl
Params:
* jql-url - Jira endpoint for search. Example: https://jira.mydomain.ru/rest/api/2/search?jql=
* jql-query - JQL query request. Example: \"project=ABC AND creator=mylogin\""
[login password jql-url ^String jql-query]
(let [resp (curl/get (str jql-url (URLEncoder/encode jql-query "UTF-8"))
{:basic-auth [login password]
:headers {"Accept" "application/json"}})]
(when (= 200 (:status resp))
(-> resp
:body
(json/parse-string true)))))
(defn search
"Make a raw JQL with predefined params: login, password, jql-url"
[jql-query]
(jira-rest-search login password jql-url jql-query))
(defn task-search
"Make a query for tasks search"
[query-string]
(->>
query-string
search
:issues
(map extract-fields)))
(defn update-task-kv
"Update task key and value in a given zipper node by jira issue data.
Returns task node with modified data."
[task-node task-key new-task-value]
(let [key-loc (-> (z/down task-node) (z/find-value z/right task-key))
modified-task-node (if (z/sexpr key-loc)
(z/replace (z/next key-loc) new-task-value) ;; replace old value
(-> ;; or insert new pair - :key value
task-node
z/down
z/rightmost
(z/insert-right task-key)
z/right
z/insert-newline-left
(z/insert-right new-task-value)
))]
(z/up modified-task-node)))
(defn update-task-node
"Try to update task from jira.
Returns task node. Node is modified if success"
[task-node]
(let [value (z/sexpr task-node)
uri (:links-to value)
issue-key (some->
uri
(string/last-index-of "/")
inc
((partial subs uri)))]
(if (seq issue-key)
(if-let [jira-issue (extract-fields (get-raw-issue issue-key))]
(-> task-node
(update-task-kv :alias (-> jira-issue :key string/lower-case keyword))
(update-task-kv :task (-> jira-issue :summary)))
task-node)
task-node)))
(defn update-all-tasks
"Update all tasks in a given EDN file.
Returns nil."
[^String edn-filename]
(let [edn-string (slurp edn-filename)
root-zloc (z/of-string edn-string)
zloc (-> root-zloc (z/find-value z/next :project-content) z/next)
updated-content-string (loop [zloc zloc
next-node (z/down zloc)]
(if (z/end? next-node)
(z/root-string zloc)
(let [value (z/sexpr next-node)
result-node (cond
(and (map? value) (:links-to value)) (update-task-node next-node)
:else next-node)]
(recur result-node (z/right result-node)))))]
(spit edn-filename updated-content-string)
(println "Successfully updated file:" edn-filename)))
(defn update-tasks-in-path
"Find EDN files in a given path and update task attributes: name, alias from Jira"
[^String plan-path]
(if (fs/directory? plan-path)
(let [edn-files (mapv str (fs/glob plan-path "**.edn"))]
(run! update-all-tasks edn-files))
(do
(println "Updating file" plan-path)
(update-all-tasks plan-path))))
(println "Updating tasks data from Jira...")
(if (pos-int? (count *command-line-args*))
(if (fs/exists? (first *command-line-args*))
(update-tasks-in-path (first *command-line-args*))
(println "Path is not exist:" (first *command-line-args*)))
(println "Usage: <this-program> <path-to-edn-files>"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment