Add minion module and todo component
This commit is contained in:
+302
@@ -0,0 +1,302 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file todo.c
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief AISL ToDo.txt module code file
|
||||
*
|
||||
* @see https://lowenware.com/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <uuid.h>
|
||||
#include <string.h>
|
||||
#include <components/query.h>
|
||||
#include <components/quick.h>
|
||||
#include <components/log.h>
|
||||
#include <cStuff/string.h>
|
||||
#include <mods/minion.h>
|
||||
|
||||
|
||||
typedef enum {
|
||||
AX_MINION_UNDEFINED
|
||||
, AX_MINION_TODAY
|
||||
, AX_MINION_CREATE_TASK
|
||||
, AX_MINION_READ_TASK
|
||||
, AX_MINION_UPDATE_TASK
|
||||
, AX_MINION_DELETE_TASK
|
||||
} AxMinionMode;
|
||||
|
||||
|
||||
struct context {
|
||||
struct ax_context root;
|
||||
struct ax_query qs;
|
||||
struct ax_todo_task task;
|
||||
uuid_t file_id;
|
||||
|
||||
AxMinionMode mode;
|
||||
};
|
||||
|
||||
typedef struct context * Context;
|
||||
|
||||
|
||||
static const char modName[] = "minion";
|
||||
|
||||
|
||||
static Context
|
||||
context_new(const char *path, AislHttpMethod method, AxMinion mod)
|
||||
{
|
||||
Context ctx;
|
||||
|
||||
if (!(ctx = (Context)ax_context_new(AX_MODULE(mod))))
|
||||
return ctx;
|
||||
|
||||
if (!(*path))
|
||||
ctx->mode = AX_MINION_TODAY;
|
||||
else if (strcmp(path, "create/") == 0)
|
||||
ctx->mode = AX_MINION_CREATE_TASK;
|
||||
else if (strcmp(path, "read/") == 0)
|
||||
ctx->mode = AX_MINION_READ_TASK;
|
||||
else if (strcmp(path, "update/") == 0)
|
||||
ctx->mode = AX_MINION_UPDATE_TASK;
|
||||
else if (strcmp(path, "delete/") == 0)
|
||||
ctx->mode = AX_MINION_DELETE_TASK;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
context_free(Context ctx)
|
||||
{
|
||||
ax_query_release(&ctx->qs);
|
||||
ax_todo_task_release(&ctx->task);
|
||||
|
||||
ax_context_free((AxContext)ctx);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
on_input_var(const char *key, uint32_t k_len, const char *val, uint32_t v_len,
|
||||
void *p_ctx )
|
||||
{
|
||||
Context ctx = (Context) p_ctx;
|
||||
/* AxMinion mod = (AxMinion)((AxContext)ctx)->mod; */
|
||||
char **out = NULL;
|
||||
|
||||
if (!ctx->task.description && k_len == 11) {
|
||||
if (strncmp(key, "description", k_len) == 0) {
|
||||
out = &ctx->task.description;
|
||||
}
|
||||
}
|
||||
|
||||
if (out) {
|
||||
if (cstuff_strncpy(out, val, v_len) == -1)
|
||||
return -1;
|
||||
ax_query__decode(*out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_open(AxMinion mod, const struct aisl_evt_open *evt)
|
||||
{
|
||||
Context ctx;
|
||||
AislStream s = (AislStream)evt->evt.source;
|
||||
|
||||
if (!(ctx = context_new(&evt->path[AX_MODULE(mod)->ep_length],
|
||||
evt->http_method, mod))) {
|
||||
return AISL_MALLOC_ERROR;
|
||||
}
|
||||
|
||||
aisl_set_context(s, ctx);
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_header(AxMinion mod, const struct aisl_evt_header *evt)
|
||||
{
|
||||
AislStream s = (AislStream)evt->evt.source;
|
||||
Context ctx = aisl_get_context(s);
|
||||
|
||||
if (strcmp(evt->key, "content-length") == 0) {
|
||||
size_t total = strtoll(evt->value, NULL, 10);
|
||||
|
||||
if(ax_query_init(&ctx->qs, total, on_input_var, (void*)ctx) != 0) {
|
||||
ax_quick_response(s, AISL_HTTP_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_input(AxMinion mod, const struct aisl_evt_input *evt)
|
||||
{
|
||||
Context ctx = aisl_get_context((AislStream)evt->evt.source);
|
||||
|
||||
ax_query_feed(&ctx->qs, evt->data, evt->size);
|
||||
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ax_minion_date2str(time_t tt, char *str_dt, size_t sz)
|
||||
{
|
||||
struct tm *p_tm = gmtime(&tt);
|
||||
strftime(str_dt, sz, "%Y-%m-%d", p_tm);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ax_minion_stream_today(AxMinion mod, AislStream s, Context ctx)
|
||||
{
|
||||
int i;
|
||||
CStuffList lst = &mod->todo.list;
|
||||
|
||||
if (aisl_response(s, AISL_HTTP_OK, AISL_AUTO_LENGTH) != AISL_SUCCESS)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_header(s, "Content-Type", "text/json") == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_write(s, "{\"tasks\":[", 10) == -1)
|
||||
goto e_reject;
|
||||
|
||||
for (i = 0; i < lst->length; i++) {
|
||||
AxTodoTask task = lst->items[i];
|
||||
int rc;
|
||||
char dt[32];
|
||||
|
||||
if (i != 0 && aisl_write(s, ",", 1) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_printf(s, "{\"x\":%s,", task->is_done ? "true" : "false") == -1)
|
||||
goto e_reject;
|
||||
|
||||
rc = aisl_printf(s, "\"p\":\"%c\",", task->priority ? task->priority : '0');
|
||||
if (rc == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (task->is_done && task->completion) {
|
||||
ax_minion_date2str(task->completion, dt, sizeof(dt));
|
||||
if (aisl_printf(s, "\"co\":\"%s\",", dt) == -1)
|
||||
goto e_reject;
|
||||
}
|
||||
|
||||
if (task->creation) {
|
||||
ax_minion_date2str(task->creation, dt, sizeof(dt));
|
||||
if (aisl_printf(s, "\"cr\":\"%s\",", dt) == -1)
|
||||
goto e_reject;
|
||||
}
|
||||
|
||||
rc = aisl_printf(s, "\"d\":\"%s\"}", task->description);
|
||||
if (rc == -1)
|
||||
goto e_reject;
|
||||
}
|
||||
|
||||
if (aisl_write(s, "]}", 2) == -1)
|
||||
goto e_reject;
|
||||
|
||||
if (aisl_flush(s) != AISL_SUCCESS)
|
||||
goto e_reject;
|
||||
|
||||
return;
|
||||
|
||||
e_reject:
|
||||
aisl_reject(s);
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_request(AxMinion mod, const struct aisl_evt *evt)
|
||||
{
|
||||
Context ctx = aisl_get_context((AislStream)evt->source);
|
||||
AislStream s = (AislStream)evt->source;
|
||||
|
||||
/* verify input */
|
||||
AX_LOG_STATE("%s: request mode = %d", modName, ctx->mode);
|
||||
switch(ctx->mode) {
|
||||
case AX_MINION_TODAY:
|
||||
ax_minion_stream_today(mod, s, ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
goto finally;
|
||||
|
||||
bad_request:
|
||||
ax_quick_response(s, AISL_HTTP_BAD_REQUEST);
|
||||
|
||||
finally:
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
on_stream_close(AxMinion mod, const struct aisl_evt *evt)
|
||||
{
|
||||
context_free((Context)aisl_get_context((AislStream)evt->source));
|
||||
return AISL_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static AislStatus
|
||||
ax_minion_on_event(AxMinion mod, const struct aisl_evt *evt)
|
||||
{
|
||||
switch(evt->code) {
|
||||
case AISL_EVENT_STREAM_OPEN:
|
||||
return on_stream_open(mod, (const struct aisl_evt_open *)evt);
|
||||
|
||||
case AISL_EVENT_STREAM_HEADER:
|
||||
return on_stream_header(mod, (const struct aisl_evt_header *)evt);
|
||||
|
||||
case AISL_EVENT_STREAM_INPUT:
|
||||
return on_stream_input(mod, (const struct aisl_evt_input *)evt);
|
||||
|
||||
case AISL_EVENT_STREAM_REQUEST:
|
||||
return on_stream_request(mod, evt);
|
||||
|
||||
case AISL_EVENT_STREAM_CLOSE:
|
||||
return on_stream_close(mod, evt);
|
||||
|
||||
default:
|
||||
return AISL_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_minion_init(AxMinion mod, const struct ax_minion_cfg *cfg)
|
||||
{
|
||||
AislStatus result;
|
||||
|
||||
AX_MODULE_INIT(ax_minion, cfg->end_point);
|
||||
|
||||
if ((result = ax_todo_init(&mod->todo, cfg->todo_file)) != AISL_SUCCESS)
|
||||
return result;
|
||||
|
||||
if ((result = ax_todo_read(&mod->todo)) != AISL_SUCCESS)
|
||||
return result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ax_minion_release(AxMinion mod)
|
||||
{
|
||||
ax_todo_release(&mod->todo);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (c) 2017-2019 by Löwenware Ltd
|
||||
* Please, refer LICENSE file for legal information
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file mod-feedback.h
|
||||
* @author Ilja Kartašov <ik@lowenware.com>
|
||||
* @brief AISL ToDo.txt module header file
|
||||
*
|
||||
* @see https://lowenware.com/aisl/
|
||||
*/
|
||||
|
||||
#ifndef MINION_H2FC5C912_0F59_43AD_B805_B0F7C82BE2EF
|
||||
#define MINION_H2FC5C912_0F59_43AD_B805_B0F7C82BE2EF
|
||||
|
||||
#include <cStuff/list.h>
|
||||
#include <mods/context.h>
|
||||
#include <mods/module.h>
|
||||
#include <components/todo.h>
|
||||
|
||||
/* ToDo:
|
||||
* Start with one todo.txt file
|
||||
* Write ToDo.txt parser
|
||||
* Join them together
|
||||
*/
|
||||
|
||||
struct ax_minion_cfg {
|
||||
const char *end_point;
|
||||
const char *todo_file;
|
||||
};
|
||||
|
||||
|
||||
struct ax_minion {
|
||||
struct ax_module root;
|
||||
struct ax_todo todo;
|
||||
};
|
||||
|
||||
|
||||
typedef struct ax_minion *AxMinion;
|
||||
|
||||
|
||||
AislStatus
|
||||
ax_minion_init(AxMinion mod, const struct ax_minion_cfg *cfg);
|
||||
|
||||
|
||||
void
|
||||
ax_minion_release(AxMinion mod);
|
||||
|
||||
#endif /* !MINION_H */
|
||||
Reference in New Issue
Block a user