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 }