415 lines
9.9 KiB
C
415 lines
9.9 KiB
C
#ifndef FLECS_REST_IMPL
|
|
#include "flecs_rest.h"
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
static
|
|
bool parse_filter(
|
|
ecs_world_t *world,
|
|
EcsHttpRequest *request,
|
|
ecs_filter_t *filter)
|
|
{
|
|
char buffer[1024];
|
|
|
|
if (ecs_http_get_query_param(
|
|
request->params, "include", buffer, sizeof(buffer)))
|
|
{
|
|
filter->include = ecs_type_from_str(world, buffer);
|
|
filter->include_kind = EcsMatchAny;
|
|
if (!filter->include) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (ecs_http_get_query_param(
|
|
request->params, "exclude", buffer, sizeof(buffer)))
|
|
{
|
|
filter->exclude = ecs_type_from_str(world, buffer);
|
|
if (!filter->exclude) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool parse_select(
|
|
ecs_world_t *world,
|
|
EcsHttpRequest *request,
|
|
ecs_type_t *select)
|
|
{
|
|
char buffer[1024];
|
|
|
|
*select = NULL;
|
|
|
|
if (ecs_http_get_query_param(
|
|
request->params, "select", buffer, sizeof(buffer)))
|
|
{
|
|
*select = ecs_type_from_str(world, buffer);
|
|
if (!*select) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool parse_entity(
|
|
ecs_world_t *world,
|
|
EcsHttpRequest *request,
|
|
ecs_entity_t *e_out)
|
|
{
|
|
ecs_entity_t e = 0;
|
|
|
|
if (request->relative_url && strlen(request->relative_url)) {
|
|
/* Request scope of entity */
|
|
const char *name = request->relative_url;
|
|
|
|
/* If name starts with a number, lookup by entity id */
|
|
if (isdigit(name[0])) {
|
|
e = atoi(name);
|
|
} else {
|
|
e = ecs_lookup_path_w_sep(world, 0, name, "/", NULL);
|
|
if (!e) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
*e_out = e;
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool endpoint_filter(
|
|
ecs_world_t *world,
|
|
ecs_entity_t entity,
|
|
EcsHttpEndpoint *endpoint,
|
|
EcsHttpRequest *request,
|
|
EcsHttpReply *reply)
|
|
{
|
|
ecs_filter_t filter = { };
|
|
ecs_type_t select = NULL;
|
|
|
|
if (!parse_filter(world, request, &filter)) {
|
|
return false;
|
|
}
|
|
|
|
if (!parse_select(world, request, &select)) {
|
|
return false;
|
|
}
|
|
|
|
ecs_iter_t it = ecs_filter_iter(world, &filter);
|
|
|
|
reply->body = ecs_iter_to_json(world, &it, ecs_filter_next, select);
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool endpoint_scope(
|
|
ecs_world_t *world,
|
|
ecs_entity_t entity,
|
|
EcsHttpEndpoint *endpoint,
|
|
EcsHttpRequest *request,
|
|
EcsHttpReply *reply)
|
|
{
|
|
ecs_entity_t e = 0;
|
|
ecs_filter_t filter = { };
|
|
ecs_type_t select = NULL;
|
|
|
|
if (!parse_entity(world, request, &e)) {
|
|
return false;
|
|
}
|
|
|
|
if (!parse_filter(world, request, &filter)) {
|
|
return false;
|
|
}
|
|
|
|
if (!parse_select(world, request, &select)) {
|
|
return false;
|
|
}
|
|
|
|
ecs_iter_t it = ecs_scope_iter_w_filter(world, e, &filter);
|
|
|
|
reply->body = ecs_iter_to_json(world, &it, ecs_scope_next, select);
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool endpoint_entity_new(
|
|
ecs_world_t *world,
|
|
EcsHttpReply *reply,
|
|
const char *name,
|
|
ecs_type_t select)
|
|
{
|
|
ecs_entity_t e = ecs_new_from_path(world, 0, name);
|
|
if (select) {
|
|
ecs_add_type(world, e, select);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool endpoint_entity_add(
|
|
ecs_world_t *world,
|
|
EcsHttpReply *reply,
|
|
ecs_entity_t e,
|
|
ecs_type_t select)
|
|
{
|
|
if (select) {
|
|
ecs_add_type(world, e, select);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool endpoint_entity_remove(
|
|
ecs_world_t *world,
|
|
EcsHttpReply *reply,
|
|
ecs_entity_t e,
|
|
ecs_type_t select)
|
|
{
|
|
if (select) {
|
|
ecs_remove_type(world, e, select);
|
|
} else {
|
|
ecs_delete(world, e);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool endpoint_entity_get(
|
|
ecs_world_t *world,
|
|
EcsHttpReply *reply,
|
|
ecs_entity_t e,
|
|
ecs_type_t select)
|
|
{
|
|
reply->body = ecs_entity_to_json(world, e, select);
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool endpoint_entity(
|
|
ecs_world_t *world,
|
|
ecs_entity_t entity,
|
|
EcsHttpEndpoint *endpoint,
|
|
EcsHttpRequest *request,
|
|
EcsHttpReply *reply)
|
|
{
|
|
ecs_entity_t e = 0;
|
|
ecs_type_t select = NULL;
|
|
|
|
if (!parse_select(world, request, &select)) {
|
|
return false;
|
|
}
|
|
|
|
if (request->method == EcsHttpPost) {
|
|
return endpoint_entity_new(world, reply, request->relative_url, select);
|
|
}
|
|
|
|
if (!parse_entity(world, request, &e)) {
|
|
return false;
|
|
}
|
|
|
|
if (request->method == EcsHttpGet) {
|
|
return endpoint_entity_get(world, reply, e, select);
|
|
} else if (request->method == EcsHttpDelete) {
|
|
return endpoint_entity_remove(world, reply, e, select);
|
|
} else if (request->method == EcsHttpPut) {
|
|
return endpoint_entity_add(world, reply, e, select);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static
|
|
void serialize_browse_info(
|
|
ecs_world_t *world,
|
|
ecs_entity_t parent,
|
|
ecs_entity_t e,
|
|
ecs_strbuf_t *str)
|
|
{
|
|
ecs_strbuf_list_append(str, "\"id\":%u", (int32_t)e);
|
|
|
|
char *type = ecs_type_to_json(world, ecs_get_type(world, e));
|
|
ecs_strbuf_list_append(str, "\"type\":%s", type);
|
|
|
|
const char *name = ecs_get_name(world, e);
|
|
if (name) {
|
|
ecs_strbuf_list_append(str, "\"name\":\"%s\"", name);
|
|
|
|
char *fullpath = ecs_get_fullpath(world, e);
|
|
ecs_strbuf_list_append(str, "\"path\":\"%s\"", fullpath);
|
|
}
|
|
|
|
int32_t child_count = ecs_get_child_count(world, e);
|
|
ecs_strbuf_list_append(str, "\"child_count\":%u", child_count);
|
|
}
|
|
|
|
static
|
|
bool endpoint_browse(
|
|
ecs_world_t *world,
|
|
ecs_entity_t entity,
|
|
EcsHttpEndpoint *endpoint,
|
|
EcsHttpRequest *request,
|
|
EcsHttpReply *reply)
|
|
{
|
|
ecs_filter_t filter = { };
|
|
ecs_entity_t e;
|
|
|
|
if (!parse_entity(world, request, &e)) {
|
|
return false;
|
|
}
|
|
|
|
if (!parse_filter(world, request, &filter)) {
|
|
return false;
|
|
}
|
|
|
|
ecs_iter_t it = ecs_scope_iter_w_filter(world, e, &filter);
|
|
ecs_strbuf_t str = ECS_STRBUF_INIT;
|
|
|
|
ecs_strbuf_list_push(&str, "[", ",");
|
|
while (ecs_scope_next(&it)) {
|
|
int32_t i;
|
|
for (i = 0; i < it.count; i ++) {
|
|
ecs_strbuf_list_next(&str);
|
|
ecs_strbuf_list_push(&str, "{", ",");
|
|
serialize_browse_info(world, e, it.entities[i], &str);
|
|
ecs_strbuf_list_pop(&str, "}");
|
|
}
|
|
}
|
|
ecs_strbuf_list_pop(&str, "]");
|
|
|
|
reply->body = ecs_strbuf_get(&str);
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
bool endpoint_info(
|
|
ecs_world_t *world,
|
|
ecs_entity_t entity,
|
|
EcsHttpEndpoint *endpoint,
|
|
EcsHttpRequest *request,
|
|
EcsHttpReply *reply)
|
|
{
|
|
ecs_entity_t e;
|
|
|
|
if (!parse_entity(world, request, &e)) {
|
|
return false;
|
|
}
|
|
|
|
ecs_strbuf_t str = ECS_STRBUF_INIT;
|
|
ecs_strbuf_list_push(&str, "{", ",");
|
|
serialize_browse_info(world, e, e, &str);
|
|
ecs_strbuf_list_pop(&str, "}");
|
|
|
|
reply->body = ecs_strbuf_get(&str);
|
|
|
|
return true;
|
|
}
|
|
|
|
static
|
|
void EcsRestSetServer(ecs_iter_t *it) {
|
|
EcsRestServer *server_data = ecs_column(it, EcsRestServer, 1);
|
|
ecs_entity_t ecs_typeid(EcsHttpServer) = ecs_column_entity(it, 2);
|
|
ecs_entity_t ecs_typeid(EcsHttpEndpoint) = ecs_column_entity(it, 3);
|
|
ecs_entity_t EcsRestInitialized = ecs_column_entity(it, 4);
|
|
|
|
ecs_world_t *world = it->world;
|
|
|
|
int i;
|
|
for (i = 0; i < it->count; i ++) {
|
|
ecs_entity_t server = it->entities[i];
|
|
|
|
// Get filtered list of entities
|
|
ecs_set(world, server, EcsHttpServer, {.port = server_data[i].port});
|
|
|
|
if (ecs_has_entity(world, server, EcsRestInitialized)) {
|
|
/* Don't add endpoints more than once */
|
|
continue;
|
|
}
|
|
|
|
ecs_entity_t e_filter = ecs_new_w_entity(world, ECS_CHILDOF | server);
|
|
ecs_set(world, e_filter, EcsName, {"e_filter"});
|
|
ecs_set(world, e_filter, EcsHttpEndpoint, {
|
|
.url = "filter",
|
|
.action = endpoint_filter,
|
|
.synchronous = true,
|
|
.ctx = NULL
|
|
});
|
|
|
|
// Get filtered list of children
|
|
ecs_entity_t e_scope = ecs_new_w_entity(world, ECS_CHILDOF | server);
|
|
ecs_set(world, e_scope, EcsName, {"e_scope"});
|
|
ecs_set(world, e_scope, EcsHttpEndpoint, {
|
|
.url = "scope",
|
|
.action = endpoint_scope,
|
|
.synchronous = true,
|
|
.ctx = NULL
|
|
});
|
|
|
|
// Get component data for single entity
|
|
ecs_entity_t e_entity = ecs_new_w_entity(world, ECS_CHILDOF | server);
|
|
ecs_set(world, e_entity, EcsName, {"e_entity"});
|
|
ecs_set(world, e_entity, EcsHttpEndpoint, {
|
|
.url = "entity",
|
|
.action = endpoint_entity,
|
|
.synchronous = true,
|
|
.ctx = NULL
|
|
});
|
|
|
|
// Browse entity information
|
|
ecs_entity_t e_browse = ecs_new_w_entity(world, ECS_CHILDOF | server);
|
|
ecs_set(world, e_browse, EcsName, {"e_browse"});
|
|
ecs_set(world, e_browse, EcsHttpEndpoint, {
|
|
.url = "browse",
|
|
.action = endpoint_browse,
|
|
.synchronous = true,
|
|
.ctx = NULL
|
|
});
|
|
|
|
// Browse entity information for single entity
|
|
ecs_entity_t e_info = ecs_new_w_entity(world, ECS_CHILDOF | server);
|
|
ecs_set(world, e_info, EcsName, {"e_info"});
|
|
ecs_set(world, e_info, EcsHttpEndpoint, {
|
|
.url = "info",
|
|
.action = endpoint_info,
|
|
.synchronous = true,
|
|
.ctx = NULL
|
|
});
|
|
|
|
ecs_add_entity(world, server, EcsRestInitialized);
|
|
}
|
|
}
|
|
|
|
void FlecsRestImport(
|
|
ecs_world_t *world)
|
|
{
|
|
ECS_MODULE(world, FlecsRest);
|
|
|
|
ecs_set_name_prefix(world, "EcsRest");
|
|
|
|
ECS_IMPORT(world, FlecsMeta);
|
|
ECS_IMPORT(world, FlecsComponentsHttp);
|
|
|
|
ECS_META(world, EcsRestServer);
|
|
ECS_TAG(world, EcsRestInitialized);
|
|
|
|
ECS_SYSTEM(world, EcsRestSetServer, EcsOnSet, Server,
|
|
:flecs.components.http.Server,
|
|
:flecs.components.http.Endpoint,
|
|
:Initialized);
|
|
|
|
ECS_EXPORT_COMPONENT(EcsRestServer);
|
|
}
|