building out asana client

This commit is contained in:
2020-06-05 17:31:05 -07:00
parent 999ca05d49
commit 3e414e62a8
5 changed files with 282 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
CFLAGS=--std=c18 -O2 -Wall -Wextra -pedantic
CFLAGS=--std=c18 -Wall -Wextra -pedantic
LDLIBS=-lncurses -lcurl
.phony: clean

View File

@@ -0,0 +1,194 @@
#include "asana.h"
#include <string.h>
#include <stdio.h>
#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;
}

View File

@@ -1,4 +1,71 @@
#ifndef ASANA_ASANA_H_
#define ASANA_ASANA_H_
#include <stdlib.h>
#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_

View File

@@ -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;

23
ncac.c
View File

@@ -5,10 +5,11 @@
#include <stdlib.h>
#include <term.h>
#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);
}