diff options
author | 2025-06-12 01:53:38 -0700 | |
---|---|---|
committer | 2025-06-12 01:53:38 -0700 | |
commit | c9adb09abc626cdcc35c345a635ad8c163fcff3e (patch) | |
tree | 93f78bf8e5910a044f96ed77aca498ac60ada804 /src/client.c | |
parent | 179679633a9fc3317585167f86c87a7fe8394945 (diff) | |
parent | b78875e2265339b58c7a67cc83e6db2988aa0d74 (diff) | |
download | github-mirror-c9adb09abc626cdcc35c345a635ad8c163fcff3e.tar.gz github-mirror-c9adb09abc626cdcc35c345a635ad8c163fcff3e.tar.zst github-mirror-c9adb09abc626cdcc35c345a635ad8c163fcff3e.zip |
Merge pull request #2 from ansg191/srht
Diffstat (limited to 'src/client.c')
-rw-r--r-- | src/client.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..034023a --- /dev/null +++ b/src/client.c @@ -0,0 +1,179 @@ +// +// Created by Anshul Gupta on 6/10/25. +// + +#include "client.h" + +#include <stdlib.h> +#include <string.h> + +struct gql_impl { + struct gql_ctx ctx; + CURL *curl; +}; + +gql_client *gql_client_new(struct gql_ctx ctx) +{ + struct gql_impl *c = malloc(sizeof(*c)); + if (!c) + return NULL; + + c->ctx.endpoint = strdup(ctx.endpoint); + c->ctx.token = strdup(ctx.token); + c->ctx.user_agent = strdup(ctx.user_agent); + c->curl = curl_easy_init(); + return c; +} + +gql_client *gql_client_dup(gql_client *client) +{ + struct gql_impl *c = client; + if (!c) + return NULL; + + struct gql_impl *dup = malloc(sizeof(*dup)); + if (!dup) + return NULL; + + dup->ctx.endpoint = strdup(c->ctx.endpoint); + dup->ctx.token = strdup(c->ctx.token); + dup->ctx.user_agent = strdup(c->ctx.user_agent); + dup->curl = curl_easy_duphandle(c->curl); + return dup; +} + +void gql_client_free(gql_client *client) +{ + struct gql_impl *c = client; + if (!c) + return; + curl_easy_cleanup(c->curl); + free((char *) c->ctx.endpoint); + free((char *) c->ctx.token); + free((char *) c->ctx.user_agent); + free(c); +} + +/** + * Wraps the query in a JSON object with a "query" key and an optional + * "variables" key + * @param query GraphQL query + * @param args GraphQL arguments + * @return JSON string + */ +static char *wrap_query(const char *query, cJSON *args) +{ + cJSON *root = NULL, *query_str = NULL; + char *str = NULL; + + root = cJSON_CreateObject(); + if (!root) + return NULL; + + query_str = cJSON_CreateString(query); + if (!query_str) + goto end; + + // Transfer ownership of the string to root + cJSON_AddItemToObject(root, "query", query_str); + + // Add the args object if it exists + if (args) + cJSON_AddItemToObject(root, "variables", args); + + str = cJSON_Print(root); +end: + cJSON_Delete(root); + return str; +} + +static size_t write_data(const void *ptr, const size_t size, size_t nmemb, + void *stream) +{ + (void) size; // unused + + buffer_t *buf = stream; + buffer_append(buf, ptr, nmemb); + return nmemb; +} + +CURLcode gql_client_send(const gql_client *client, const char *query, + cJSON *args, buffer_t *buf) +{ + struct gql_impl *c = (struct gql_impl *) client; + struct curl_slist *headers = NULL; + char auth[1024]; + + // Set the URL + curl_easy_setopt(c->curl, CURLOPT_URL, c->ctx.endpoint); + + // Set the authorization header + snprintf(auth, sizeof(auth), "Authorization: Bearer %s", c->ctx.token); + headers = curl_slist_append(headers, auth); + // Set the content type to JSON + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(c->curl, CURLOPT_HTTPHEADER, headers); + + // Set user agent + curl_easy_setopt(c->curl, CURLOPT_USERAGENT, c->ctx.user_agent); + + // Set the request type to POST + curl_easy_setopt(c->curl, CURLOPT_CUSTOMREQUEST, "POST"); + + // Prepare request body + char *wrapped_query = wrap_query(query, args); + + // Set the request body + curl_easy_setopt(c->curl, CURLOPT_POSTFIELDS, wrapped_query); + curl_easy_setopt(c->curl, CURLOPT_POSTFIELDSIZE, strlen(wrapped_query)); + + // Set the write function to capture the response + curl_easy_setopt(c->curl, CURLOPT_WRITEFUNCTION, write_data); + curl_easy_setopt(c->curl, CURLOPT_WRITEDATA, (void *) buf); + + // Perform the request + const CURLcode ret = curl_easy_perform(c->curl); + + // Append null terminator to the buffer + if (ret == CURLE_OK) + buffer_append(buf, "\0", 1); + + // Cleanup + free(wrapped_query); + curl_slist_free_all(headers); + + return ret; +} + +/** + * Handle errors in the response. + * Will check for the "errors" key in the response and print the error messages. + * Returns failure if any errors are found. + * @param root Parsed JSON response + * @return 0 on success (no errors), -1 on failure + */ +int gql_handle_error(const cJSON *root) +{ + // Check for errors + cJSON *errors = cJSON_GetObjectItemCaseSensitive(root, "errors"); + if (!errors || !cJSON_IsArray(errors)) { + // No errors + return 0; + } + + cJSON *err; + cJSON_ArrayForEach(err, errors) + { + // Get the error message + cJSON *message = cJSON_GetObjectItemCaseSensitive(err, + "message"); + if (message && cJSON_IsString(message)) { + fprintf(stderr, "Github Error: %s\n", + message->valuestring); + } else { + fprintf(stderr, "Github Error: Unknown error\n"); + } + } + + return -1; +} |