building out asana client
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,4 +1,4 @@
|
|||||||
CFLAGS=--std=c18 -O2 -Wall -Wextra -pedantic
|
CFLAGS=--std=c18 -Wall -Wextra -pedantic
|
||||||
LDLIBS=-lncurses -lcurl
|
LDLIBS=-lncurses -lcurl
|
||||||
|
|
||||||
.phony: clean
|
.phony: clean
|
||||||
|
|||||||
194
asana/asana.c
194
asana/asana.c
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,71 @@
|
|||||||
#ifndef ASANA_ASANA_H_
|
#ifndef ASANA_ASANA_H_
|
||||||
#define 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_
|
#endif // ASANA_ASANA_H_
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ size_t asana_write_callback(void *contents, size_t size, size_t nmemb,
|
|||||||
Response *asana_fetch(char *path) {
|
Response *asana_fetch(char *path) {
|
||||||
char url[ASANA_URL_MAX_LENGTH];
|
char url[ASANA_URL_MAX_LENGTH];
|
||||||
snprintf(url, ASANA_URL_MAX_LENGTH, "https://app.asana.com/api/1.0/%s", path);
|
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_URL, url);
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, asana_auth_header);
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, asana_auth_header);
|
||||||
@@ -62,6 +64,7 @@ Response *asana_fetch(char *path) {
|
|||||||
CURLcode result;
|
CURLcode result;
|
||||||
result = curl_easy_perform(curl);
|
result = curl_easy_perform(curl);
|
||||||
if (result == CURLE_OK) {
|
if (result == CURLE_OK) {
|
||||||
|
fprintf(stderr, "API Response:\n\n%s\n", response->body);
|
||||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &(response->status));
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &(response->status));
|
||||||
} else {
|
} else {
|
||||||
response->status = 500;
|
response->status = 500;
|
||||||
|
|||||||
23
ncac.c
23
ncac.c
@@ -5,10 +5,11 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <term.h>
|
#include <term.h>
|
||||||
|
|
||||||
|
#include "asana/asana.h"
|
||||||
#include "asana/fetch.h"
|
#include "asana/fetch.h"
|
||||||
#include "ui/base.h"
|
#include "ui/base.h"
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(/*int argc, char **argv*/) {
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
int curs_x = 0;
|
int curs_x = 0;
|
||||||
@@ -35,7 +36,7 @@ int main(int argc, char **argv) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finish(int sig) {
|
static void finish(/*int sig*/) {
|
||||||
endwin();
|
endwin();
|
||||||
asana_cleanup();
|
asana_cleanup();
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -75,11 +76,21 @@ static void setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void get_me(int *curs_x, int *curs_y) {
|
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_x = 0;
|
||||||
(*curs_y)++;
|
(*curs_y)++;
|
||||||
draw_text(response->body, curs_x, curs_y);
|
draw_text(gid, curs_x, curs_y);
|
||||||
|
|
||||||
asana_free_response(response);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user