From 3e414e62a8916f005e0baf7840c4420c81b41dd4 Mon Sep 17 00:00:00 2001 From: Aaron Gutierrez Date: Fri, 5 Jun 2020 17:31:05 -0700 Subject: [PATCH] building out asana client --- Makefile | 2 +- asana/asana.c | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++ asana/asana.h | 67 +++++++++++++++++ asana/fetch.c | 3 + ncac.c | 23 ++++-- 5 files changed, 282 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 2395417..571915e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS=--std=c18 -O2 -Wall -Wextra -pedantic +CFLAGS=--std=c18 -Wall -Wextra -pedantic LDLIBS=-lncurses -lcurl .phony: clean diff --git a/asana/asana.c b/asana/asana.c index e69de29..a7df02a 100644 --- a/asana/asana.c +++ b/asana/asana.c @@ -0,0 +1,194 @@ +#include "asana.h" + +#include +#include + +#include "../cJSON/cJSON.h" +#include "fetch.h" + +asana_err asana_parse(char *json, void *resource) { + if (json == NULL) { + return ASANA_ERR_PARSE; + } + + asana_err ret = ASANA_ERR_PARSE; + + cJSON *parsed = cJSON_Parse(json); + + if (parsed != NULL) { + cJSON *data = cJSON_GetObjectItemCaseSensitive(parsed, "data"); + if (cJSON_IsObject(data)) { + cJSON *resource_type = cJSON_GetObjectItemCaseSensitive(data, "resource_type"); + + if (cJSON_IsString(resource_type)) { + char *typ = resource_type->valuestring; + + fprintf(stderr, "Parsing a %s\n", typ); + ret = ASANA_ERR_OK; + + if (strcmp(typ, "user") == 0) { + asana_extract_user(data, resource); + } else if (strcmp(typ, "workspace") == 0) { + asana_extract_resource(data, resource); + } else if (strcmp(typ, "task") == 0) { + asana_extract_task(data, resource); + } else if (strcmp(typ, "project") == 0 || strcmp(typ, "user_task_list") == 0) { + asana_extract_project(data, resource); + } else { + fprintf(stderr, "Unknown resource type: %s\n", typ); + ret = ASANA_ERR_UNKNOWN_TYPE; + } + } + } + } + + cJSON_Delete(parsed); + return ret; +} + +void asana_extract_user(cJSON *json, User *user) { + user->type = USER; + asana_extract_resource(json, (Resource *)user); + + fprintf(stderr, "Parsing one user. Name: %s\n", user->name); + + cJSON *workspaces = cJSON_GetObjectItemCaseSensitive(json, "workspaces"); + if (cJSON_IsArray(workspaces)) { + fprintf(stderr, "We got workspaces\n"); + user->workspaces_len = cJSON_GetArraySize(workspaces); + user->workspaces = calloc(sizeof(Workspace), user->workspaces_len); + + cJSON *workspace = NULL; + size_t i = 0; + cJSON_ArrayForEach(workspace, workspaces) { + fprintf(stderr, "Extracting workspace\n"); + asana_extract_resource(workspace, (Resource *)&(user->workspaces[i])); + i++; + } + } else { + user->workspaces_len = 0; + user->workspaces = NULL; + } +} + +void asana_extract_task(cJSON *json, Task *task) { + task->type = TASK; + asana_extract_resource(json, (Resource *)task); + + cJSON *notes = cJSON_GetObjectItemCaseSensitive(json, "notes"); + if (cJSON_IsString(notes)) { + task->notes = malloc(sizeof(char)*strlen(notes->valuestring)); + strcpy(task->notes, notes->valuestring); + } else { + task->notes = NULL; + } +} + +void asana_extract_project(cJSON *json, Project *project) { + project->type = PROJECT; + asana_extract_resource(json, (Resource *)project); + + cJSON *tasks = cJSON_GetObjectItemCaseSensitive(json, "tasks"); + if (cJSON_IsArray(tasks)) { + project->tasks_len = cJSON_GetArraySize(tasks); + project->tasks = calloc(sizeof(Task), project->tasks_len); + + cJSON *task = NULL; + size_t i=0; + cJSON_ArrayForEach(task, tasks) { + asana_extract_task(task, &(project->tasks[i])); + i++; + } + } else { + project->tasks_len = 0; + project->tasks = NULL; + } +} + +void asana_extract_resource(cJSON *json, Resource *resource) { + cJSON *gid = cJSON_GetObjectItemCaseSensitive(json, "gid"); + if (cJSON_IsString(gid)) { + resource->gid = malloc(sizeof(char)*strlen(gid->valuestring)); + strcpy(resource->gid, gid->valuestring); + } else { + resource->gid = NULL; + } + + cJSON *name = cJSON_GetObjectItemCaseSensitive(json, "name"); + if (cJSON_IsString(name)) { + resource->name = malloc(sizeof(char)*strlen(name->valuestring)); + strcpy(resource->name, name->valuestring); + } else { + resource->name = NULL; + } +} + +asana_err user_info(User *user) { + asana_err ret = ASANA_ERR_FETCH; + + Response *user_resp = asana_fetch("users/me"); + + if (user_resp == NULL) { + return ret; + } + + if (user_resp->status == 200) { + ret = asana_parse(user_resp->body, user); + } else { + fprintf(stderr, "Error fetching user: %d", user_resp->status); + } + + asana_free_response(user_resp); + + return ret; +} + +asana_err user_task_list_gid(char *workspace_gid, char *gid) { + int ret = ASANA_ERR_FETCH; + char path[ASANA_URL_MAX_LENGTH]; + snprintf(path, ASANA_URL_MAX_LENGTH, "users/me/user_task_list?workspace=%s&opt_fields=gid,resource_type", workspace_gid); + Response *task_list_resp = asana_fetch(path); + + if (task_list_resp == NULL) { + return ret; + } + + if (task_list_resp->status == 200) { + Project task_list; + ret = asana_parse(task_list_resp->body, &task_list); + + if (ret == ASANA_ERR_OK) { + strcpy(gid, task_list.gid); + ret = ASANA_ERR_OK; + } + } else { + fprintf(stderr, "Error fetching user_task_list: %d\n", task_list_resp->status); + } + + asana_free_response(task_list_resp); + + return ret; +} + +asana_err user_task_list(char *task_list_gid, Project *task_list) { + asana_err ret = ASANA_ERR_FETCH; + char path[ASANA_URL_MAX_LENGTH]; + snprintf(path, ASANA_URL_MAX_LENGTH, "user_task_lists/%s/tasks?limit=100&completed_since=now"); + + Response *task_list_resp = asana_fetch(path); + + if (task_list_resp == NULL) { + return ret; + } + + if (task_list_resp->status == 200) { + ret = asana_parse(task_list_resp->body, task_list); + // TODO: need to parse top-level arrays + } else { + fprintf(stderr, "Error fetching user_Task_list: %d\n", task_list_resp->status); + } + + asana_free_response(task_list_resp); + + return ret; +} diff --git a/asana/asana.h b/asana/asana.h index c7081bf..2736484 100644 --- a/asana/asana.h +++ b/asana/asana.h @@ -1,4 +1,71 @@ #ifndef ASANA_ASANA_H_ #define ASANA_ASANA_H_ +#include + +#include "../cJSON/cJSON.h" + +enum ASANA_RESOURCE { + WORKSPACE, + USER, + TASK, + PROJECT, +}; + +typedef struct Resource { + enum ASANA_RESOURCE type; + char *gid; + char *name; +} Resource; + +typedef struct Workspace { + enum ASANA_RESOURCE type; + char *gid; + char *name; +} Workspace; + +typedef struct User { + enum ASANA_RESOURCE type; + char *gid; + char *name; + size_t workspaces_len; + Workspace *workspaces; +} User; + +typedef struct Task { + enum ASANA_RESOURCE type; + char *gid; + char *name; + char *notes; +} Task; + +typedef struct Project { + enum ASANA_RESOURCE type; + char *gid; + char *name; + size_t tasks_len; + Task *tasks; +} Project; + + +typedef int asana_err; +#define ASANA_ERR_OK 0 +#define ASANA_ERR_FETCH 1 +#define ASANA_ERR_PARSE 2 +#define ASANA_ERR_UNKNOWN_TYPE 3 + +/** + * Parsing methods to take a cJSON object and populate the Asana resource struct + */ +void asana_extract_resource(cJSON *json, Resource *resource); +void asana_extract_user(cJSON *json, User *user); +void asana_extract_workspace(cJSON *json, Workspace *workspace); +void asana_extract_task(cJSON *json, Task *task); +void asana_extract_project(cJSON *json, Project *project); + +asana_err user_info(User *user); + +asana_err user_task_list_gid(char *workspace_gid, char *gid); +asana_err user_task_list(char *task_list_gid, Project *task_list); + #endif // ASANA_ASANA_H_ diff --git a/asana/fetch.c b/asana/fetch.c index b17446e..2a0d900 100644 --- a/asana/fetch.c +++ b/asana/fetch.c @@ -49,6 +49,8 @@ size_t asana_write_callback(void *contents, size_t size, size_t nmemb, Response *asana_fetch(char *path) { char url[ASANA_URL_MAX_LENGTH]; snprintf(url, ASANA_URL_MAX_LENGTH, "https://app.asana.com/api/1.0/%s", path); + + fprintf(stderr, "fetching %s\n", url); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, asana_auth_header); @@ -62,6 +64,7 @@ Response *asana_fetch(char *path) { CURLcode result; result = curl_easy_perform(curl); if (result == CURLE_OK) { + fprintf(stderr, "API Response:\n\n%s\n", response->body); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &(response->status)); } else { response->status = 500; diff --git a/ncac.c b/ncac.c index 1f60bd8..3d70cd8 100644 --- a/ncac.c +++ b/ncac.c @@ -5,10 +5,11 @@ #include #include +#include "asana/asana.h" #include "asana/fetch.h" #include "ui/base.h" -int main(int argc, char **argv) { +int main(/*int argc, char **argv*/) { setup(); int curs_x = 0; @@ -35,7 +36,7 @@ int main(int argc, char **argv) { return 0; } -static void finish(int sig) { +static void finish(/*int sig*/) { endwin(); asana_cleanup(); exit(0); @@ -75,11 +76,21 @@ static void setup() { } void get_me(int *curs_x, int *curs_y) { - Response *response = asana_fetch("users/me"); + User me; + user_info(&me); + + if (me.workspaces == NULL || me.workspaces_len == 0) { + fprintf(stderr, "Unable to get workspaces.\n"); + return; + } + + char gid[64]; + gid[0] = '\0'; + + user_task_list_gid(me.workspaces[0].gid, gid); + fprintf(stderr, "Got a task list? %s\n", gid); *curs_x = 0; (*curs_y)++; - draw_text(response->body, curs_x, curs_y); - - asana_free_response(response); + draw_text(gid, curs_x, curs_y); }