diff options
author | Kinsey Moore <kmoore@digium.com> | 2013-05-10 13:13:06 +0000 |
---|---|---|
committer | Kinsey Moore <kmoore@digium.com> | 2013-05-10 13:13:06 +0000 |
commit | 7ce05bfb9b5f06c18451e37bb5b91cd543d6ba84 (patch) | |
tree | b4e833909ec2ba776d39848ecf8d28f74b341424 /rest-api-templates | |
parent | 2cfedc12adf64cc24e855bd9ea30df3aa1b759ce (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.py | 53 | ||||
-rw-r--r-- | rest-api-templates/event_function_decl.mustache | 10 | ||||
-rw-r--r-- | rest-api-templates/res_stasis_http_resource.c.mustache | 86 | ||||
-rw-r--r-- | rest-api-templates/stasis_http_resource.h.mustache | 38 | ||||
-rw-r--r-- | rest-api-templates/swagger_model.py | 4 |
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] |