Mercurial > sci
comparison lib/apic.c @ 27:dae2de19ca5d
misc: switch to JSON everywhere
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 03 Aug 2022 15:18:09 +0200 |
parents | 34cbbd215ef7 |
children |
comparison
equal
deleted
inserted
replaced
26:7e10cace67a3 | 27:dae2de19ca5d |
---|---|
5 #include <string.h> | 5 #include <string.h> |
6 | 6 |
7 #include <curl/curl.h> | 7 #include <curl/curl.h> |
8 | 8 |
9 #include "apic.h" | 9 #include "apic.h" |
10 #include "types.h" | |
11 #include "util.h" | 10 #include "util.h" |
12 | 11 |
13 struct curlpack { | 12 struct curlpack { |
14 CURL *curl; | 13 CURL *curl; |
15 CURLcode code; | 14 CURLcode code; |
16 struct curl_slist *headers; | 15 struct curl_slist *headers; |
17 }; | 16 }; |
18 | 17 |
19 struct converter { | |
20 void *data; | |
21 size_t datasz; | |
22 ssize_t (*unpack)(void *, size_t, json_t *); | |
23 json_t *(*pack)(const void *, size_t); | |
24 }; | |
25 | |
26 struct apiconf apiconf = { | 18 struct apiconf apiconf = { |
27 .baseurl = "http://127.0.0.1" | 19 .baseurl = "http://127.0.0.1" |
28 }; | 20 }; |
29 | 21 |
30 static size_t | 22 static size_t |
37 } | 29 } |
38 | 30 |
39 static inline char * | 31 static inline char * |
40 create_url(const char *fmt, va_list args) | 32 create_url(const char *fmt, va_list args) |
41 { | 33 { |
42 static _Thread_local char ret[256]; | 34 static _Thread_local char ret[1024]; |
43 char page[128]; | 35 char page[128]; |
44 va_list ap; | 36 va_list ap; |
45 | 37 |
46 ret[0] = 0; | 38 ret[0] = 0; |
47 va_copy(ap, args); | 39 va_copy(ap, args); |
91 pack.code = curl_easy_perform(pack.curl); | 83 pack.code = curl_easy_perform(pack.curl); |
92 | 84 |
93 return pack; | 85 return pack; |
94 } | 86 } |
95 | 87 |
96 static int | 88 static json_t * |
97 perform(struct apic *req, const char *body, const char *fmt, va_list ap) | 89 perform(struct apic *req, const char *body, const char *fmt, va_list ap) |
98 { | 90 { |
99 FILE *fp; | 91 FILE *fp; |
100 char *response, *url; | 92 char *response, *url; |
101 size_t responsesz; | 93 size_t responsesz; |
94 json_t *doc = NULL; | |
102 json_error_t error; | 95 json_error_t error; |
103 int ret = -1; | |
104 struct curlpack curl; | 96 struct curlpack curl; |
105 | 97 |
106 memset(req, 0, sizeof (*req)); | 98 memset(req, 0, sizeof (*req)); |
107 | 99 |
108 url = create_url(fmt, ap); | 100 url = create_url(fmt, ap); |
117 else { | 109 else { |
118 curl_easy_getinfo(curl.curl, CURLINFO_RESPONSE_CODE, &req->status); | 110 curl_easy_getinfo(curl.curl, CURLINFO_RESPONSE_CODE, &req->status); |
119 | 111 |
120 if (req->status != 200) | 112 if (req->status != 200) |
121 snprintf(req->error, sizeof (req->error), "HTTP returned %ld", req->status); | 113 snprintf(req->error, sizeof (req->error), "HTTP returned %ld", req->status); |
122 if (response[0] && !(req->doc = json_loads(response, 0, &error))) | 114 if (response[0] && !(doc = json_loads(response, 0, &error))) |
123 snprintf(req->error, sizeof (req->error), "JSON parse error: %s", error.text); | 115 snprintf(req->error, sizeof (req->error), "JSON parse error: %s", error.text); |
124 else | |
125 ret = 0; | |
126 } | 116 } |
127 | 117 |
128 curl_easy_cleanup(curl.curl); | 118 curl_easy_cleanup(curl.curl); |
129 curl_slist_free_all(curl.headers); | 119 curl_slist_free_all(curl.headers); |
130 | 120 |
131 free(response); | 121 free(response); |
132 | 122 |
133 return ret; | 123 return doc; |
134 } | 124 } |
135 | 125 |
136 static ssize_t | 126 static json_t * |
137 get(struct apic *req, const struct converter *cv, const char *fmt, ...) | 127 get(struct apic *req, const char *fmt, ...) |
138 { | 128 { |
139 va_list ap; | 129 va_list ap; |
140 ssize_t ret; | 130 json_t *ret; |
141 | 131 |
142 va_start(ap, fmt); | 132 va_start(ap, fmt); |
143 ret = perform(req, NULL, fmt, ap); | 133 ret = perform(req, NULL, fmt, ap); |
144 va_end(ap); | 134 va_end(ap); |
145 | 135 |
146 if (ret < 0) | 136 if (!ret || (!json_is_object(ret) && !json_is_array(ret))) |
147 return -1; | 137 snprintf(req->error, sizeof (req->error), "invalid JSON document received"); |
148 if (!req->doc || (!json_is_object(req->doc) && !json_is_array(req->doc))) | |
149 return snprintf(req->error, sizeof (req->error), "invalid JSON document received"), -1; | |
150 if ((ret = cv->unpack(cv->data, cv->datasz, req->doc)) < 0) | |
151 return snprintf(req->error, sizeof (req->error), "%s", strerror(errno)); | |
152 | 138 |
153 return ret; | 139 return ret; |
154 } | 140 } |
155 | 141 |
156 static int | 142 static int |
157 create(struct apic *req, const struct converter *cv, const char *fmt, ...) | 143 create(struct apic *req, json_t *doc, const char *fmt, ...) |
158 { | 144 { |
159 va_list ap; | 145 va_list ap; |
160 int ret; | 146 json_t *ret; |
161 json_t *doc; | |
162 char *body; | 147 char *body; |
163 | 148 |
164 memset(req, 0, sizeof (*req)); | 149 memset(req, 0, sizeof (*req)); |
165 | 150 |
166 if (!(doc = cv->pack(cv->data, cv->datasz))) | |
167 return snprintf(req->error, sizeof (req->error), "%s", strerror(errno)); | |
168 if (!(body = json_dumps(doc, JSON_COMPACT))) { | 151 if (!(body = json_dumps(doc, JSON_COMPACT))) { |
169 json_decref(doc); | 152 json_decref(doc); |
170 return snprintf(req->error, sizeof (req->error), "%s", strerror(errno)); | 153 return snprintf(req->error, sizeof (req->error), "%s", strerror(errno)), -1; |
171 } | 154 } |
172 | 155 |
173 va_start(ap, fmt); | 156 va_start(ap, fmt); |
174 ret = perform(req, body, fmt, ap); | 157 ret = perform(req, body, fmt, ap); |
175 va_end(ap); | 158 va_end(ap); |
176 | 159 |
177 json_decref(doc); | 160 /* TODO: update id. */ |
161 (void)ret; | |
162 | |
178 free(body); | 163 free(body); |
179 | 164 |
180 return ret; | 165 return 0; |
181 } | 166 } |
182 | 167 |
183 static json_t * | 168 json_t * |
184 wrap_job_to(const void *data, size_t datasz) | |
185 { | |
186 return job_to(data, datasz); | |
187 } | |
188 | |
189 static ssize_t | |
190 wrap_job_from(void *data, size_t datasz, json_t *doc) | |
191 { | |
192 return job_from(data, datasz, doc); | |
193 } | |
194 | |
195 static json_t * | |
196 wrap_jobresult_to(const void *data, size_t datasz) | |
197 { | |
198 return jobresult_to(data, datasz); | |
199 } | |
200 | |
201 static ssize_t | |
202 wrap_project_from(void *data, size_t datasz, json_t *doc) | |
203 { | |
204 return project_from(data, datasz, doc); | |
205 } | |
206 | |
207 static json_t * | |
208 wrap_project_to(const void *data, size_t datasz) | |
209 { | |
210 return project_to(data, datasz); | |
211 } | |
212 | |
213 static ssize_t | |
214 wrap_worker_from(void *data, size_t datasz, json_t *doc) | |
215 { | |
216 return worker_from(data, datasz, doc); | |
217 } | |
218 | |
219 static json_t * | |
220 wrap_worker_to(const void *data, size_t datasz) | |
221 { | |
222 return worker_to(data, datasz); | |
223 } | |
224 | |
225 static ssize_t | |
226 wrap_jobresult_from(void *data, size_t datasz, json_t *doc) | |
227 { | |
228 return jobresult_from(data, datasz, doc); | |
229 } | |
230 | |
231 int | |
232 apic_get(struct apic *req, const char *fmt, ...) | 169 apic_get(struct apic *req, const char *fmt, ...) |
233 { | 170 { |
234 assert(req); | 171 assert(req); |
235 assert(fmt); | 172 assert(fmt); |
236 | 173 |
237 va_list ap; | 174 va_list ap; |
238 int ret; | 175 json_t *ret; |
239 | 176 |
240 va_start(ap, fmt); | 177 va_start(ap, fmt); |
241 ret = perform(req, NULL, fmt, ap); | 178 ret = perform(req, NULL, fmt, ap); |
242 va_end(ap); | 179 va_end(ap); |
243 | 180 |
244 return ret; | 181 return ret; |
245 } | 182 } |
246 | 183 |
247 int | 184 json_t * |
248 apic_post(struct apic *req, const json_t *doc, const char *fmt, ...) | 185 apic_post(struct apic *req, const json_t *doc, const char *fmt, ...) |
249 { | 186 { |
250 assert(req); | 187 assert(req); |
251 assert(fmt); | 188 assert(fmt); |
252 | 189 |
253 va_list ap; | 190 va_list ap; |
254 int ret; | 191 json_t *ret; |
255 char *body; | 192 char *body; |
256 | 193 |
257 if (!(body = json_dumps(doc, JSON_COMPACT))) | 194 if (!(body = json_dumps(doc, JSON_COMPACT))) |
258 util_die("%s", strerror(ENOMEM)); | 195 util_die("%s", strerror(ENOMEM)); |
259 | 196 |
265 | 202 |
266 return ret; | 203 return ret; |
267 } | 204 } |
268 | 205 |
269 int | 206 int |
270 apic_job_add(struct apic *req, struct job *job) | 207 apic_job_add(struct apic *req, json_t *job) |
271 { | 208 { |
272 assert(req); | 209 assert(req); |
273 assert(job); | 210 assert(job); |
274 | 211 |
275 const struct converter cv = { | 212 return create(req, job, "api/v1/jobs"); |
276 .data = job, | 213 } |
277 .datasz = 1, | 214 |
278 .pack = wrap_job_to, | 215 json_t * |
279 .unpack = wrap_job_from | 216 apic_job_todo(struct apic *req, const char *worker_name) |
280 }; | 217 { |
281 | 218 assert(req); |
282 return create(req, &cv, "api/v1/jobs"); | 219 assert(worker_name); |
283 } | 220 |
284 | 221 return get(req, "api/v1/todo/%s", worker_name); |
285 ssize_t | 222 } |
286 apic_job_todo(struct apic *req, struct job *jobs, size_t jobsz, const char *worker_name) | 223 |
287 { | 224 int |
288 assert(req); | 225 apic_jobresult_add(struct apic *req, json_t *res) |
289 assert(jobs); | 226 { |
290 | 227 assert(req); |
291 struct converter cv = { | 228 assert(res); |
292 .data = jobs, | 229 |
293 .datasz = jobsz, | 230 return create(req, res, "api/v1/jobresults"); |
294 .unpack = wrap_job_from | 231 } |
295 }; | 232 |
296 | 233 int |
297 return get(req, &cv, "api/v1/todo/%s", worker_name); | 234 apic_project_save(struct apic *req, json_t *p) |
298 } | 235 { |
299 | 236 assert(req); |
300 int | 237 assert(p); |
301 apic_jobresult_add(struct apic *req, struct jobresult *result) | 238 |
302 { | 239 return create(req, p, "api/v1/projects"); |
303 assert(req); | 240 } |
304 assert(result); | 241 |
305 | 242 json_t * |
306 struct converter cv = { | 243 apic_project_list(struct apic *req) |
307 .data = result, | 244 { |
308 .datasz = 1, | 245 assert(req); |
309 .pack = wrap_jobresult_to, | 246 |
310 .unpack = wrap_jobresult_from | 247 return get(req, "api/v1/projects"); |
311 }; | 248 } |
312 | 249 |
313 return create(req, &cv, "api/v1/jobresults"); | 250 json_t * |
314 } | 251 apic_project_find(struct apic *req, const char *name) |
315 | 252 { |
316 int | 253 assert(req); |
317 apic_project_save(struct apic *req, struct project *project) | 254 assert(name); |
318 { | 255 |
319 assert(req); | 256 return get(req, "api/v1/projects/%s", name); |
320 assert(project); | 257 } |
321 | 258 |
322 struct converter cv = { | 259 int |
323 .data = project, | 260 apic_worker_save(struct apic *req, json_t *wk) |
324 .datasz = 1, | |
325 .pack = wrap_project_to, | |
326 .unpack = wrap_project_from | |
327 }; | |
328 | |
329 return create(req, &cv, "api/v1/projects"); | |
330 } | |
331 | |
332 int | |
333 apic_project_update(struct apic *req, struct project *project) | |
334 { | |
335 assert(req); | |
336 assert(project); | |
337 | |
338 struct converter cv = { | |
339 .data = project, | |
340 .datasz = 1, | |
341 .pack = wrap_project_to, | |
342 .unpack = wrap_project_from | |
343 }; | |
344 | |
345 return create(req, &cv, "api/v1/projects"); | |
346 } | |
347 | |
348 ssize_t | |
349 apic_project_list(struct apic *req, struct project *projects, size_t projectsz) | |
350 { | |
351 assert(req); | |
352 assert(projects); | |
353 | |
354 struct converter cv = { | |
355 .data = projects, | |
356 .datasz = projectsz, | |
357 .unpack = wrap_project_from | |
358 }; | |
359 | |
360 return get(req, &cv, "api/v1/projects"); | |
361 } | |
362 | |
363 int | |
364 apic_project_find(struct apic *req, struct project *project, const char *name) | |
365 { | |
366 assert(req); | |
367 assert(project); | |
368 | |
369 struct converter cv = { | |
370 .data = project, | |
371 .datasz = 1, | |
372 .unpack = wrap_project_from | |
373 }; | |
374 | |
375 return get(req, &cv, "api/v1/projects/%s", name); | |
376 } | |
377 | |
378 int | |
379 apic_worker_save(struct apic *req, struct worker *wk) | |
380 { | 261 { |
381 assert(req); | 262 assert(req); |
382 assert(wk); | 263 assert(wk); |
383 | 264 |
384 struct converter cv = { | 265 return create(req, wk, "api/v1/workers"); |
385 .data = wk, | 266 } |
386 .datasz = 1, | 267 |
387 .pack = wrap_worker_to | 268 json_t * |
388 }; | 269 apic_worker_list(struct apic *req) |
389 | 270 { |
390 return create(req, &cv, "api/v1/workers"); | 271 assert(req); |
391 } | 272 |
392 | 273 return get(req, "api/v1/workers"); |
393 ssize_t | 274 } |
394 apic_worker_list(struct apic *req, struct worker *wk, size_t wksz) | 275 |
395 { | 276 json_t * |
396 assert(req); | 277 apic_worker_find(struct apic *req, const char *name) |
397 assert(wk); | 278 { |
398 assert(wksz); | 279 assert(req); |
399 | 280 assert(name); |
400 struct converter cv = { | 281 |
401 .data = wk, | 282 return get(req, "api/v1/workers/%s", name); |
402 .datasz = wksz, | 283 } |
403 .unpack = wrap_worker_from | |
404 }; | |
405 | |
406 return get(req, &cv, "api/v1/workers"); | |
407 } | |
408 | |
409 int | |
410 apic_worker_find(struct apic *req, struct worker *wk, const char *name) | |
411 { | |
412 assert(req); | |
413 assert(wk); | |
414 | |
415 struct converter cv = { | |
416 .data = wk, | |
417 .datasz = 1, | |
418 .unpack = wrap_worker_from | |
419 }; | |
420 | |
421 return get(req, &cv, "api/v1/workers/%s", name); | |
422 } | |
423 | |
424 void | |
425 apic_finish(struct apic *req) | |
426 { | |
427 assert(req); | |
428 | |
429 if (req->doc) | |
430 json_decref(req->doc); | |
431 | |
432 memset(req, 0, sizeof (*req)); | |
433 } |