git.fiddlerwoaroof.com
Browse code

Reorganize code

fiddlerwoaroof authored on 09/05/2016 02:35:58
Showing 6 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+jira-client
0 2
new file mode 100644
... ...
@@ -0,0 +1,26 @@
1
+(in-package #:jira-api) 
2
+
3
+; curl -u user:password https://atomampd.atlassian.net/rest/api/2/issue/ATOMOS-212 | jq .
4
+(defparameter *endpoint* "https://atomampd.atlassian.net/rest/api/2/")
5
+
6
+(defun api-get-call (auth method &rest parameters)
7
+  "Connect to a GET REST endpoint specified by method and return a stream from
8
+   which the response can be read."
9
+  (let ((drakma:*text-content-types* (acons "application" "json" drakma:*text-content-types*)))
10
+    (drakma:http-request (puri:merge-uris method *endpoint*)
11
+                         :parameters (alexandria:plist-alist parameters)
12
+                         :basic-authorization auth
13
+                         :want-stream t)))
14
+
15
+(defun api-post-call (auth method post-data)
16
+  "Connect to a GET REST endpoint specified by method and return a stream from
17
+   which the response can be read."
18
+  (let ((drakma:*text-content-types* (acons "application" "json" drakma:*text-content-types*)))
19
+    (drakma:http-request (puri:merge-uris method *endpoint*)
20
+                         :method :POST
21
+                         :content-type "application/json"
22
+                         :content post-data
23
+                         :basic-authorization auth
24
+                         :want-stream t)))
25
+
26
+
... ...
@@ -14,5 +14,7 @@
14 14
   :serial t
15 15
   :components ((:file "package")
16 16
                (:file "prompt")
17
+               (:file "api-handler")
18
+               (:file "sheeple-protos")
17 19
                (:file "jira-api")))
18 20
 
... ...
@@ -1,32 +1,8 @@
1 1
 ;;;; jira-api.lisp
2
+(declaim (optimize (debug 3)))
2 3
 
3 4
 (in-package #:jira-api)
4 5
 
5
-;;; "jira-api" goes here. Hacks and glory await!
6
-
7
-; curl -u user:password https://atomampd.atlassian.net/rest/api/2/issue/ATOMOS-212 | jq .
8
-(defparameter *endpoint* "https://socraticum.atlassian.net/rest/api/2/")
9
-
10
-(defun api-get-call (auth method &rest parameters)
11
-  "Connect to a GET REST endpoint specified by method and return a stream from
12
-   which the response can be read."
13
-  (let ((drakma:*text-content-types* (acons "application" "json" drakma:*text-content-types*)))
14
-    (drakma:http-request (puri:merge-uris method *endpoint*)
15
-                         :parameters (alexandria:plist-alist parameters)
16
-                         :basic-authorization auth
17
-                         :want-stream t)))
18
-
19
-(defun api-post-call (auth method post-data)
20
-  "Connect to a GET REST endpoint specified by method and return a stream from
21
-   which the response can be read."
22
-  (let ((drakma:*text-content-types* (acons "application" "json" drakma:*text-content-types*)))
23
-    (drakma:http-request (puri:merge-uris method *endpoint*)
24
-                         :method :POST
25
-                         :content-type "application/json"
26
-                         :content post-data
27
-                         :basic-authorization auth
28
-                         :want-stream t)))
29
-
30 6
 (defun get-issues (auth)
31 7
   (api-get-call auth "search"))
32 8
 
... ...
@@ -80,30 +56,8 @@
80 56
              (when description
81 57
                (list description))))))
82 58
 
83
-(deftype vector-of-objects () '(vector (or hash-table sheeple:object) *))
84
-
85
-(defun json2sheeple (json)
86
-  (labels
87
-    ((handle-parsed (parsed-json)
88
-       (typecase parsed-json
89
-         (vector-of-objects (map 'vector #'handle-parsed parsed-json))
90
-         (hash-table
91
-           (let ((result (sheeple:object)))
92
-             (loop for json-prop being the hash-keys of parsed-json using (hash-value json-value)
93
-                   do (setf (sheeple:property-value result
94
-                                                    (intern (string-upcase json-prop) :jira-api))
95
-                            (typecase json-value
96
-                              (hash-table (handle-parsed json-value))
97
-                              (vector-of-objects (map 'vector #'handle-parsed json-value))
98
-                              (t json-value)))
99
-                   finally (return result))))
100
-         (t parsed-json))))
101
-    (let ((yason:*parse-json-arrays-as-vectors* t))
102
-      (handle-parsed (yason:parse json)))))
103
-
104 59
 (defun show-person (person title)
105
-  (sheeple:with-properties (displayname emailaddress) person
106
-    (format t "~&~4t~a: \"~a\" <~a>~%" title displayname emailaddress)))
60
+  (format t "~&~4t~a: ~a~%" title (show person)))
107 61
 
108 62
 (defun show-labels (labels)
109 63
   (format t "~&~4tLabels: ~{~a~}~%" (coerce labels 'list)))
... ...
@@ -137,69 +91,35 @@
137 91
       (princ #\space))))
138 92
 
139 93
 (defun classify-issues (sheeple-issues &optional (classify-by '(status name)))
140
-   (sheeple:with-properties (issues) sheeple-issues
141
-     (loop with classes = (make-hash-table :test 'equalp)
142
-           for issue across issues
143
-           do (sheeple:with-properties (fields) issue
144
-                (loop for classification in classify-by
145
-                      for thing = (sheeple:property-value fields classification)
146
-                          then (sheeple:property-value thing classification)
147
-                      finally (push issue (gethash thing classes))))
148
-           finally (return classes))))
149
-
150
-(defun show-issues (sheeple-issues)
151 94
   (sheeple:with-properties (issues) sheeple-issues
152
-    (loop for issue across issues
153
-          do (show-issue-short issue))))
95
+    (loop with classes = (make-hash-table :test 'equalp)
96
+          for issue across issues
97
+          do (loop for classification in classify-by
98
+                   for thing = (sheeple:property-value (fields issue) classification)
99
+                       then (sheeple:property-value thing classification)
100
+                   finally (push issue (gethash thing classes)))
101
+          finally (return classes))))
154 102
 
155 103
 (defun show-issue-short (issue)
156
-  (sheeple:with-properties (key fields self) issue
157
-    (sheeple:with-properties (summary status) fields
158
-      (sheeple:with-properties ((status-name name)) status
159
-        (format t "~&~a ~a (~a)~%" self key status-name))
104
+  (sheeple:with-properties (key self) issue
105
+    (let* ((fields (fields issue))
106
+           (status (name (status fields)))
107
+           (summary (summary fields)))
108
+      (format t "~&~a ~a (~a)~%" self key status)
160 109
       (show-summary summary)
161 110
       (fresh-line))))
162 111
 
163
-(defun show-issue (issue)
164
-  (declare (optimize (debug 3)))
165
-  (sheeple:with-properties (fields key id self) issue
166
-    (sheeple:with-properties (summary description reporter creator assignee status labels) fields
167
-      (sheeple:with-properties ((status-name name)) status
168
-        (format t "~a (~a) <~a>~%" key status-name self))
169
-
170
-      (show-summary summary)
171
-
172
-      (show-person reporter "Reporter")
173
-      (show-person creator "Creator")
174
-      (when assignee
175
-        (show-person assignee "Assignee"))
176
-
177
-      (when (< 0 (length labels))
178
-        (show-labels labels))
179
-
180
-      (when description
181
-        (show-description description)))))
112
+(defun show-issues (sheeple-issues)
113
+  (sheeple:with-properties (issues) sheeple-issues
114
+    (loop for issue across issues
115
+          do (ensure-parent issue =issue=)
116
+          do (show-issue-short issue))))
182 117
 
183
-(defun show-issuetype (issuetype)
184
-  (sheeple:with-properties (description name) issuetype
185
-    (pprint-logical-block (*standard-output* (tokens description))
186
-      (pprint-indent :block 15 *standard-output*)
187
-      (pprint-exit-if-list-exhausted)
188
-      (format *standard-output* "~4t~10@a: " name)
189
-      (loop
190
-        (princ (pprint-pop))
191
-        (pprint-exit-if-list-exhausted)
192
-        (pprint-newline :fill *standard-output*)
193
-        (princ #\space)))))
194
-
195
-(defun show-project (project)
196
-  (sheeple:with-properties (name key issuetypes) project
197
-    (format t "~a: ~a~%" key name)
198
-    (loop for issuetype across issuetypes
199
-          do (show-issuetype issuetype)
200
-          do (terpri))))
118
+(defun print-on-own-line (str)
119
+  (format t "~&~a~&" str))
201 120
 
202 121
 (defun show-projects (projects)
203 122
   (sheeple:with-properties (projects) projects
204 123
     (loop for project across projects
205
-          do (show-project project))))
124
+          do (ensure-parent project =project=)
125
+          do (print-on-own-line (show project)))))
206 126
new file mode 100644
... ...
@@ -0,0 +1,120 @@
1
+(in-package :jira-api)
2
+
3
+(defun ensure-parent (object parent &key (err-if-nil t))
4
+  (when (and (not err-if-nil) (null object))
5
+    (return-from ensure-parent object))
6
+
7
+  (unless (sheeple:parentp parent object)
8
+    (push parent (sheeple:object-parents object))
9
+    (sheeple:shared-init object))
10
+  object)
11
+
12
+(sheeple:defproto =status= () (name))
13
+(sheeple:defproto =person= () (displayname emailaddress))
14
+(sheeple:defproto =issue= () (fields key id self))
15
+(sheeple:defproto =fields= () (summary description reporter creator assignee status))
16
+(sheeple:defmessage show (object &rest args))
17
+(sheeple:defmessage fields-labels (fields))
18
+(sheeple:defreply fields-labels ((fields =fields=))
19
+  (sheeple:with-properties (labels) fields
20
+    labels))
21
+
22
+(sheeple:defreply sheeple:shared-init :after ((issue =issue=) &key)
23
+  (with-accessors ((fields fields)) issue
24
+    (when fields
25
+      (ensure-parent fields =fields=)
26
+      (ensure-parent (status fields) =status=)
27
+      (ensure-parent (reporter fields) =person=)
28
+      (ensure-parent (creator fields) =person=)
29
+      (ensure-parent (assignee fields) =person= :err-if-nil nil))))
30
+
31
+(sheeple:defreply show ((person =person=) &rest args)
32
+  (declare (ignore args))
33
+  (format nil "\"~a\" <~a>" (displayname person) (emailaddress person)))
34
+
35
+(sheeple:defreply show ((status =status=) &rest args)
36
+  (declare (ignore args))
37
+  (format nil "~a" (name status)))
38
+
39
+(sheeple:defreply show ((issue =issue=) &rest args)
40
+  (declare (ignorable args))
41
+  (with-output-to-string (*standard-output*)
42
+    (if-let ((fields (fields issue)))
43
+      (with-accessors ((status status) (summary summary) (reporter reporter)
44
+                                       (creator creator) (assignee assignee)
45
+                                       (labels fields-labels)) fields
46
+
47
+        (format t "~a (~a) <~a>~%"
48
+                (key issue)
49
+                (show status)
50
+                (self issue))
51
+
52
+        (show-summary summary)
53
+
54
+        (show-person reporter "Reporter")
55
+        (show-person creator "Creator")
56
+        (when assignee
57
+          (show-person assignee "Assignee"))
58
+
59
+        (when (< 0 (length labels))
60
+          (show-labels labels))
61
+
62
+        (when (description fields)
63
+          (show-description (description fields))))
64
+      (fresh-line))))
65
+
66
+(sheeple:defproto =project= () (name key issuetypes))
67
+
68
+(sheeple:defproto =issuetype= () (description name))
69
+
70
+(sheeple:defreply sheeple:shared-init :after ((project =project=) &key)
71
+                  (declare (optimize (debug 3)))
72
+  (with-accessors ((issuetypes issuetypes)) project
73
+    (when issuetypes
74
+      (map nil
75
+           (lambda (issuetype) (ensure-parent issuetype =issuetype=))
76
+           issuetypes))))
77
+
78
+(sheeple:defreply show ((issuetype =issuetype=) &rest arg)
79
+  (declare (ignore args))
80
+  (with-output-to-string (*standard-output*)
81
+    (pprint-logical-block (*standard-output* (tokens (description issuetype)))
82
+      (pprint-indent :block 15 *standard-output*)
83
+      (pprint-exit-if-list-exhausted)
84
+      (format *standard-output* "~4t~10@a: " (name issuetype))
85
+      (loop
86
+        (princ (pprint-pop))
87
+        (pprint-exit-if-list-exhausted)
88
+        (pprint-newline :fill *standard-output*)
89
+        (princ #\space)))))
90
+
91
+(sheeple:defreply show ((project =project=) &rest args)
92
+  (declare (ignore args))
93
+  (with-output-to-string (*standard-output*)
94
+    (format t "~a: ~a~%~{~a~&~}" (key project) (name project)
95
+            (map 'list #'show (issuetypes project)))))
96
+
97
+(deftype vector-of-objects () '(vector (or hash-table sheeple:object) *))
98
+
99
+(defun json2sheeple (json &optional parent)
100
+  (labels
101
+    ((handle-parsed (parsed-json)
102
+       (typecase parsed-json
103
+         (vector-of-objects (map 'vector #'handle-parsed parsed-json))
104
+         (hash-table
105
+           (let ((result (sheeple:object)))
106
+             (loop for json-prop being the hash-keys of parsed-json using (hash-value json-value)
107
+                   do (setf (sheeple:property-value result
108
+                                                    (intern (string-upcase json-prop) :jira-api))
109
+                            (typecase json-value
110
+                              (hash-table (handle-parsed json-value))
111
+                              (vector-of-objects (map 'vector #'handle-parsed json-value))
112
+                              (t json-value)))
113
+                   finally (return result))))
114
+         (t parsed-json))))
115
+    (let* ((yason:*parse-json-arrays-as-vectors* t)
116
+           (result (handle-parsed (yason:parse json))))
117
+      (when parent
118
+        (ensure-parent result parent))
119
+      result)))
120
+
... ...
@@ -58,12 +58,14 @@
58 58
       ((getopt :long-name "help") (help))
59 59
       ((getopt :long-name "version") (format t "~&~a~%" *version*))
60 60
       ((getopt :long-name "get-issue") (let ((options (remainder)))
61
-                                         (jira-api::show-issue
62
-                                           (jira-api::json2sheeple
63
-                                             (jira-api::get-issue *auth*
64
-                                                                  (format nil "~a-~a"
65
-                                                                          (car options)
66
-                                                                          (cadr options)))))))
61
+                                         (format t "~&~a~&"
62
+                                                 (jira-api::show
63
+                                                   (jira-api::json2sheeple
64
+                                                     (jira-api::get-issue *auth*
65
+                                                                          (format nil "~a-~a"
66
+                                                                                  (car options)
67
+                                                                                  (cadr options)))
68
+                                                     jira-api::=issue=)))))
67 69
       ((getopt :long-name "get-issues")
68 70
        (let ((options (remainder)))
69 71
          (let ((issues (jira-api::json2sheeple (jira-api::get-issues *auth*))))