Mercurial > sci
view lib/apic.c @ 22:dd078aea5d02
misc: use project/worker name as primary key
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 21 Jul 2022 20:23:22 +0200 |
parents | f98ea578b1ef |
children | 2cb228f23f53 |
line wrap: on
line source
#include <assert.h> #include <errno.h> #include <stdarg.h> #include <stdio.h> #include <string.h> #include <curl/curl.h> #include "apic.h" #include "types.h" #include "util.h" struct curlpack { CURL *curl; CURLcode code; struct curl_slist *headers; }; struct converter { void *data; size_t datasz; ssize_t (*unpack)(void *, size_t, json_t *); json_t *(*pack)(const void *, size_t); }; struct apiconf apiconf = { .baseurl = "http://127.0.0.1" }; static size_t writer(char *in, size_t n, size_t w, FILE *fp) { if (fwrite(in, n, w, fp) != w) return 0; return w; } static inline char * create_url(const char *fmt, va_list args) { static _Thread_local char ret[256]; char page[128]; va_list ap; ret[0] = 0; va_copy(ap, args); vsnprintf(page, sizeof (page), fmt, ap); va_end(ap); snprintf(ret, sizeof (ret), "%s/%s", apiconf.baseurl, page); return ret; } static inline FILE * create_file(char **buf, size_t *bufsz) { FILE *fp; *buf = NULL; *bufsz = 0; if (!(fp = open_memstream(buf, bufsz))) util_die("open_memstream: %s\n", strerror(errno)); return fp; } static struct curlpack create_curl(FILE *fp, const char *body, const char *url) { struct curlpack pack = {0}; pack.headers = curl_slist_append(pack.headers, "Content-Type: application/json"); pack.curl = curl_easy_init(); curl_easy_setopt(pack.curl, CURLOPT_HTTPHEADER, pack.headers); curl_easy_setopt(pack.curl, CURLOPT_URL, url); curl_easy_setopt(pack.curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(pack.curl, CURLOPT_TIMEOUT, 3L); curl_easy_setopt(pack.curl, CURLOPT_WRITEFUNCTION, writer); curl_easy_setopt(pack.curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(pack.curl, CURLOPT_NOSIGNAL, 1L); /* Assume POST request if there is a body. */ if (body) { curl_easy_setopt(pack.curl, CURLOPT_POSTFIELDS, body); curl_easy_setopt(pack.curl, CURLOPT_POSTFIELDSIZE, strlen(body)); } pack.code = curl_easy_perform(pack.curl); return pack; } static int perform(struct apic *req, const char *body, const char *fmt, va_list ap) { FILE *fp; char *response, *url; size_t responsesz; json_error_t error; int ret = -1; struct curlpack curl; memset(req, 0, sizeof (*req)); url = create_url(fmt, ap); fp = create_file(&response, &responsesz); curl = create_curl(fp, body, url); /* Perform that request now. */ fclose(fp); if (curl.code != CURLE_OK) snprintf(req->error, sizeof (req->error), "%s", curl_easy_strerror(curl.code)); else { curl_easy_getinfo(curl.curl, CURLINFO_RESPONSE_CODE, &req->status); if (req->status != 200) snprintf(req->error, sizeof (req->error), "HTTP returned %ld", req->status); if (response[0] && !(req->doc = json_loads(response, 0, &error))) snprintf(req->error, sizeof (req->error), "JSON parse error: %s", error.text); else ret = 0; } curl_easy_cleanup(curl.curl); curl_slist_free_all(curl.headers); free(response); return ret; } static ssize_t get(struct apic *req, const struct converter *cv, const char *fmt, ...) { va_list ap; ssize_t ret; va_start(ap, fmt); ret = perform(req, NULL, fmt, ap); va_end(ap); if (ret < 0) return -1; if (!req->doc || (!json_is_object(req->doc) && !json_is_array(req->doc))) return snprintf(req->error, sizeof (req->error), "invalid JSON document received"), -1; if ((ret = cv->unpack(cv->data, cv->datasz, req->doc)) < 0) return snprintf(req->error, sizeof (req->error), "%s", strerror(errno)); return ret; } static int create(struct apic *req, const struct converter *cv, const char *fmt, ...) { va_list ap; int ret; json_t *doc; char *body; memset(req, 0, sizeof (*req)); if (!(doc = cv->pack(cv->data, cv->datasz))) return snprintf(req->error, sizeof (req->error), "%s", strerror(errno)); if (!(body = json_dumps(doc, JSON_COMPACT))) { json_decref(doc); return snprintf(req->error, sizeof (req->error), "%s", strerror(errno)); } va_start(ap, fmt); ret = perform(req, body, fmt, ap); va_end(ap); json_decref(doc); free(body); return 0; } static json_t * wrap_job_to(const void *data, size_t datasz) { return job_to(data, datasz); } static ssize_t wrap_job_from(void *data, size_t datasz, json_t *doc) { return job_from(data, datasz, doc); } static json_t * wrap_jobresult_to(const void *data, size_t datasz) { return jobresult_to(data, datasz); } static ssize_t wrap_project_from(void *data, size_t datasz, json_t *doc) { return project_from(data, datasz, doc); } static json_t * wrap_project_to(const void *data, size_t datasz) { return project_to(data, datasz); } static ssize_t wrap_worker_from(void *data, size_t datasz, json_t *doc) { return worker_from(data, datasz, doc); } static json_t * wrap_worker_to(const void *data, size_t datasz) { return worker_to(data, datasz); } static ssize_t wrap_jobresult_from(void *data, size_t datasz, json_t *doc) { return jobresult_from(data, datasz, doc); } int apic_get(struct apic *req, const char *fmt, ...) { assert(req); assert(fmt); va_list ap; int ret; va_start(ap, fmt); ret = perform(req, NULL, fmt, ap); va_end(ap); return ret; } int apic_post(struct apic *req, const json_t *doc, const char *fmt, ...) { assert(req); assert(fmt); va_list ap; int ret; char *body; if (!(body = json_dumps(doc, JSON_COMPACT))) util_die("%s", strerror(ENOMEM)); va_start(ap, fmt); ret = perform(req, body, fmt, ap); va_end(ap); free(body); return ret; } int apic_job_add(struct apic *req, struct job *job) { assert(req); assert(job); const struct converter cv = { .data = job, .datasz = 1, .pack = wrap_job_to, .unpack = wrap_job_from }; return create(req, &cv, "api/v1/jobs"); } ssize_t apic_job_todo(struct apic *req, struct job *jobs, size_t jobsz, intmax_t worker_id) { assert(req); assert(jobs); struct converter cv = { .data = jobs, .datasz = 1, .unpack = wrap_job_from }; return get(req, &cv, "api/v1/jobs/%jd", worker_id); } int apic_jobresult_add(struct apic *req, struct jobresult *result) { assert(req); assert(result); struct converter cv = { .data = result, .datasz = 1, .pack = wrap_jobresult_to, .unpack = wrap_jobresult_from }; return create(req, &cv, "api/v1/jobresults"); } int apic_project_save(struct apic *req, struct project *project) { assert(req); assert(project); struct converter cv = { .data = project, .datasz = 1, .pack = wrap_project_to, .unpack = wrap_project_from }; return create(req, &cv, "api/v1/projects"); } int apic_project_update(struct apic *req, struct project *project) { assert(req); assert(project); struct converter cv = { .data = project, .datasz = 1, .pack = wrap_project_to, .unpack = wrap_project_from }; return create(req, &cv, "api/v1/projects"); } ssize_t apic_project_list(struct apic *req, struct project *projects, size_t projectsz) { assert(req); assert(projects); struct converter cv = { .data = projects, .datasz = projectsz, .unpack = wrap_project_from }; return get(req, &cv, "api/v1/projects"); } int apic_project_find(struct apic *req, struct project *project, const char *name) { assert(req); assert(project); struct converter cv = { .data = project, .datasz = 1, .unpack = wrap_project_from }; return get(req, &cv, "api/v1/projects/%s", name); } int apic_worker_save(struct apic *req, struct worker *wk) { assert(req); assert(wk); struct converter cv = { .data = wk, .datasz = 1, .pack = wrap_worker_to }; return create(req, &cv, "api/v1/workers"); } ssize_t apic_worker_list(struct apic *req, struct worker *wk, size_t wksz) { assert(req); assert(wk); assert(wksz); struct converter cv = { .data = wk, .datasz = wksz, .unpack = wrap_worker_from }; return get(req, &cv, "api/v1/workers"); } int apic_worker_find(struct apic *req, struct worker *wk, const char *name) { assert(req); assert(wk); struct converter cv = { .data = wk, .datasz = 1, .unpack = wrap_worker_from }; return get(req, &cv, "api/v1/workers/%s", name); } void apic_finish(struct apic *req) { assert(req); if (req->doc) json_decref(req->doc); memset(req, 0, sizeof (*req)); }