diff options
Diffstat (limited to 'xds-service')
-rw-r--r-- | xds-service/CMakeLists.txt | 42 | ||||
-rw-r--r-- | xds-service/supervisor-service.c | 179 | ||||
-rw-r--r-- | xds-service/supervisor-service.h | 51 | ||||
-rw-r--r-- | xds-service/xds-service-api.c | 224 | ||||
-rw-r--r-- | xds-service/xds-service-api.h | 24 | ||||
-rw-r--r-- | xds-service/xds-service-apidef.h | 85 | ||||
-rw-r--r-- | xds-service/xds-service-apidef.json | 152 |
7 files changed, 757 insertions, 0 deletions
diff --git a/xds-service/CMakeLists.txt b/xds-service/CMakeLists.txt new file mode 100644 index 0000000..9ce0b81 --- /dev/null +++ b/xds-service/CMakeLists.txt @@ -0,0 +1,42 @@ +########################################################################### +# Copyright 2018 IoT.bzh +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Add target to project dependency list +PROJECT_TARGET_ADD(xds-service) + + # Define project Targets + file(GLOB sourcelist "*.c") + + # Define project Targets + ADD_LIBRARY(${TARGET_NAME} MODULE ${sourcelist}) + + target_compile_options(${TARGET_NAME} + PUBLIC -Wno-unused-variable + ) + + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "afb-" + LABELS "BINDINGV2" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME} + ) + + TARGET_LINK_LIBRARIES(${TARGET_NAME} + afb-helpers + ${link_libraries} + ) diff --git a/xds-service/supervisor-service.c b/xds-service/supervisor-service.c new file mode 100644 index 0000000..96955fa --- /dev/null +++ b/xds-service/supervisor-service.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE +#include "supervisor-service.h" +#include "xds-service-api.h" +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include "curl-wrap.h" +#include "wrap-json.h" + +#define SRV_SUPERVISOR_NAME "supervisor" + +struct afb_cred { + int refcount; + uid_t uid; + gid_t gid; + pid_t pid; + const char* user; + const char* label; + const char* id; +}; + +static const char* null_str = "null"; + +static void decode_daemons_cb(void* closure, json_object* obj, + const char* resp) +{ + int rc; + struct afb_cred cred; + json_object *j_response, *j_query, *j_config, *j_ws_servers, *j_ws_clients; + json_object *j_name, *j_apis; + DAEMONS_T* daemons = (DAEMONS_T*)closure; + DAEMON_T* daemon = calloc(sizeof(DAEMON_T), 1); + + if (!daemons) + return; + + if ((rc = wrap_json_unpack(obj, "{si si si ss ss ss}", "pid", &cred.pid, + "uid", &cred.uid, "gid", &cred.gid, "id", &cred.id, + "label", &cred.label, "user", &cred.user)) + < 0) { + // TODO + return; + } + + AFB_INFO("Get config of pid %d", cred.pid); + daemon->pid = cred.pid; + + // Get config + wrap_json_pack(&j_query, "{s:i}", "pid", cred.pid); + rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "config", j_query, &j_response); + if (rc < 0) { + AFB_ERROR("Cannot get config of pid %d", cred.pid); + return; + } + + AFB_DEBUG("%s config result, res=%s", SRV_SUPERVISOR_NAME, + json_object_to_json_string(j_response)); + + if (json_object_object_get_ex(j_response, "response", &j_config)) { + // FIXME : implement free + daemon->config = j_config; + + rc = wrap_json_unpack(j_config, "{s:o s:o s:o}", + "name", &j_name, + "ws_servers", &j_ws_servers, + "ws_clients", &j_ws_clients); + if (rc < 0) { + AFB_ERROR("Error decoding config response %s", wrap_json_get_error_string(rc)); + return; + } + + daemon->name = json_object_is_type(j_name, json_type_null) ? null_str : json_object_get_string(j_name); + daemon->ws_servers = j_ws_servers; + daemon->isServer = (json_object_array_length(j_ws_servers) > 0); + daemon->ws_clients = j_ws_clients; + daemon->isClient = (json_object_array_length(j_ws_clients) > 0); + } + + // Get apis + // '{"pid":6262,"api":"monitor","verb":"get","args":{"apis":true}} + wrap_json_pack(&j_query, "{si ss ss s {sb}}", + "pid", cred.pid, + "api", "monitor", + "verb", "get", + "args", "apis", true); + rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "do", j_query, &j_response); + if (rc < 0) { + AFB_ERROR("Cannot get apis of pid %d", cred.pid); + return; + } else { + //AFB_DEBUG("%s do ...get apis result, res=%s", SRV_SUPERVISOR_NAME, + // json_object_to_json_string(j_response)); + + if (json_object_object_get_ex(j_response, "response", &j_config) && + json_object_object_get_ex(j_config, "apis", &j_apis)) { + daemon->apis = j_apis; + } + } + daemons->daemons[daemons->count] = daemon; + daemons->count++; +} + +int getDaemons(DAEMONS_T** daemons) +{ + int rc; + json_object *j_response, *j_daemons = NULL; + + *daemons = calloc(sizeof(DAEMONS_T), 1); + + if ((rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "discover", NULL, + &j_response)) + < 0) { + return rc; + } + + if ((rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "list", NULL, + &j_response)) + < 0) { + return rc; + } + + AFB_DEBUG("%s list result, res=%s", SRV_SUPERVISOR_NAME, + json_object_to_json_string(j_response)); + + if (json_object_object_get_ex(j_response, "response", &j_daemons)) { + wrap_json_object_for_all(j_daemons, &decode_daemons_cb, *daemons); + } + + return 0; +} + +int trace_exchange(DAEMON_T* svr, DAEMON_T* cli) +{ + int rc; + json_object *j_response, *j_query; + + if (svr == NULL || cli == NULL) { + return -1; + } + + wrap_json_pack(&j_query, "{s:i, s:{s:s}}", "pid", svr->pid, "add", + "request", "common"); + if ((rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "trace", j_query, + &j_response)) + < 0) { + AFB_ERROR("ERROR trace %d result: %s", svr->pid, + json_object_to_json_string(j_response)); + return rc; + } + + wrap_json_pack(&j_query, "{s:i}", "pid", cli->pid); + if ((rc = afb_service_call_sync(SRV_SUPERVISOR_NAME, "trace", j_query, + &j_response)) + < 0) { + AFB_ERROR("ERROR trace %d result: %s", cli->pid, + json_object_to_json_string(j_response)); + return rc; + } + + return 0; +} + +void supervisor_service_init(void) {} diff --git a/xds-service/supervisor-service.h b/xds-service/supervisor-service.h new file mode 100644 index 0000000..84fe36c --- /dev/null +++ b/xds-service/supervisor-service.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author "Sebastien Douheret" <sebastien@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <stdbool.h> +#include "wrap-json.h" + +// FIXME Use chained list instead of static array +#define MAX_CLIENTS 32 +#define MAX_SERVERS 32 +#define MAX_DAEMONS 1024 + + +typedef struct daemon +{ + int pid; + const char* name; + bool isServer; + bool isClient; + //char *ws_clients[MAX_CLIENTS]; + //char *ws_servers[MAX_SERVERS]; + json_object *ws_clients; + json_object *ws_servers; + json_object *config; + json_object *apis; +} DAEMON_T; + +typedef struct daemons_result_ +{ + int count; + DAEMON_T *daemons[MAX_DAEMONS]; +} DAEMONS_T; + + +extern int getDaemons(DAEMONS_T **daemons); +extern int trace_exchange(DAEMON_T *svr, DAEMON_T *cli); +extern void supervisor_service_init(void); diff --git a/xds-service/xds-service-api.c b/xds-service/xds-service-api.c new file mode 100644 index 0000000..08e6a59 --- /dev/null +++ b/xds-service/xds-service-api.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE +#include "xds-service-api.h" +#include "supervisor-service.h" +#include "xds-service-apidef.h" +#include <stdio.h> +#include <string.h> +#include <time.h> + +#define SRV_SUPERVISOR_NAME "supervisor" +#define SRV_HARVESTER_NAME "harvester" + +typedef struct metric_t { + char* name; + json_object* data; + struct timespec timestamp; +} METRIC_T; + +void list(struct afb_req request) +{ + json_object *result, *item = NULL; + DAEMONS_T* daemons = NULL; + + getDaemons(&daemons); + if (daemons == NULL) { + afb_req_fail(request, "failed", ""); + return; + } + + result = json_object_new_array(); + + for (int i = 0; i < daemons->count; i++) { + wrap_json_pack(&item, "{si ss sb sb so so so}", + "pid", daemons->daemons[i]->pid, + "name", daemons->daemons[i]->name, + "isServer", daemons->daemons[i]->isServer, + "isClient", daemons->daemons[i]->isClient, + "ws_servers", daemons->daemons[i]->ws_servers, + "ws_clients", daemons->daemons[i]->ws_clients, + "apis", daemons->daemons[i]->apis); + //, "config", daemons->daemons[i]->config); + json_object_array_add(result, item); + } + afb_req_success(request, result, NULL); +} + +void trace(struct afb_req request) +{ + int rc; + json_object* req_args = afb_req_json(request); + json_object* result = NULL; + DAEMONS_T* daemons = NULL; + const char* ws_name; + const char* wsn; + + if (wrap_json_unpack(req_args, "{s:?s}", "ws", &ws_name)) { + afb_req_fail(request, "Failed", "Error processing arguments."); + return; + } + AFB_INFO("Trace ws: %s", ws_name); + + getDaemons(&daemons); + if (daemons == NULL || daemons->count <= 0) { + afb_req_fail(request, "failed", "No daemon found"); + } + + // search server and client pid + DAEMON_T *pid_s = NULL, *pid_c = NULL; + for (int i = 0; i < daemons->count; i++) { + AFB_DEBUG("_DEBUG_ svr %s", + json_object_to_json_string(daemons->daemons[i]->ws_servers)); + AFB_DEBUG("_DEBUG_ cli %s", + json_object_to_json_string(daemons->daemons[i]->ws_clients)); + + json_object* ws_servers = daemons->daemons[i]->ws_servers; + for (int j = 0; j < json_object_array_length(ws_servers); j++) { + + wsn = json_object_get_string(json_object_array_get_idx(ws_servers, j++)); + if (wsn && strstr(wsn, ws_name) != NULL) { + pid_s = daemons->daemons[i]; + break; + } + } + + json_object* ws_clients = daemons->daemons[i]->ws_clients; + for (int j = 0; j < json_object_array_length(ws_clients); j++) { + wsn = json_object_get_string(json_object_array_get_idx(ws_clients, j++)); + if (wsn && strstr(wsn, ws_name) != NULL) { + pid_c = daemons->daemons[i]; + break; + } + } + + if (pid_s != NULL && pid_c != NULL) { + if ((rc = trace_exchange(pid_s, pid_c)) < 0) { + afb_req_fail_f(request, "failed", "Trace error %d", rc); + } + break; + } + } + + if (pid_s == NULL || pid_c == NULL) { + afb_req_fail(request, "failed", "Cannot determine Server or Client"); + return; + } + + afb_req_success_f(request, result, "Tracing Server pid=%d <-> Client pid=%d", + pid_s->pid, pid_c->pid); +} + +static int harvester_post_data(METRIC_T* metric) +{ + + int rc; + json_object *j_res, *j_query; + + if (!metric->timestamp.tv_sec && !metric->timestamp.tv_nsec) { + clock_gettime(CLOCK_MONOTONIC, &metric->timestamp); + } + + rc = wrap_json_pack(&j_query, "{s:s s:i s:{ s:s s:o s:i } }", "host", + "localhost", "port", 8086, "metric", "name", metric->name, + "value", metric->data, "timestamp", + metric->timestamp.tv_sec); + if (rc < 0) { + AFB_ERROR("Error packing metric, rc=%d", rc); + return rc; + } + + AFB_DEBUG("%s write: %s", SRV_HARVESTER_NAME, + json_object_to_json_string(j_query)); + + rc = afb_service_call_sync(SRV_HARVESTER_NAME, "write", j_query, &j_res); + if (rc < 0) { + AFB_ERROR("Error %s write : rc=%d, j_res=%s", SRV_HARVESTER_NAME, rc, + json_object_to_json_string(j_res)); + return rc; + } + return 0; +} + +void xds_event_cb(const char* evtname, json_object* j_event) +{ + int rc; + METRIC_T metric; + const char* type = NULL; + struct json_object* request = NULL; + + AFB_NOTICE("RECV Event %s : %s", evtname, + json_object_to_json_string(j_event)); + + if (strcmp(evtname, "supervisor/trace") != 0) { + return; + } + + if ((rc = wrap_json_unpack(j_event, "{s:?s}", "type", &type)) < 0) { + AFB_ERROR("Cannot decode event type"); + return; + } + + if (strcmp(type, "request") == 0) { + + if (!json_object_object_get_ex(j_event, "request", &request)) { + AFB_ERROR("Cannot decode event request"); + return; + } + metric.name = "trace"; + metric.data = request; + + rc = harvester_post_data(&metric); + if (rc < 0) { + AFB_ERROR("ERROR harvester_post_data: rc %d", rc); + } + } +} + +void auth(struct afb_req request) +{ + afb_req_session_set_LOA(request, 1); + afb_req_success(request, NULL, NULL); +} + +int init() +{ + +#if 0 // DEBUG + DAEMONS_T *daemons = NULL; + getDaemons(&daemons); + + if (daemons) { + if (daemons->count) { + AFB_DEBUG("_DEBUG_ daemons->count %d", daemons->count); + for (int i = 0; i < daemons->count; i++) { + AFB_DEBUG("pid %d : isServer %d, isClient %d, %s %s", + daemons->daemons[i]->pid, daemons->daemons[i]->isServer, + daemons->daemons[i]->isClient, + json_object_to_json_string(daemons->daemons[i]->ws_servers), + json_object_to_json_string(daemons->daemons[i]->ws_clients)); + } + free(daemons); + } else { + AFB_DEBUG("_DEBUG_ no dameons detected !"); + } + } +#endif + + supervisor_service_init(); + + return 0; +} diff --git a/xds-service/xds-service-api.h b/xds-service/xds-service-api.h new file mode 100644 index 0000000..8a95f77 --- /dev/null +++ b/xds-service/xds-service-api.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author "Sebastien Douheret" <sebastien@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#define AFB_BINDING_VERSION 2 +#include <afb/afb-binding.h> +#include "wrap-json.h" + +extern void xds_event_cb(const char *evtname, json_object *j_event); +extern int init(); diff --git a/xds-service/xds-service-apidef.h b/xds-service/xds-service-apidef.h new file mode 100644 index 0000000..5d53d89 --- /dev/null +++ b/xds-service/xds-service-apidef.h @@ -0,0 +1,85 @@ + +static const char _afb_description_v2_xds_service[] = + "{\"openapi\":\"3.0.0\",\"$schema\":\"http://iot.bzh/download/openapi/sch" + "ema-3.0/default-schema.json\",\"info\":{\"description\":\"TBD - TODO\",\"" + "title\":\"xds-service\",\"version\":\"4.0\",\"x-binding-c-generator\":{\"" + "api\":\"xds-service\",\"version\":2,\"prefix\":\"\",\"postfix\":\"\",\"s" + "tart\":null,\"onevent\":\"xds_event_cb\",\"init\":\"init\",\"scope\":\"\"" + ",\"private\":false}},\"servers\":[{}],\"components\":{\"schemas\":{\"afb" + "-reply\":{\"$ref\":\"#/components/schemas/afb-reply-v2\"},\"afb-event\":" + "{\"$ref\":\"#/components/schemas/afb-event-v2\"},\"afb-reply-v2\":{\"tit" + "le\":\"Generic response.\",\"type\":\"object\",\"required\":[\"jtype\",\"" + "request\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"af" + "b-reply\"},\"request\":{\"type\":\"object\",\"required\":[\"status\"],\"" + "properties\":{\"status\":{\"type\":\"string\"},\"info\":{\"type\":\"stri" + "ng\"},\"token\":{\"type\":\"string\"},\"uuid\":{\"type\":\"string\"},\"r" + "eqid\":{\"type\":\"string\"}}},\"response\":{\"type\":\"object\"}}},\"af" + "b-event-v2\":{\"type\":\"object\",\"required\":[\"jtype\",\"event\"],\"p" + "roperties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-event\"},\"e" + "vent\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}}},\"x-permi" + "ssions\":{\"list\":{\"permission\":\"urn:AGL:permission::platform:can:li" + "st \"},\"trace\":{\"permission\":\"urn:AGL:permission::platform:can:trac" + "e \"}},\"responses\":{\"200\":{\"description\":\"A complex object array " + "response\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/c" + "omponents/schemas/afb-reply\"}}}}}},\"paths\":{\"/auth\":{\"description\"" + ":\"Authenticate session to raise Level Of Assurance of the session\",\"g" + "et\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/list\"},\"" + "responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"}}}},\"/lis" + "t\":{\"description\":\"list \",\"get\":{\"x-permissions\":{\"LOA\":1},\"" + "parameters\":[],\"responses\":{\"200\":{\"$ref\":\"#/components/response" + "s/200\"}}}},\"/trace\":{\"description\":\"trace \",\"get\":{\"x-permissi" + "ons\":{\"LOA\":1},\"parameters\":[{\"in\":\"query\",\"name\":\"ws\",\"re" + "quired\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":" + "{\"$ref\":\"#/components/responses/200\"}}}}}}" +; + +static const struct afb_auth _afb_auths_v2_xds_service[] = { + { .type = afb_auth_Permission, .text = "urn:AGL:permission::platform:can:list " } +}; + + void auth(struct afb_req req); + void list(struct afb_req req); + void trace(struct afb_req req); + +static const struct afb_verb_v2 _afb_verbs_v2_xds_service[] = { + { + .verb = "auth", + .callback = auth, + .auth = &_afb_auths_v2_xds_service[0], + .info = "Authenticate session to raise Level Of Assurance of the session", + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = "list", + .callback = list, + .auth = NULL, + .info = "list ", + .session = AFB_SESSION_LOA_1_V2 + }, + { + .verb = "trace", + .callback = trace, + .auth = NULL, + .info = "trace ", + .session = AFB_SESSION_LOA_1_V2 + }, + { + .verb = NULL, + .callback = NULL, + .auth = NULL, + .info = NULL, + .session = 0 + } +}; + +const struct afb_binding_v2 afbBindingV2 = { + .api = "xds-service", + .specification = _afb_description_v2_xds_service, + .info = "TBD - TODO", + .verbs = _afb_verbs_v2_xds_service, + .preinit = NULL, + .init = init, + .onevent = xds_event_cb, + .noconcurrency = 0 +}; + diff --git a/xds-service/xds-service-apidef.json b/xds-service/xds-service-apidef.json new file mode 100644 index 0000000..76e35b7 --- /dev/null +++ b/xds-service/xds-service-apidef.json @@ -0,0 +1,152 @@ +{ + "openapi": "3.0.0", + "$schema": "http://iot.bzh/download/openapi/schema-3.0/default-schema.json", + "info": { + "description": "TBD - TODO", + "title": "xds-service", + "version": "4.0", + "x-binding-c-generator": { + "api": "xds-service", + "version": 2, + "prefix": "", + "postfix": "", + "start": null, + "onevent": "xds_event_cb", + "init": "init", + "scope": "", + "private": false + } + }, + "servers": [{}], + "components": { + "schemas": { + "afb-reply": { + "$ref": "#/components/schemas/afb-reply-v2" + }, + "afb-event": { + "$ref": "#/components/schemas/afb-event-v2" + }, + "afb-reply-v2": { + "title": "Generic response.", + "type": "object", + "required": ["jtype", "request"], + "properties": { + "jtype": { + "type": "string", + "const": "afb-reply" + }, + "request": { + "type": "object", + "required": ["status"], + "properties": { + "status": { + "type": "string" + }, + "info": { + "type": "string" + }, + "token": { + "type": "string" + }, + "uuid": { + "type": "string" + }, + "reqid": { + "type": "string" + } + } + }, + "response": { + "type": "object" + } + } + }, + "afb-event-v2": { + "type": "object", + "required": ["jtype", "event"], + "properties": { + "jtype": { + "type": "string", + "const": "afb-event" + }, + "event": { + "type": "string" + }, + "data": { + "type": "object" + } + } + } + }, + "x-permissions": { + "list": { + "permission": "urn:AGL:permission::platform:can:list " + }, + "trace": { + "permission": "urn:AGL:permission::platform:can:trace " + } + }, + "responses": { + "200": { + "description": "A complex object array response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/afb-reply" + } + } + } + } + } + }, + "paths": { + "/auth": { + "description": "Authenticate session to raise Level Of Assurance of the session", + "get": { + "x-permissions": { + "$ref": "#/components/x-permissions/list" + }, + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/list": { + "description": "list ", + "get": { + "x-permissions": { + "LOA": 1 + }, + "parameters": [], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + }, + "/trace": { + "description": "trace ", + "get": { + "x-permissions": { + "LOA": 1 + }, + "parameters": [{ + "in": "query", + "name": "ws", + "required": true, + "schema": { + "type": "string" + } + }], + "responses": { + "200": { + "$ref": "#/components/responses/200" + } + } + } + } + } +} |