|
1 | 1 | :Class GitHubAPIv3 |
2 | | -⍝ This clas offers methods that allows one to communicate with GitHub by using GitHub's REST API from Dyalog APL. |
| 2 | +⍝ This class offers methods that allows one to communicate with GitHub by using GitHub's REST API from Dyalog APL. |
3 | 3 | ⍝ Note that this is version 3 of the GitHub API; the coming version 4 is **_not_** a REST interface!\\ |
4 | 4 | ⍝ Most methods of this class become available only by instanciating the class. At the moment this might seem |
5 | 5 | ⍝ unnecessary because there is only one constructor requiring an owner's name, but this will change once OAuth |
6 | 6 | ⍝ is going to be implemented.\\ |
7 | 7 | ⍝ The class came into being because APL Team needed such a class in order to deal with the members of the APLTree |
8 | 8 | ⍝ and the APL-cation projects.\\ |
9 | | -⍝ Note that this class needs **at least Dyalog 16.0** (⎕JSON).\\ |
10 | 9 | ⍝ The project lives on <https://github.com/aplteam/GitHubAPIv3>\\ |
11 | 10 | ⍝ It is part of the [APLTree library](https://github.com/aplteam/apltree/wiki) |
12 | 11 | ⍝ Kai Jaeger ⋄ APL Team Ltd |
13 | 12 |
|
14 | | - :Include ##.APLTreeUtils |
| 13 | + :Include APLTreeUtils |
15 | 14 |
|
16 | 15 | ⎕IO←1 ⋄ ⎕ML←1 |
17 | 16 |
|
18 | 17 | ∇ r←Version |
19 | 18 | :Access Public Shared |
20 | | - r←'GitHub' '0.0.2.8' '2018-12-31' |
| 19 | + r←'GitHub' '0.3.0.8' '2019-02-17' |
21 | 20 | ∇ |
22 | 21 |
|
23 | 22 | ∇ History |
24 | 23 | :Access Public Shared |
| 24 | + ⍝ * 0.3.0 |
| 25 | + ⍝ * Oktokit support added. |
| 26 | + ⍝ * Versioning corrected. |
| 27 | + ⍝ * Documentation polished. |
| 28 | + ⍝ * :Include ##.APLTreeUtils fixed |
25 | 29 | ⍝ * 0.0.2 |
26 | 30 | ⍝ * Method `GetAllTopics` added. |
27 | 31 | ⍝ * 0.0.1 |
|
51 | 55 | gitPath←'https://api.github.com/repos/',_owner,'/',repoName,'/releases/latest' |
52 | 56 | (rc msg ns)←GetJson gitPath |
53 | 57 | :If 0=rc |
54 | | - ns.⎕DF'[JSON object: Release (',repoName,'-',ns.tag_name,')]' |
| 58 | + ns.⎕DF'[JSON object: ',repoName,'-',ns.tag_name,']' |
55 | 59 | :EndIf |
56 | 60 | ∇ |
57 | 61 |
|
|
66 | 70 | gitPath←'https://api.github.com/repos/',_owner,'/',repoName,'/releases/tags/',tagName |
67 | 71 | (rc msg ns)←GetJson gitPath |
68 | 72 | :If 0=rc |
69 | | - ns.⎕DF'[JSON object: Release (',repoName,'-',tagName,')]' |
| 73 | + ns.⎕DF'[JSON object: ',repoName,'-',tagName,']' |
70 | 74 | :EndIf |
71 | 75 | ∇ |
72 | 76 |
|
|
82 | 86 | gitPath←'https://api.github.com/repos/',_owner,'/',repoName,'/tags' |
83 | 87 | (rc msg ns)←GetJson gitPath |
84 | 88 | :If 0≠≢ns |
85 | | - ns.{⎕DF'[JSON Object: Release (',name,')]'}⊂⍬ |
| 89 | + ns.{⎕DF'[JSON Object: ',⍵,' ',name,']'}⊂repoName |
86 | 90 | :EndIf |
87 | 91 | ∇ |
88 | 92 |
|
|
97 | 101 | parms.Accepted←'application/vnd.github.mercy-preview+json' |
98 | 102 | (rc msg ns)←parms GetJson gitPath |
99 | 103 | :If 0=rc |
100 | | - ns.⎕DF'[JSON object: Release (',repoName,': topics)]' |
| 104 | + ns.⎕DF'[JSON object: ',repoName,':topics]' |
101 | 105 | :EndIf |
102 | 106 | ∇ |
103 | 107 |
|
|
107 | 111 | gitPath←'https://api.github.com/users/',(_owner),'/repos' |
108 | 112 | (rc msg ns)←GetJson gitPath |
109 | 113 | :If 0≠≢ns |
110 | | - ns.{⎕DF'[JSON Object: Repository (',name,')]'}⊂⍬ |
| 114 | + ns.{⎕DF'[JSON Object: ',name,']'}⊂⍬ |
111 | 115 | :EndIf |
112 | 116 | ∇ |
113 | 117 |
|
|
141 | 145 | parms←CreateParms |
142 | 146 | parms.Accepted←'application/vnd.github.mercy-preview+json' |
143 | 147 | parms.Method←'PUT' |
144 | | - parms.Body←'{',(1 ⎕JSON'names'topics),'}' |
| 148 | + parms.Body←'{',(1 JSON'names'topics),'}' |
145 | 149 | (rc msg dummy)←parms GetJson gitPath |
146 | 150 | ∇ |
147 | 151 |
|
|
151 | 155 | ⍝ The items are called major.minor.patch.built with "built" being optional.\\ |
152 | 156 | ⍝ In case that is impossible (because `text` does not fulfil the criteria) `⍬` is returned.\\ |
153 | 157 | ⍝ Assumptions: |
154 | | - ⍝ * `text` may or may not start with a non-digit. If there is one it will be removed.\\ |
| 158 | + ⍝ * `text` may or may not start with a non-digit. All leading non-digit characters are ignored.\\ |
155 | 159 | ⍝ Therefore both `1.2.3` and `v.1.2.3` are valid input. |
156 | 160 | ⍝ * The remaining `text` must consist of nothing but digits and dots. |
157 | 161 | ⍝ * The first two numbers ("major" and "minor") must not be bigger than 99. |
158 | 162 | ⍝ * The third number ("path") must not be bigger than 999. |
159 | 163 | ⍝ * The optional last (forth) number must not be bigger than 99999. |
160 | 164 | ⍝ * `text` must come either with three numbers (as in `1.2.3`) or with four number (as in `1.2.3.9999`). |
161 | 165 | ⍝ However, even if all assumptions are fulfilled but the result is zero there is still a `⍬` returned. |
162 | | - ⍝ Note that leading zeros are mermitted. Therefor 1.2.3.001 is **_not_** a valid input. |
| 166 | + ⍝ Note that leading zeros are not permitted. Therefore 1.2.3.001 is **_not_** a valid input.\\ |
163 | 167 | ⍝ Examples: |
164 | 168 | ⍝ + 1.20.333 transforms into 120333 |
165 | | - ⍝ + 12.12.123.12345 transforms into 1212123.12345 |
| 169 | + ⍝ + 12.12.123.12345 transforms into 1212123.12345\\ |
166 | 170 | ⍝ If the tag name does not fulfil the assumptions the conversion might crash. In that case `⍬` is |
167 | 171 | ⍝ returned as result. |
168 | 172 | number←⍬ |
169 | | - (bool vec)←'.'⎕VFI{⍵↓⍨~⎕D∊⍨⊃⍵}text |
170 | | - :If 3 4∊⍨⍴bool |
171 | | - vec←{⍵↑⍨3⌈4⌊⍴⍵}↑vec |
172 | | - :If 3=⍴vec |
173 | | - :If ∧/100 100 1000>vec |
174 | | - :AndIf 0=number←100 100 1000⊥vec |
175 | | - number←⍬ |
176 | | - :EndIf |
177 | | - :Else |
178 | | - :If ~∧/100 100 1000 100000>vec |
179 | | - number←⍬ |
180 | | - :ElseIf 0=number←100 100 1000 100000⊥vec |
181 | | - number←⍬ |
| 173 | + text←{⍵↓⍨+/∧\0=⍵∊⎕D}text |
| 174 | + :If 0=+/{+/∧\'0'=⍵}¨'.'Split text |
| 175 | + (bool vec)←'.'⎕VFI text |
| 176 | + :If 3 4∊⍨⍴bool |
| 177 | + vec←{⍵↑⍨3⌈4⌊⍴⍵}↑vec |
| 178 | + :If 3=⍴vec |
| 179 | + :If ∧/100 100 1000>vec |
| 180 | + :AndIf 0=number←100 100 1000⊥vec |
| 181 | + number←⍬ |
| 182 | + :EndIf |
| 183 | + :Else |
| 184 | + :If ~∧/100 100 1000 100000>vec |
| 185 | + number←⍬ |
| 186 | + :ElseIf 0=number←100 100 1000 100000⊥vec |
| 187 | + number←⍬ |
| 188 | + :EndIf |
| 189 | + number÷←100000 |
182 | 190 | :EndIf |
183 | | - number÷←100000 |
184 | 191 | :EndIf |
185 | 192 | :EndIf |
186 | 193 | ∇ |
|
261 | 268 | msg←'HTTP error' |
262 | 269 | :Return |
263 | 270 | :EndIf |
264 | | - ns←⎕JSON GetText res.GetResponseStream |
| 271 | + ns←JSON GetText res.GetResponseStream |
265 | 272 | headers←SplitHeaders⍕res.Headers |
266 | 273 | :If 0<noOfPages←GetNoOfPages headers |
267 | 274 | :For i :In 1↓⍳noOfPages |
|
273 | 280 | msg←'HTTP error' |
274 | 281 | :Return |
275 | 282 | :EndIf |
276 | | - ns,←⎕JSON GetText res.GetResponseStream |
| 283 | + ns,←JSON GetText res.GetResponseStream |
277 | 284 | :EndFor |
278 | 285 | :EndIf |
279 | 286 | :Else |
|
290 | 297 | r.Body←'' |
291 | 298 | ∇ |
292 | 299 |
|
| 300 | + ∇ r←{type}JSON y;version;buff |
| 301 | + ⍝ Cover for `⎕JSON` in order to support 15.0 which had only an ⌶ for what became later `⎕JSON`.\\ |
| 302 | + ⍝ Note that by default this function imports JSON (`type`=0).\\ |
| 303 | + ⍝ In order to export `type` must be specified as 1.\\ |
| 304 | + ⍝ `y` must be a nested vector representing JSON in case `type` is 0 (the default). |
| 305 | + ⍝ Otherwise `y` must be an APL array (including a namespace) that will be exported to JSON. |
| 306 | + type←{0<⎕NC ⍵:⍎⍵ ⋄ 0}'type' |
| 307 | + version←⊃(//)⎕VFI{⍵/⍨2>+\⍵='.'}2⊃'#'⎕WG'APLVersion' |
| 308 | + 'This version of Dyalog is not supported'⎕SIGNAL 11/⍨15>version |
| 309 | + 'Invalid left argument: must be Boolean'⎕SIGNAL 11/⍨~(⊂type)∊0 1 |
| 310 | + :If 15=version |
| 311 | + :If 0=type ⍝ Import? |
| 312 | + r←7159⌶y |
| 313 | + :ElseIf 1=type ⍝ Export! |
| 314 | + ∘∘∘ ⍝ Not implemented eyt (because not required) |
| 315 | + 7160⌶ |
| 316 | + :Else |
| 317 | + ∘∘∘ ⍝ Huuh?! |
| 318 | + :EndIf |
| 319 | + :Else |
| 320 | + r←⍎'type ⎕JSON filename' |
| 321 | + :EndIf |
| 322 | + ∇ |
| 323 | + |
293 | 324 | :EndClass |
0 commit comments