summaryrefslogtreecommitdiff
path: root/rest-api-templates
diff options
context:
space:
mode:
authorKinsey Moore <kmoore@digium.com>2013-05-10 13:13:06 +0000
committerKinsey Moore <kmoore@digium.com>2013-05-10 13:13:06 +0000
commit7ce05bfb9b5f06c18451e37bb5b91cd543d6ba84 (patch)
treeb4e833909ec2ba776d39848ecf8d28f74b341424 /rest-api-templates
parent2cfedc12adf64cc24e855bd9ea30df3aa1b759ce (diff)
Add channel events for res_stasis apps
This change adds a framework in res_stasis for handling events from channel topics. JSON event generation and validation code is created from event documentation in rest-api/api-docs/events.json to assist in JSON event generation, ensure consistency, and ensure that accurate documentation is available for ALL events that are received by res_stasis applications. The userevent application has been refactored along with the code that handles userevent channel blob events to pass the headers as key/value pairs in the JSON blob. As a side-effect, app_userevent now handles duplicate keys by overwriting the previous value. Review: https://reviewboard.asterisk.org/r/2428/ (closes issue ASTERISK-21180) Patch-By: Kinsey Moore <kmoore@digium.com> git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@388275 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'rest-api-templates')
-rw-r--r--rest-api-templates/asterisk_processor.py53
-rw-r--r--rest-api-templates/event_function_decl.mustache10
-rw-r--r--rest-api-templates/res_stasis_http_resource.c.mustache86
-rw-r--r--rest-api-templates/stasis_http_resource.h.mustache38
-rw-r--r--rest-api-templates/swagger_model.py4
5 files changed, 187 insertions, 4 deletions
diff --git a/rest-api-templates/asterisk_processor.py b/rest-api-templates/asterisk_processor.py
index 81aefbb39..b1fac013a 100644
--- a/rest-api-templates/asterisk_processor.py
+++ b/rest-api-templates/asterisk_processor.py
@@ -107,7 +107,6 @@ class PathSegment(Stringify):
"""
return len(self.__children)
-
class AsteriskProcessor(SwaggerPostProcessor):
"""A SwaggerPostProcessor which adds fields needed to generate Asterisk
RESTful HTTP binding code.
@@ -145,6 +144,18 @@ class AsteriskProcessor(SwaggerPostProcessor):
segment = resource_api.root_path.get_child(api.path.split('/'))
for operation in api.operations:
segment.operations.append(operation)
+ resource_api.api_declaration.has_events = False
+ for model in resource_api.api_declaration.models:
+ if model.id == "Event":
+ resource_api.api_declaration.has_events = True
+ break
+ if resource_api.api_declaration.has_events:
+ resource_api.api_declaration.events = \
+ [self.process_model(model, context) for model in \
+ resource_api.api_declaration.models if model.id != "Event"]
+ else:
+ resource_api.api_declaration.events = []
+
# Since every API path should start with /[resource], root should
# have exactly one child.
if resource_api.root_path.num_children() != 1:
@@ -177,3 +188,43 @@ class AsteriskProcessor(SwaggerPostProcessor):
parameter.c_space = ''
else:
parameter.c_space = ' '
+
+ def process_model(self, model, context):
+ model.c_id = snakify(model.id)
+ model.channel = False
+ model.channel_desc = ""
+ model.bridge = False
+ model.bridge_desc = ""
+ model.properties = [self.process_property(model, prop, context) for prop in model.properties]
+ model.properties = [prop for prop in model.properties if prop]
+ model.has_properties = (len(model.properties) != 0)
+ return model
+
+ def process_property(self, model, prop, context):
+ # process channel separately since it will be pulled out
+ if prop.name == 'channel' and prop.type == 'Channel':
+ model.channel = True
+ model.channel_desc = prop.description or ""
+ return None
+
+ # process bridge separately since it will be pulled out
+ if prop.name == 'bridge' and prop.type == 'Bridge':
+ model.bridge = True
+ model.bridge_desc = prop.description or ""
+ return None
+
+ prop.c_name = snakify(prop.name)
+ if prop.type in self.type_mapping:
+ prop.c_type = self.type_mapping[prop.type]
+ prop.c_convert = self.convert_mapping[prop.c_type]
+ else:
+ prop.c_type = "Property type %s not mappable to a C type" % (prop.type)
+ prop.c_convert = "Property type %s not mappable to a C conversion" % (prop.type)
+ #raise SwaggerError(
+ # "Invalid property type %s" % prop.type, context)
+ # You shouldn't put a space between 'char *' and the variable
+ if prop.c_type.endswith('*'):
+ prop.c_space = ''
+ else:
+ prop.c_space = ' '
+ return prop
diff --git a/rest-api-templates/event_function_decl.mustache b/rest-api-templates/event_function_decl.mustache
new file mode 100644
index 000000000..fd2c7eb5b
--- /dev/null
+++ b/rest-api-templates/event_function_decl.mustache
@@ -0,0 +1,10 @@
+struct ast_json *stasis_json_event_{{c_id}}_create(
+{{#bridge}}
+ struct ast_bridge_snapshot *bridge_snapshot{{#channel}},{{/channel}}{{^channel}}{{#has_properties}},{{/has_properties}}{{/channel}}
+{{/bridge}}
+{{#channel}}
+ struct ast_channel_snapshot *channel_snapshot{{#has_properties}},{{/has_properties}}
+{{/channel}}
+{{#has_properties}}
+ struct ast_json *blob
+{{/has_properties}}
diff --git a/rest-api-templates/res_stasis_http_resource.c.mustache b/rest-api-templates/res_stasis_http_resource.c.mustache
index b02ab62bd..9b1324c09 100644
--- a/rest-api-templates/res_stasis_http_resource.c.mustache
+++ b/rest-api-templates/res_stasis_http_resource.c.mustache
@@ -47,6 +47,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "stasis_http/resource_{{name}}.h"
+{{#has_events}}
+#include "asterisk/stasis_channels.h"
+{{/has_events}}
{{#apis}}
{{#operations}}
@@ -96,6 +99,89 @@ static void stasis_http_{{c_nickname}}_cb(
{{> rest_handler}}
{{/root_path}}
+{{#has_events}}
+{{#events}}
+{{> event_function_decl}}
+ )
+{
+ RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
+ RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
+{{#has_properties}}
+ struct ast_json *validator;
+{{/has_properties}}
+{{#channel}}
+ int ret;
+{{/channel}}
+{{#bridge}}
+{{^channel}}
+ int ret;
+{{/channel}}
+{{/bridge}}
+
+{{#channel}}
+ ast_assert(channel_snapshot != NULL);
+{{/channel}}
+{{#bridge}}
+ ast_assert(bridge_snapshot != NULL);
+{{/bridge}}
+{{#has_properties}}
+ ast_assert(blob != NULL);
+{{#channel}}
+ ast_assert(ast_json_object_get(blob, "channel") == NULL);
+{{/channel}}
+{{#bridge}}
+ ast_assert(ast_json_object_get(blob, "bridge") == NULL);
+{{/bridge}}
+ ast_assert(ast_json_object_get(blob, "type") == NULL);
+{{#properties}}
+
+ validator = ast_json_object_get(blob, "{{name}}");
+ if (validator) {
+ /* do validation? XXX */
+{{#required}}
+ } else {
+ /* fail message generation if the required parameter doesn't exist */
+ return NULL;
+{{/required}}
+ }
+{{/properties}}
+
+ event = ast_json_deep_copy(blob);
+{{/has_properties}}
+{{^has_properties}}
+
+ event = ast_json_object_create();
+{{/has_properties}}
+ if (!event) {
+ return NULL;
+ }
+
+{{#channel}}
+ ret = ast_json_object_set(event,
+ "channel", ast_channel_snapshot_to_json(channel_snapshot));
+ if (ret) {
+ return NULL;
+ }
+
+{{/channel}}
+{{#bridge}}
+ ret = ast_json_object_set(event,
+ "bridge", ast_bridge_snapshot_to_json(bridge_snapshot));
+ if (ret) {
+ return NULL;
+ }
+
+{{/bridge}}
+ message = ast_json_pack("{s: o}", "{{c_id}}", ast_json_ref(event));
+ if (!message) {
+ return NULL;
+ }
+
+ return ast_json_ref(message);
+}
+
+{{/events}}
+{{/has_events}}
static int load_module(void)
{
return stasis_http_add_handler(&{{root_full_name}});
diff --git a/rest-api-templates/stasis_http_resource.h.mustache b/rest-api-templates/stasis_http_resource.h.mustache
index bfec07f0b..2a417186c 100644
--- a/rest-api-templates/stasis_http_resource.h.mustache
+++ b/rest-api-templates/stasis_http_resource.h.mustache
@@ -64,16 +64,48 @@ void stasis_http_{{c_nickname}}(struct ast_variable *headers, struct ast_{{c_nic
{{/operations}}
{{/apis}}
+{{#has_events}}
+struct ast_channel_snapshot;
+struct ast_bridge_snapshot;
+
+{{#events}}
+/*!
+ * \brief {{description}}
+{{#notes}}
+ *
+ * {{{notes}}}
+{{/notes}}
+ *
+{{#channel}}
+ * \param channel {{#channel_desc}}{{channel_desc}}{{/channel_desc}}{{^channel_desc}}The channel to be used to generate this event{{/channel_desc}}
+{{/channel}}
+{{#bridge}}
+ * \param bridge {{#bridge_desc}}{{bridge_desc}}{{/bridge_desc}}{{^bridge_desc}}The bridge to be used to generate this event{{/bridge_desc}}
+{{/bridge}}
+{{#has_properties}}
+ * \param blob JSON blob containing the following parameters:
+{{/has_properties}}
+{{#properties}}
+ * - {{name}}: {{type}} {{#description}}- {{description}}{{/description}}{{#required}} (required){{/required}}
+{{/properties}}
+ *
+ * \retval NULL on error
+ * \retval JSON (ast_json) describing the event
+ */
+{{> event_function_decl}}
+ );
+
+{{/events}}
+{{/has_events}}
/*
* JSON models
*
{{#models}}
* {{id}}
{{#properties}}
- * - {{name}}: {{type}} {{#required}}(required){{/required}}
+ * - {{name}}: {{type}}{{#required}} (required){{/required}}
{{/properties}}
-{{/models}}
- */
+{{/models}} */
#endif /* _ASTERISK_RESOURCE_{{name_caps}}_H */
{{/api_declaration}}
diff --git a/rest-api-templates/swagger_model.py b/rest-api-templates/swagger_model.py
index 3e25cfdc2..c58a5f09b 100644
--- a/rest-api-templates/swagger_model.py
+++ b/rest-api-templates/swagger_model.py
@@ -295,13 +295,17 @@ class Model(Stringify):
def __init__(self):
self.id = None
+ self.notes = None
+ self.description = None
self.properties = None
def load(self, id, model_json, processor, context):
context = add_context(context, model_json, 'id')
+ # This arrangement is required by the Swagger API spec
self.id = model_json.get('id')
if id != self.id:
raise SwaggerError("Model id doesn't match name", c)
+ self.description = model_json.get('description')
props = model_json.get('properties').items() or []
self.properties = [
Property(k).load(j, processor, context) for (k, j) in props]