Update events to use Swagger 1.3 subtyping, and related aftermath
authorDavid M. Lee <dlee@digium.com>
Wed, 3 Jul 2013 16:32:41 +0000 (16:32 +0000)
committerDavid M. Lee <dlee@digium.com>
Wed, 3 Jul 2013 16:32:41 +0000 (16:32 +0000)
This patch started with the simple idea of changing the /events data
model to be more sane. The original model would send out events like:

    { "stasis_start": { "args": [], "channel": { ... } } }

The event discriminator was the field name instead of being a value in
the object, due to limitations in how Swagger 1.1 could model objects.
While technically sufficient in communicating event information, it was
really difficult to deal with in terms of client side JSON handling.

This patch takes advantage of a proposed extension[1] to Swagger which
allows type variance through the use of a discriminator field. This had
a domino effect that made this a surprisingly large patch.

 [1]: https://groups.google.com/d/msg/wordnik-api/EC3rGajE0os/ey_5dBI_jWcJ

In changing the models, I also had to change the swagger_model.py
processor so it can handle the type discriminator and subtyping. I took
that a big step forward, and using that information to generate an
ari_model module, which can validate a JSON object against the Swagger
model.

The REST and WebSocket generators were changed to take advantage of the
validators. If compiled with AST_DEVMODE enabled, JSON objects that
don't match their corresponding models will not be sent out. For REST
API calls, a 500 Internal Server response is sent. For WebSockets, the
invalid JSON message is replaced with an error message.

Since this took over about half of the job of the existing JSON
generators, and the .to_json virtual function on messages took over the
other half, I reluctantly removed the generators.

The validators turned up all sorts of errors and inconsistencies in our
data models, and the code. These were cleaned up, with checks in the
code generator avoid some of the consistency problems in the future.

 * The model for a channel snapshot was trimmed down to match the
   information sent via AMI. Many of the field being sent were not
   useful in the general case.
 * The model for a bridge snapshot was updated to be more consistent
   with the other ARI models.

Another impact of introducing subtyping was that the swagger-codegen
documentation generator was insufficient (at least until it catches up
with Swagger 1.2). I wanted it to be easier to generate docs for the API
anyways, so I ported the wiki pages to use the Asterisk Swagger
generator. In the process, I was able to clean up many of the model
links, which would occasionally give inconsistent results on the wiki. I
also added error responses to the wiki docs, making the wiki
documentation more complete.

Finally, since Stasis-HTTP will now be named Asterisk REST Interface
(ARI), any new functions and files I created carry the ari_ prefix. I
changed a few stasis_http references to ari where it was non-intrusive
and made sense.

(closes issue ASTERISK-21885)
Review: https://reviewboard.asterisk.org/r/2639/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393529 65c4cc65-6c06-0410-ace0-fbb531ad65f3

73 files changed:
Makefile
include/asterisk/json.h
include/asterisk/stasis_http.h
main/json.c
main/stasis_bridging.c
main/stasis_channels.c
main/stasis_endpoints.c
res/Makefile
res/res_ari_model.c [new file with mode: 0644]
res/res_ari_model.exports.in [new file with mode: 0644]
res/res_stasis.c
res/res_stasis_http.c
res/res_stasis_http_asterisk.c
res/res_stasis_http_bridges.c
res/res_stasis_http_channels.c
res/res_stasis_http_endpoints.c
res/res_stasis_http_events.c
res/res_stasis_http_playback.c
res/res_stasis_http_recordings.c
res/res_stasis_http_sounds.c
res/res_stasis_json_asterisk.c [deleted file]
res/res_stasis_json_asterisk.exports.in [deleted file]
res/res_stasis_json_bridges.c [deleted file]
res/res_stasis_json_bridges.exports.in [deleted file]
res/res_stasis_json_channels.c [deleted file]
res/res_stasis_json_channels.exports.in [deleted file]
res/res_stasis_json_endpoints.c [deleted file]
res/res_stasis_json_endpoints.exports.in [deleted file]
res/res_stasis_json_events.c [deleted file]
res/res_stasis_json_events.exports.in [deleted file]
res/res_stasis_json_playback.c [deleted file]
res/res_stasis_json_playback.exports.in [deleted file]
res/res_stasis_json_recordings.c [deleted file]
res/res_stasis_json_recordings.exports.in [deleted file]
res/res_stasis_json_sounds.c [deleted file]
res/res_stasis_json_sounds.exports.in [deleted file]
res/stasis_http/ari_model_validators.c [new file with mode: 0644]
res/stasis_http/ari_model_validators.h [new file with mode: 0644]
res/stasis_http/ari_websockets.c
res/stasis_http/resource_recordings.c
res/stasis_http/resource_recordings.h
res/stasis_json/resource_asterisk.h [deleted file]
res/stasis_json/resource_bridges.h [deleted file]
res/stasis_json/resource_channels.h [deleted file]
res/stasis_json/resource_endpoints.h [deleted file]
res/stasis_json/resource_events.h [deleted file]
res/stasis_json/resource_playback.h [deleted file]
res/stasis_json/resource_recordings.h [deleted file]
res/stasis_json/resource_sounds.h [deleted file]
rest-api-templates/api.wiki.mustache [new file with mode: 0644]
rest-api-templates/ari_model_validators.c.mustache [new file with mode: 0644]
rest-api-templates/ari_model_validators.h.mustache [new file with mode: 0644]
rest-api-templates/asterisk_processor.py
rest-api-templates/event_function_decl.mustache [deleted file]
rest-api-templates/make_ari_stubs.py [moved from rest-api-templates/make_stasis_http_stubs.py with 73% similarity]
rest-api-templates/models.wiki.mustache [new file with mode: 0644]
rest-api-templates/res_stasis_http_resource.c.mustache
rest-api-templates/res_stasis_json_resource.c.mustache [deleted file]
rest-api-templates/res_stasis_json_resource.exports.mustache [deleted file]
rest-api-templates/stasis_json_resource.h.mustache [deleted file]
rest-api-templates/swagger_model.py
rest-api-templates/transform.py
rest-api/api-docs/asterisk.json
rest-api/api-docs/bridges.json
rest-api/api-docs/channels.json
rest-api/api-docs/endpoints.json
rest-api/api-docs/events.json
rest-api/api-docs/playback.json
rest-api/api-docs/recordings.json
rest-api/api-docs/sounds.json
tests/test_ari_model.c [new file with mode: 0644]
tests/test_res_stasis.c
tests/test_stasis_channels.c

index 6cca3aa..a515344 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -416,6 +416,7 @@ _clean:
        rm -f main/version.c
        rm -f doc/core-en_US.xml
        rm -f doc/full-en_US.xml
+       rm -f docs/rest-api/*.wiki
        @$(MAKE) -C menuselect clean
        cp -f .cleancount .lastclean
 
@@ -963,15 +964,15 @@ menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(di
 
 # We don't want to require Python or Pystache for every build, so this is its
 # own target.
-stasis-stubs:
+ari-stubs:
 ifeq ($(PYTHON),:)
        @echo "--------------------------------------------------------------------------"
-       @echo "---        Please install python to build Stasis HTTP stubs            ---"
+       @echo "---        Please install python to build ARI stubs            ---"
        @echo "--------------------------------------------------------------------------"
        @false
 else
-       $(PYTHON) rest-api-templates/make_stasis_http_stubs.py \
-               rest-api/resources.json res/
+       $(PYTHON) rest-api-templates/make_ari_stubs.py \
+               rest-api/resources.json .
 endif
 
 .PHONY: menuselect
@@ -993,7 +994,7 @@ endif
 .PHONY: installdirs
 .PHONY: validate-docs
 .PHONY: _clean
-.PHONY: stasis-stubs
+.PHONY: ari-stubs
 .PHONY: $(SUBDIRS_INSTALL)
 .PHONY: $(SUBDIRS_DIST_CLEAN)
 .PHONY: $(SUBDIRS_CLEAN)
index 61685fd..0584c99 100644 (file)
@@ -158,6 +158,15 @@ enum ast_json_type
  */
 enum ast_json_type ast_json_typeof(const struct ast_json *value);
 
+/*!
+ * \brief Get the string name for the given type.
+ * \since 12.0.0
+ * \param type Type to convert to string.
+ * \return Simple string for the type name (object, array, string, etc.)
+ * \return \c "?" for invalid types.
+ */
+const char *ast_json_typename(enum ast_json_type type);
+
 /*!@}*/
 
 /*!@{*/
index 05e9dde..8d5a74e 100644 (file)
 #include "asterisk/json.h"
 #include "asterisk/http_websocket.h"
 
+/*!
+ * \brief Configured encoding format for JSON output.
+ * \return JSON output encoding (compact, pretty, etc.)
+ */
+enum ast_json_encoding_format stasis_http_json_format(void);
+
 struct stasis_http_response;
 
 /*!
@@ -141,12 +147,16 @@ struct ari_websocket_session;
 /*!
  * \brief Create an ARI WebSocket session.
  *
+ * If \c NULL is given for the validator function, no validation will be
+ * performed.
+ *
  * \param ws_session Underlying WebSocket session.
+ * \param validator Function to validate outgoing messages.
  * \return New ARI WebSocket session.
  * \return \c NULL on error.
  */
 struct ari_websocket_session *ari_websocket_session_create(
-       struct ast_websocket *ws_session);
+       struct ast_websocket *ws_session, int (*validator)(struct ast_json *));
 
 /*!
  * \brief Read a message from an ARI WebSocket.
index dff35db..c185b05 100644 (file)
@@ -103,6 +103,23 @@ enum ast_json_type ast_json_typeof(const struct ast_json *json)
        return r;
 }
 
+const char *ast_json_typename(enum ast_json_type type)
+{
+       switch (type) {
+       case AST_JSON_OBJECT: return "object";
+       case AST_JSON_ARRAY: return "array";
+       case AST_JSON_STRING: return "string";
+       case AST_JSON_INTEGER: return "integer";
+       case AST_JSON_REAL: return "real";
+       case AST_JSON_TRUE: return "boolean";
+       case AST_JSON_FALSE: return "boolean";
+       case AST_JSON_NULL: return "null";
+       }
+       ast_assert(0);
+       return "?";
+}
+
+
 struct ast_json *ast_json_true(void)
 {
        return (struct ast_json *)json_true();
index 39a8ba8..dcf4275 100644 (file)
@@ -657,10 +657,10 @@ struct ast_json *ast_bridge_snapshot_to_json(const struct ast_bridge_snapshot *s
        }
 
        json_bridge = ast_json_pack("{s: s, s: s, s: s, s: s, s: o}",
-               "bridgeUniqueid", snapshot->uniqueid,
-               "bridgeTechnology", snapshot->technology,
-               "bridgeType", capability2str(snapshot->capabilities),
-               "bridgeClass", snapshot->subclass,
+               "id", snapshot->uniqueid,
+               "technology", snapshot->technology,
+               "bridge_type", capability2str(snapshot->capabilities),
+               "bridge_class", snapshot->subclass,
                "channels", json_channels);
        if (!json_bridge) {
                return NULL;
index 6dddb0a..d121279 100644 (file)
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include "asterisk/stasis.h"
 #include "asterisk/astobj2.h"
-#include "asterisk/stasis_channels.h"
+#include "asterisk/json.h"
 #include "asterisk/pbx.h"
+#include "asterisk/stasis.h"
+#include "asterisk/stasis_channels.h"
 
 /*** DOCUMENTATION
        <managerEvent language="en_US" name="VarSet">
@@ -621,25 +622,25 @@ struct ast_json *ast_channel_snapshot_to_json(const struct ast_channel_snapshot
                return NULL;
        }
 
-       json_chan = ast_json_pack("{ s: s, s: s, s: s, s: s, s: s, s: s, s: s,"
-                                 "  s: s, s: s, s: s, s: s, s: o, s: o, s: o,"
-                                 "  s: o"
-                                 "}",
-                                 "name", snapshot->name,
-                                 "state", ast_state2str(snapshot->state),
-                                 "accountcode", snapshot->accountcode,
-                                 "peeraccount", snapshot->peeraccount,
-                                 "userfield", snapshot->userfield,
-                                 "uniqueid", snapshot->uniqueid,
-                                 "linkedid", snapshot->linkedid,
-                                 "parkinglot", snapshot->parkinglot,
-                                 "hangupsource", snapshot->hangupsource,
-                                 "appl", snapshot->appl,
-                                 "data", snapshot->data,
-                                 "dialplan", ast_json_dialplan_cep(snapshot->context, snapshot->exten, snapshot->priority),
-                                 "caller", ast_json_name_number(snapshot->caller_name, snapshot->caller_number),
-                                 "connected", ast_json_name_number(snapshot->connected_name, snapshot->connected_number),
-                                 "creationtime", ast_json_timeval(snapshot->creationtime, NULL));
+       json_chan = ast_json_pack(
+               /* Broken up into groups of three for readability */
+               "{ s: s, s: s, s: s,"
+               "  s: o, s: o, s: s,"
+               "  s: o, s: o }",
+               /* First line */
+               "id", snapshot->uniqueid,
+               "name", snapshot->name,
+               "state", ast_state2str(snapshot->state),
+               /* Second line */
+               "caller", ast_json_name_number(
+                       snapshot->caller_name, snapshot->caller_number),
+               "connected", ast_json_name_number(
+                       snapshot->connected_name, snapshot->connected_number),
+               "accountcode", snapshot->accountcode,
+               /* Third line */
+               "dialplan", ast_json_dialplan_cep(
+                       snapshot->context, snapshot->exten, snapshot->priority),
+               "creationtime", ast_json_timeval(snapshot->creationtime, NULL));
 
        return ast_json_ref(json_chan);
 }
@@ -675,6 +676,91 @@ int ast_channel_snapshot_caller_id_equal(
                strcmp(old_snapshot->caller_name, new_snapshot->caller_name) == 0;
 }
 
+static struct ast_json *channel_blob_to_json(struct stasis_message *message,
+       const char *type)
+{
+       RAII_VAR(struct ast_json *, out, NULL, ast_json_unref);
+       struct ast_channel_blob *channel_blob = stasis_message_data(message);
+       struct ast_json *blob = channel_blob->blob;
+       struct ast_channel_snapshot *snapshot = channel_blob->snapshot;
+       const struct timeval *tv = stasis_message_timestamp(message);
+       int res = 0;
+
+       if (blob == NULL || ast_json_is_null(blob)) {
+               out = ast_json_object_create();
+       } else {
+               /* blobs are immutable, so shallow copies are fine */
+               out = ast_json_copy(blob);
+       }
+
+       if (!out) {
+               return NULL;
+       }
+
+       res |= ast_json_object_set(out, "type", ast_json_string_create(type));
+       res |= ast_json_object_set(out, "timestamp",
+               ast_json_timeval(*tv, NULL));
+
+       /* For global channel messages, the snapshot is optional */
+       if (snapshot) {
+               res |= ast_json_object_set(out, "channel",
+                       ast_channel_snapshot_to_json(snapshot));
+       }
+
+       if (res != 0) {
+               return NULL;
+       }
+
+       return ast_json_ref(out);
+}
+
+static struct ast_json *dtmf_end_to_json(struct stasis_message *message)
+{
+       struct ast_channel_blob *channel_blob = stasis_message_data(message);
+       struct ast_json *blob = channel_blob->blob;
+       struct ast_channel_snapshot *snapshot = channel_blob->snapshot;
+       const char *direction =
+               ast_json_string_get(ast_json_object_get(blob, "direction"));
+       const struct timeval *tv = stasis_message_timestamp(message);
+
+       /* Only present received DTMF end events as JSON */
+       if (strcasecmp("Received", direction) != 0) {
+               return NULL;
+       }
+
+       return ast_json_pack("{s: s, s: o, s: O, s: O, s: o}",
+               "type", "ChannelDtmfReceived",
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "digit", ast_json_object_get(blob, "digit"),
+               "duration_ms", ast_json_object_get(blob, "duration_ms"),
+               "channel", ast_channel_snapshot_to_json(snapshot));
+}
+
+static struct ast_json *user_event_to_json(struct stasis_message *message)
+{
+       struct ast_channel_blob *channel_blob = stasis_message_data(message);
+       struct ast_json *blob = channel_blob->blob;
+       struct ast_channel_snapshot *snapshot = channel_blob->snapshot;
+       const struct timeval *tv = stasis_message_timestamp(message);
+
+       return ast_json_pack("{s: s, s: o, s: O, s: O, s: o}",
+               "type", "ChannelUserevent",
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "eventname", ast_json_object_get(blob, "eventname"),
+               "userevent", blob,
+               "channel", ast_channel_snapshot_to_json(snapshot));
+}
+
+static struct ast_json *varset_to_json(struct stasis_message *message)
+{
+       return channel_blob_to_json(message, "ChannelVarset");
+}
+
+static struct ast_json *hangup_request_to_json(struct stasis_message *message)
+{
+       return channel_blob_to_json(message, "ChannelHangupRequest");
+}
+
 /*!
  * @{ \brief Define channel message types.
  */
@@ -682,11 +768,18 @@ STASIS_MESSAGE_TYPE_DEFN(ast_channel_snapshot_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_dial_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_varset_type,
        .to_ami = varset_to_ami,
+       .to_json = varset_to_json,
+       );
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_user_event_type,
+       .to_json = user_event_to_json,
+       );
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_hangup_request_type,
+       .to_json = hangup_request_to_json,
        );
-STASIS_MESSAGE_TYPE_DEFN(ast_channel_user_event_type);
-STASIS_MESSAGE_TYPE_DEFN(ast_channel_hangup_request_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_begin_type);
-STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_end_type);
+STASIS_MESSAGE_TYPE_DEFN(ast_channel_dtmf_end_type,
+       .to_json = dtmf_end_to_json,
+       );
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_hold_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_unhold_type);
 STASIS_MESSAGE_TYPE_DEFN(ast_channel_chanspy_start_type);
index 90d9685..a675618 100644 (file)
@@ -239,7 +239,7 @@ struct ast_json *ast_endpoint_snapshot_to_json(
                "technology", snapshot->tech,
                "resource", snapshot->resource,
                "state", ast_endpoint_state_to_string(snapshot->state),
-               "channels");
+               "channel_ids");
 
        if (json == NULL) {
                return NULL;
@@ -253,7 +253,7 @@ struct ast_json *ast_endpoint_snapshot_to_json(
                }
        }
 
-       channel_array = ast_json_object_get(json, "channels");
+       channel_array = ast_json_object_get(json, "channel_ids");
        ast_assert(channel_array != NULL);
        for (i = 0; i < snapshot->num_channels; ++i) {
                int res = ast_json_array_append(channel_array,
index c698628..1310dae 100644 (file)
@@ -83,5 +83,8 @@ $(subst .c,.o,$(wildcard parking/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_par
 res_stasis_http.so: stasis_http/ari_websockets.o
 stasis_http/ari_websockets.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_stasis_http_asterisk)
 
+res_ari_model.so: stasis_http/ari_model_validators.o
+stasis_http/ari_model_validators.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ari_model)
+
 # Dependencies for res_stasis_http_*.so are generated, so they're in this file
 include stasis_http.make
diff --git a/res/res_ari_model.c b/res/res_ari_model.c
new file mode 100644 (file)
index 0000000..fd2ec64
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Implementation Swagger validators.
+ *
+ * \author David M. Lee, II <dlee@digium.com>
+ */
+
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "stasis_http/ari_model_validators.h"
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "asterisk/utils.h"
+
+#include <regex.h>
+
+/* Regex to match date strings */
+static regex_t date_regex;
+
+/* Regex for YYYY-MM-DD */
+#define REGEX_YMD "[0-9]{4}-[01][0-9]-[0-3][0-9]"
+
+/* Regex for hh:mm(:ss(.s)); seconds and subseconds optional
+ * Handles the probably impossible case of a leap second, too */
+#define REGEX_HMS "[0-2][0-9]:[0-5][0-9](:[0-6][0-9](.[0-9]+)?)?"
+
+/* Regex for timezone: (+|-)hh(:mm), with optional colon. */
+#define REGEX_TZ "(Z|[-+][0-2][0-9](:?[0-5][0-9])?)"
+
+/* REGEX for ISO 8601, the time specifier optional */
+#define ISO8601_PATTERN "^" REGEX_YMD "(T" REGEX_HMS REGEX_TZ ")?$"
+
+static int check_type(struct ast_json *json, enum ast_json_type expected)
+{
+       enum ast_json_type actual;
+
+       if (!json) {
+               ast_log(LOG_ERROR, "Expected type %s, was NULL\n",
+                       ast_json_typename(expected));
+               return 0;
+       }
+
+       actual = ast_json_typeof(json);
+       if (expected != actual) {
+               ast_log(LOG_ERROR, "Expected type %s, was %s\n",
+                       ast_json_typename(expected), ast_json_typename(actual));
+               return 0;
+       }
+       return 1;
+}
+
+static int check_range(intmax_t minval, intmax_t maxval, struct ast_json *json)
+{
+       intmax_t v;
+
+       if (!check_type(json, AST_JSON_INTEGER)) {
+               return 0;
+       }
+
+       v = ast_json_integer_get(json);
+
+       if (v < minval || maxval < v) {
+               ast_log(LOG_ERROR, "Value out of range. Expected %jd <= %jd <= %jd\n", minval, v, maxval);
+               return 0;
+       }
+       return 1;
+}
+
+int ari_validate_void(struct ast_json *json)
+{
+       return check_type(json, AST_JSON_NULL);
+}
+
+int ari_validate_byte(struct ast_json *json)
+{
+       /* Java bytes are signed, which accounts for great fun for all */
+       return check_range(-128, 255, json);
+}
+
+int ari_validate_boolean(struct ast_json *json)
+{
+       enum ast_json_type actual = ast_json_typeof(json);
+       switch (actual) {
+       case AST_JSON_TRUE:
+       case AST_JSON_FALSE:
+               return 1;
+       default:
+               ast_log(LOG_ERROR, "Expected type boolean, was %s\n",
+                       ast_json_typename(actual));
+               return 0;
+       }
+}
+
+int ari_validate_int(struct ast_json *json)
+{
+       /* Swagger int's are 32-bit */
+       return check_range(-2147483648, 2147483647, json);
+}
+
+int ari_validate_long(struct ast_json *json)
+{
+       /* All integral values are valid longs. No need for range check. */
+       return check_type(json, AST_JSON_INTEGER);
+}
+
+int ari_validate_float(struct ast_json *json)
+{
+       return check_type(json, AST_JSON_REAL);
+}
+
+int ari_validate_double(struct ast_json *json)
+{
+       return check_type(json, AST_JSON_REAL);
+}
+
+int ari_validate_string(struct ast_json *json)
+{
+       return check_type(json, AST_JSON_STRING);
+}
+
+int ari_validate_date(struct ast_json *json)
+{
+       /* Dates are ISO-8601 strings */
+       const char *str;
+       if (!check_type(json, AST_JSON_STRING)) {
+               return 0;
+       }
+       str = ast_json_string_get(json);
+       ast_assert(str != NULL);
+       if (regexec(&date_regex, str, 0, NULL, 0) != 0) {
+               ast_log(LOG_ERROR, "Date field is malformed: '%s'\n", str);
+               return 0;
+       }
+       return 1;
+}
+
+int ari_validate_list(struct ast_json *json, int (*fn)(struct ast_json *))
+{
+       int res = 1;
+       size_t i;
+
+       if (!check_type(json, AST_JSON_ARRAY)) {
+               return 0;
+       }
+
+       for (i = 0; i < ast_json_array_size(json); ++i) {
+               int member_res;
+               member_res = fn(ast_json_array_get(json, i));
+               if (!member_res) {
+                       ast_log(LOG_ERROR,
+                               "Array member %zd failed validation\n", i);
+                       res = 0;
+               }
+       }
+
+       return res;
+}
+
+static int load_module(void)
+{
+       int res;
+       res = regcomp(&date_regex, ISO8601_PATTERN,
+               REG_EXTENDED | REG_ICASE | REG_NOSUB);
+
+       if (res != 0) {
+               return AST_MODULE_LOAD_FAILURE;
+       }
+       return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+       regfree(&date_regex);
+       return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY,
+       AST_MODFLAG_LOAD_ORDER | AST_MODFLAG_GLOBAL_SYMBOLS,
+       "ARI Model validators",
+       .load = load_module,
+       .unload = unload_module,
+       .load_pri = AST_MODPRI_APP_DEPEND,
+        );
diff --git a/res/res_ari_model.exports.in b/res/res_ari_model.exports.in
new file mode 100644 (file)
index 0000000..160e23f
--- /dev/null
@@ -0,0 +1,6 @@
+{
+       global:
+               LINKER_SYMBOL_PREFIXari_*;
+       local:
+               *;
+};
index de432e4..ed38230 100644 (file)
@@ -48,7 +48,6 @@
  */
 
 /*** MODULEINFO
-       <depend>res_stasis_json_events</depend>
        <support_level>core</support_level>
  ***/
 
@@ -66,7 +65,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/strings.h"
 #include "stasis/app.h"
 #include "stasis/control.h"
-#include "stasis_json/resource_events.h"
 
 /*! Time to wait for a frame in the application */
 #define MAX_WAIT_MS 200
@@ -233,28 +231,60 @@ static struct ao2_container *get_apps_watching_channel(const char *uniqueid)
 /*! \brief Typedef for callbacks that get called on channel snapshot updates */
 typedef struct ast_json *(*channel_snapshot_monitor)(
        struct ast_channel_snapshot *old_snapshot,
-       struct ast_channel_snapshot *new_snapshot);
+       struct ast_channel_snapshot *new_snapshot,
+       const struct timeval *tv);
+
+static struct ast_json *simple_channel_event(
+       const char *type,
+       struct ast_channel_snapshot *snapshot,
+       const struct timeval *tv)
+{
+       return ast_json_pack("{s: s, s: o, s: o}",
+               "type", type,
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "channel", ast_channel_snapshot_to_json(snapshot));
+}
+
+static struct ast_json *channel_created_event(
+       struct ast_channel_snapshot *snapshot,
+       const struct timeval *tv)
+{
+       return simple_channel_event("ChannelCreated", snapshot, tv);
+}
+
+static struct ast_json *channel_destroyed_event(
+       struct ast_channel_snapshot *snapshot,
+       const struct timeval *tv)
+{
+       return ast_json_pack("{s: s, s: o, s: i, s: s, s: o}",
+               "type", "ChannelDestroyed",
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "cause", snapshot->hangupcause,
+               "cause_txt", ast_cause2str(snapshot->hangupcause),
+               "channel", ast_channel_snapshot_to_json(snapshot));
+}
+
+static struct ast_json *channel_state_change_event(
+       struct ast_channel_snapshot *snapshot,
+       const struct timeval *tv)
+{
+       return simple_channel_event("ChannelStateChange", snapshot, tv);
+}
 
 /*! \brief Handle channel state changes */
 static struct ast_json *channel_state(
        struct ast_channel_snapshot *old_snapshot,
-       struct ast_channel_snapshot *new_snapshot)
+       struct ast_channel_snapshot *new_snapshot,
+       const struct timeval *tv)
 {
-       RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
        struct ast_channel_snapshot *snapshot = new_snapshot ? new_snapshot : old_snapshot;
 
        if (!old_snapshot) {
-               return stasis_json_event_channel_created_create(snapshot);
+               return channel_created_event(snapshot, tv);
        } else if (!new_snapshot) {
-               json = ast_json_pack("{s: i, s: s}",
-                       "cause", snapshot->hangupcause,
-                       "cause_txt", ast_cause2str(snapshot->hangupcause));
-               if (!json) {
-                       return NULL;
-               }
-               return stasis_json_event_channel_destroyed_create(snapshot, json);
+               return channel_destroyed_event(snapshot, tv);
        } else if (old_snapshot->state != new_snapshot->state) {
-               return stasis_json_event_channel_state_change_create(snapshot);
+               return channel_state_change_event(snapshot, tv);
        }
 
        return NULL;
@@ -262,7 +292,8 @@ static struct ast_json *channel_state(
 
 static struct ast_json *channel_dialplan(
        struct ast_channel_snapshot *old_snapshot,
-       struct ast_channel_snapshot *new_snapshot)
+       struct ast_channel_snapshot *new_snapshot,
+       const struct timeval *tv)
 {
        RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
 
@@ -280,19 +311,18 @@ static struct ast_json *channel_dialplan(
                return NULL;
        }
 
-       json = ast_json_pack("{s: s, s: s}",
-               "application", new_snapshot->appl,
-               "application_data", new_snapshot->data);
-       if (!json) {
-               return NULL;
-       }
-
-       return stasis_json_event_channel_dialplan_create(new_snapshot, json);
+       return ast_json_pack("{s: s, s: o, s: s, s: s, s: o}",
+               "type", "ChannelDialplan",
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "dialplan_app", new_snapshot->appl,
+               "dialplan_app_data", new_snapshot->data,
+               "channel", ast_channel_snapshot_to_json(new_snapshot));
 }
 
 static struct ast_json *channel_callerid(
        struct ast_channel_snapshot *old_snapshot,
-       struct ast_channel_snapshot *new_snapshot)
+       struct ast_channel_snapshot *new_snapshot,
+       const struct timeval *tv)
 {
        RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
 
@@ -305,29 +335,16 @@ static struct ast_json *channel_callerid(
                return NULL;
        }
 
-       json = ast_json_pack("{s: i, s: s}",
+       return ast_json_pack("{s: s, s: o, s: i, s: s, s: o}",
+               "type", "ChannelCallerId",
+               "timestamp", ast_json_timeval(*tv, NULL),
                "caller_presentation", new_snapshot->caller_pres,
-               "caller_presentation_txt", ast_describe_caller_presentation(new_snapshot->caller_pres));
-       if (!json) {
-               return NULL;
-       }
-
-       return stasis_json_event_channel_caller_id_create(new_snapshot, json);
-}
-
-static struct ast_json *channel_snapshot(
-       struct ast_channel_snapshot *old_snapshot,
-       struct ast_channel_snapshot *new_snapshot)
-{
-       if (!new_snapshot) {
-               return NULL;
-       }
-
-       return stasis_json_event_channel_snapshot_create(new_snapshot);
+               "caller_presentation_txt", ast_describe_caller_presentation(
+                       new_snapshot->caller_pres),
+               "channel", ast_channel_snapshot_to_json(new_snapshot));
 }
 
 channel_snapshot_monitor channel_monitors[] = {
-       channel_snapshot,
        channel_state,
        channel_dialplan,
        channel_callerid
@@ -351,6 +368,9 @@ static void sub_channel_snapshot_handler(void *data,
        struct stasis_cache_update *update = stasis_message_data(message);
        struct ast_channel_snapshot *new_snapshot = stasis_message_data(update->new_snapshot);
        struct ast_channel_snapshot *old_snapshot = stasis_message_data(update->old_snapshot);
+       /* Pull timestamp from the new snapshot, or from the update message
+        * when there isn't one. */
+       const struct timeval *tv = update->new_snapshot ? stasis_message_timestamp(update->new_snapshot) : stasis_message_timestamp(message);
        int i;
 
        watching_apps = get_apps_watching_channel(new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid);
@@ -361,7 +381,7 @@ static void sub_channel_snapshot_handler(void *data,
        for (i = 0; i < ARRAY_LEN(channel_monitors); ++i) {
                RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 
-               msg = channel_monitors[i](old_snapshot, new_snapshot);
+               msg = channel_monitors[i](old_snapshot, new_snapshot, tv);
                if (msg) {
                        ao2_callback(watching_apps, OBJ_NODATA, app_send_cb, msg);
                }
@@ -373,22 +393,26 @@ static void distribute_message(struct ao2_container *apps, struct ast_json *msg)
        ao2_callback(apps, OBJ_NODATA, app_send_cb, msg);
 }
 
-static void generic_blob_handler(struct ast_channel_blob *obj, channel_blob_handler_cb handler_cb)
+static void sub_channel_blob_handler(void *data,
+               struct stasis_subscription *sub,
+               struct stasis_topic *topic,
+               struct stasis_message *message)
 {
        RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
        RAII_VAR(struct ao2_container *, watching_apps, NULL, ao2_cleanup);
+       struct ast_channel_blob *obj = stasis_message_data(message);
 
        if (!obj->snapshot) {
                return;
        }
 
-       watching_apps = get_apps_watching_channel(obj->snapshot->uniqueid);
-       if (!watching_apps) {
+       msg = stasis_message_to_json(message);
+       if (!msg) {
                return;
        }
 
-       msg = handler_cb(obj);
-       if (!msg) {
+       watching_apps = get_apps_watching_channel(obj->snapshot->uniqueid);
+       if (!watching_apps) {
                return;
        }
 
@@ -446,7 +470,6 @@ int app_send_start_msg(struct app *app, struct ast_channel *chan,
        int argc, char *argv[])
 {
        RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
        RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
 
        struct ast_json *json_args;
@@ -460,13 +483,16 @@ int app_send_start_msg(struct app *app, struct ast_channel *chan,
                return -1;
        }
 
-       blob = ast_json_pack("{s: []}", "args");
-       if (!blob) {
+       msg = ast_json_pack("{s: s, s: [], s: o}",
+               "type", "StasisStart",
+               "args",
+               "channel", ast_channel_snapshot_to_json(snapshot));
+       if (!msg) {
                return -1;
        }
 
        /* Append arguments to args array */
-       json_args = ast_json_object_get(blob, "args");
+       json_args = ast_json_object_get(msg, "args");
        ast_assert(json_args != NULL);
        for (i = 0; i < argc; ++i) {
                int r = ast_json_array_append(json_args,
@@ -477,11 +503,6 @@ int app_send_start_msg(struct app *app, struct ast_channel *chan,
                }
        }
 
-       msg = stasis_json_event_stasis_start_create(snapshot, blob);
-       if (!msg) {
-               return -1;
-       }
-
        app_send(app, msg);
        return 0;
 }
@@ -499,7 +520,9 @@ int app_send_end_msg(struct app *app, struct ast_channel *chan)
                return -1;
        }
 
-       msg = stasis_json_event_stasis_end_create(snapshot);
+       msg = ast_json_pack("{s: s, s: o}",
+               "type", "StasisEnd",
+               "channel", ast_channel_snapshot_to_json(snapshot));
        if (!msg) {
                return -1;
        }
@@ -633,15 +656,13 @@ int stasis_app_register(const char *app_name, stasis_app_cb handler, void *data)
        app = ao2_find(apps_registry, app_name, OBJ_KEY | OBJ_NOLOCK);
 
        if (app) {
-               RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
                RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 
-               blob = ast_json_pack("{s: s}", "application", app_name);
-               if (blob) {
-                       msg = stasis_json_event_application_replaced_create(blob);
-                       if (msg) {
-                               app_send(app, msg);
-                       }
+               msg = ast_json_pack("{s: s, s: s}",
+                       "type", "ApplicationReplaced",
+                       "application", app_name);
+               if (msg) {
+                       app_send(app, msg);
                }
 
                app_update(app, handler, data);
@@ -665,82 +686,6 @@ void stasis_app_unregister(const char *app_name)
        }
 }
 
-static struct ast_json *handle_blob_dtmf(struct ast_channel_blob *obj)
-{
-       RAII_VAR(struct ast_json *, extra, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
-       const char *direction;
-
-       /* To simplify events, we'll only generate on receive */
-       direction = ast_json_string_get(
-               ast_json_object_get(obj->blob, "direction"));
-
-       if (strcmp("Received", direction) != 0) {
-               return NULL;
-       }
-
-       extra = ast_json_pack(
-               "{s: o}",
-               "digit", ast_json_ref(ast_json_object_get(obj->blob, "digit")));
-       if (!extra) {
-               return NULL;
-       }
-
-       return stasis_json_event_channel_dtmf_received_create(obj->snapshot, extra);
-}
-
-/* To simplify events, we'll only generate on DTMF end (dtmf_end type) */
-static void sub_dtmf_handler(void *data,
-               struct stasis_subscription *sub,
-               struct stasis_topic *topic,
-               struct stasis_message *message)
-{
-       struct ast_channel_blob *obj = stasis_message_data(message);
-       generic_blob_handler(obj, handle_blob_dtmf);
-}
-
-static struct ast_json *handle_blob_userevent(struct ast_channel_blob *obj)
-{
-       return stasis_json_event_channel_userevent_create(obj->snapshot, obj->blob);
-}
-
-static void sub_userevent_handler(void *data,
-               struct stasis_subscription *sub,
-               struct stasis_topic *topic,
-               struct stasis_message *message)
-{
-       struct ast_channel_blob *obj = stasis_message_data(message);
-       generic_blob_handler(obj, handle_blob_userevent);
-}
-
-static struct ast_json *handle_blob_hangup_request(struct ast_channel_blob *obj)
-{
-       return stasis_json_event_channel_hangup_request_create(obj->snapshot, obj->blob);
-}
-
-static void sub_hangup_request_handler(void *data,
-               struct stasis_subscription *sub,
-               struct stasis_topic *topic,
-               struct stasis_message *message)
-{
-       struct ast_channel_blob *obj = stasis_message_data(message);
-       generic_blob_handler(obj, handle_blob_hangup_request);
-}
-
-static struct ast_json *handle_blob_varset(struct ast_channel_blob *obj)
-{
-       return stasis_json_event_channel_varset_create(obj->snapshot, obj->blob);
-}
-
-static void sub_varset_handler(void *data,
-               struct stasis_subscription *sub,
-               struct stasis_topic *topic,
-               struct stasis_message *message)
-{
-       struct ast_channel_blob *obj = stasis_message_data(message);
-       generic_blob_handler(obj, handle_blob_varset);
-}
-
 void stasis_app_ref(void)
 {
        ast_module_ref(ast_module_info->self);
@@ -788,6 +733,30 @@ static int remove_bridge_cb(void *obj, void *arg, int flags)
        return 0;
 }
 
+static struct ast_json *simple_bridge_event(
+       const char *type,
+       struct ast_bridge_snapshot *snapshot,
+       const struct timeval *tv)
+{
+       return ast_json_pack("{s: s, s: o, s: o}",
+               "type", type,
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "bridge", ast_bridge_snapshot_to_json(snapshot));
+}
+
+static struct ast_json *simple_bridge_channel_event(
+       const char *type,
+       struct ast_bridge_snapshot *bridge_snapshot,
+       struct ast_channel_snapshot *channel_snapshot,
+       const struct timeval *tv)
+{
+       return ast_json_pack("{s: s, s: o, s: o}",
+               "type", type,
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "bridge", ast_bridge_snapshot_to_json(bridge_snapshot),
+               "channel", ast_channel_snapshot_to_json(channel_snapshot));
+}
+
 static void sub_bridge_snapshot_handler(void *data,
                struct stasis_subscription *sub,
                struct stasis_topic *topic,
@@ -797,6 +766,8 @@ static void sub_bridge_snapshot_handler(void *data,
        struct stasis_cache_update *update = stasis_message_data(message);
        struct ast_bridge_snapshot *new_snapshot = stasis_message_data(update->new_snapshot);
        struct ast_bridge_snapshot *old_snapshot = stasis_message_data(update->old_snapshot);
+       const struct timeval *tv = update->new_snapshot ? stasis_message_timestamp(update->new_snapshot) : stasis_message_timestamp(message);
+
        RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
 
        watching_apps = get_apps_watching_bridge(new_snapshot ? new_snapshot->uniqueid : old_snapshot->uniqueid);
@@ -810,11 +781,11 @@ static void sub_bridge_snapshot_handler(void *data,
                /* The bridge has gone away. Create the message, make sure no apps are
                 * watching this bridge anymore, and destroy the bridge's control
                 * structure */
-               msg = stasis_json_event_bridge_destroyed_create(old_snapshot);
+               msg = simple_bridge_event("BridgeDestroyed", old_snapshot, tv);
                ao2_callback(watching_apps, OBJ_NODATA, remove_bridge_cb, bridge_id);
                stasis_app_bridge_destroy(old_snapshot->uniqueid);
        } else if (!old_snapshot) {
-               msg = stasis_json_event_bridge_created_create(old_snapshot);
+               msg = simple_bridge_event("BridgeCreated", old_snapshot, tv);
        }
 
        if (!msg) {
@@ -865,6 +836,7 @@ static void sub_bridge_merge_handler(void *data,
        struct ast_bridge_merge_message *merge = stasis_message_data(message);
        RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
        RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+       const struct timeval *tv = stasis_message_timestamp(message);
 
        watching_apps_to = get_apps_watching_bridge(merge->to->uniqueid);
        if (watching_apps_to) {
@@ -881,16 +853,16 @@ static void sub_bridge_merge_handler(void *data,
                return;
        }
 
-       /* The secondary bridge has to be packed into JSON by hand because the auto-generated
-        * JSON event generator can only handle one instance of a given snapshot type in an
-        * elegant way */
-       blob = ast_json_pack("{s: o}", "bridge_from", ast_bridge_snapshot_to_json(merge->from));
-       if (!blob) {
+       msg = ast_json_pack("{s: s, s: o, s: o, s: o}",
+               "type", "BridgeMerged",
+               "timestamp", ast_json_timeval(*tv, NULL),
+               "bridge", ast_bridge_snapshot_to_json(merge->to),
+               "bridge_from", ast_bridge_snapshot_to_json(merge->from));
+
+       if (!msg) {
                return;
        }
 
-       msg = stasis_json_event_bridge_merged_create(merge->to, blob);
-
        distribute_message(watching_apps_all, msg);
 }
 
@@ -920,7 +892,8 @@ static void sub_bridge_enter_handler(void *data,
                return;
        }
 
-       msg = stasis_json_event_channel_entered_bridge_create(obj->bridge, obj->channel);
+       msg = simple_bridge_channel_event("ChannelEnteredBridge", obj->bridge,
+               obj->channel, stasis_message_timestamp(message));
 
        distribute_message(watching_apps_all, msg);
 }
@@ -939,7 +912,8 @@ static void sub_bridge_leave_handler(void *data,
                return;
        }
 
-       msg = stasis_json_event_channel_left_bridge_create(obj->bridge, obj->channel);
+       msg = simple_bridge_channel_event("ChannelLeftBridge", obj->bridge,
+               obj->channel, stasis_message_timestamp(message));
 
        distribute_message(watching_apps_bridge, msg);
 }
@@ -972,10 +946,16 @@ static int load_module(void)
        }
 
        r |= stasis_message_router_add(channel_router, stasis_cache_update_type(), sub_channel_snapshot_handler, NULL);
-       r |= stasis_message_router_add(channel_router, ast_channel_user_event_type(), sub_userevent_handler, NULL);
-       r |= stasis_message_router_add(channel_router, ast_channel_varset_type(), sub_varset_handler, NULL);
-       r |= stasis_message_router_add(channel_router, ast_channel_dtmf_begin_type(), sub_dtmf_handler, NULL);
-       r |= stasis_message_router_add(channel_router, ast_channel_hangup_request_type(), sub_hangup_request_handler, NULL);
+       /* TODO: This could be handled a lot better. Instead of subscribing to
+        * the one caching topic and filtering out messages by channel id, we
+        * should have individual caching topics per-channel, with a shared
+        * back-end cache. That would simplify a lot of what's going on right
+        * here.
+        */
+       r |= stasis_message_router_add(channel_router, ast_channel_user_event_type(), sub_channel_blob_handler, NULL);
+       r |= stasis_message_router_add(channel_router, ast_channel_varset_type(), sub_channel_blob_handler, NULL);
+       r |= stasis_message_router_add(channel_router, ast_channel_dtmf_end_type(), sub_channel_blob_handler, NULL);
+       r |= stasis_message_router_add(channel_router, ast_channel_hangup_request_type(), sub_channel_blob_handler, NULL);
        if (r) {
                return AST_MODULE_LOAD_FAILURE;
        }
index fce1081..3ff6482 100644 (file)
@@ -324,7 +324,7 @@ void stasis_http_response_ok(struct stasis_http_response *response,
 
 void stasis_http_response_no_content(struct stasis_http_response *response)
 {
-       response->message = NULL;
+       response->message = ast_json_null();
        response->response_code = 204;
        response->response_text = "No Content";
 }
@@ -386,9 +386,7 @@ static void handle_options(struct stasis_rest_handlers *handler,
 
        /* Regular OPTIONS response */
        add_allow_header(handler, response);
-       response->response_code = 204;
-       response->response_text = "No Content";
-       response->message = NULL;
+       stasis_http_response_no_content(response);
 
        /* Parse CORS headers */
        for (header = headers; header != NULL; header = header->next) {
@@ -797,6 +795,11 @@ static void process_cors_request(struct ast_variable *headers,
         */
 }
 
+enum ast_json_encoding_format stasis_http_json_format(void)
+{
+       RAII_VAR(struct conf *, cfg, ao2_global_obj_ref(confs), ao2_cleanup);
+       return cfg->global->format;
+}
 
 /*!
  * \internal
@@ -819,7 +822,6 @@ static int stasis_http_callback(struct ast_tcptls_session_instance *ser,
                                struct ast_variable *get_params,
                                struct ast_variable *headers)
 {
-       RAII_VAR(struct conf *, cfg, ao2_global_obj_ref(confs), ao2_cleanup);
        RAII_VAR(struct ast_str *, response_headers, ast_str_create(40), ast_free);
        RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
        struct stasis_http_response response = {};
@@ -859,11 +861,10 @@ static int stasis_http_callback(struct ast_tcptls_session_instance *ser,
                return 0;
        }
 
-       /* Leaving message unset is only allowed for 204 (No Content).
-        * If you explicitly want to have no content for a different return
-        * code, set message to ast_json_null().
+       /* If you explicitly want to have no content, set message to
+        * ast_json_null().
         */
-       ast_assert(response.response_code == 204 || response.message != NULL);
+       ast_assert(response.message != NULL);
        ast_assert(response.response_code > 0);
 
        ast_str_append(&response_headers, 0, "%s", ast_str_buffer(response.headers));
@@ -874,7 +875,7 @@ static int stasis_http_callback(struct ast_tcptls_session_instance *ser,
        if (response.message && !ast_json_is_null(response.message)) {
                ast_str_append(&response_headers, 0,
                               "Content-type: application/json\r\n");
-               if (ast_json_dump_str_format(response.message, &response_body, cfg->global->format) != 0) {
+               if (ast_json_dump_str_format(response.message, &response_body, stasis_http_json_format()) != 0) {
                        /* Error encoding response */
                        response.response_code = 500;
                        response.response_text = "Internal Server Error";
index 9f4fd63..01f082a 100644 (file)
@@ -44,6 +44,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app.h"
 #include "stasis_http/resource_asterisk.h"
+#if defined(AST_DEVMODE)
+#include "stasis_http/ari_model_validators.h"
+#endif
 
 /*!
  * \brief Parameter parsing callback for /asterisk/info.
@@ -53,9 +56,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_asterisk_info_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_asterisk_info_args args = {};
        struct ast_variable *i;
 
@@ -66,6 +74,29 @@ static void stasis_http_get_asterisk_info_cb(
                {}
        }
        stasis_http_get_asterisk_info(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_asterisk_info(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/info\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /asterisk/info\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 
 /*! \brief REST handler for /api-docs/asterisk.{format} */
index 717b2f8..a4801df 100644 (file)
@@ -44,6 +44,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app.h"
 #include "stasis_http/resource_bridges.h"
+#if defined(AST_DEVMODE)
+#include "stasis_http/ari_model_validators.h"
+#endif
 
 /*!
  * \brief Parameter parsing callback for /bridges.
@@ -53,11 +56,39 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_bridges_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_bridges_args args = {};
        stasis_http_get_bridges(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_list(response->message,
+                               ari_validate_bridge);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /bridges\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /bridges\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /bridges.
@@ -67,9 +98,14 @@ static void stasis_http_get_bridges_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_new_bridge_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_new_bridge_args args = {};
        struct ast_variable *i;
 
@@ -80,6 +116,29 @@ static void stasis_http_new_bridge_cb(
                {}
        }
        stasis_http_new_bridge(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_bridge(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /bridges\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /bridges\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /bridges/{bridgeId}.
@@ -89,9 +148,14 @@ static void stasis_http_new_bridge_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_bridge_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_bridge_args args = {};
        struct ast_variable *i;
 
@@ -102,6 +166,30 @@ static void stasis_http_get_bridge_cb(
                {}
        }
        stasis_http_get_bridge(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Bridge not found */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_bridge(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /bridges/{bridgeId}.
@@ -111,9 +199,14 @@ static void stasis_http_get_bridge_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_delete_bridge_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_delete_bridge_args args = {};
        struct ast_variable *i;
 
@@ -124,6 +217,30 @@ static void stasis_http_delete_bridge_cb(
                {}
        }
        stasis_http_delete_bridge(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Bridge not found */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /bridges/{bridgeId}/addChannel.
@@ -133,9 +250,14 @@ static void stasis_http_delete_bridge_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_add_channel_to_bridge_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_add_channel_to_bridge_args args = {};
        struct ast_variable *i;
 
@@ -152,6 +274,32 @@ static void stasis_http_add_channel_to_bridge_cb(
                {}
        }
        stasis_http_add_channel_to_bridge(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Bridge not found */
+       case 409: /* Bridge not in Stasis application */
+       case 422: /* Channel not found, or not in Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/addChannel\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/addChannel\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /bridges/{bridgeId}/removeChannel.
@@ -161,9 +309,14 @@ static void stasis_http_add_channel_to_bridge_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_remove_channel_from_bridge_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_remove_channel_from_bridge_args args = {};
        struct ast_variable *i;
 
@@ -180,6 +333,29 @@ static void stasis_http_remove_channel_from_bridge_cb(
                {}
        }
        stasis_http_remove_channel_from_bridge(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/removeChannel\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/removeChannel\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /bridges/{bridgeId}/record.
@@ -189,9 +365,14 @@ static void stasis_http_remove_channel_from_bridge_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_record_bridge_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_record_bridge_args args = {};
        struct ast_variable *i;
 
@@ -223,6 +404,29 @@ static void stasis_http_record_bridge_cb(
                {}
        }
        stasis_http_record_bridge(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_live_recording(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/record\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/record\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 
 /*! \brief REST handler for /api-docs/bridges.{format} */
index c865b39..ebcc9e8 100644 (file)
@@ -44,6 +44,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app.h"
 #include "stasis_http/resource_channels.h"
+#if defined(AST_DEVMODE)
+#include "stasis_http/ari_model_validators.h"
+#endif
 
 /*!
  * \brief Parameter parsing callback for /channels.
@@ -53,11 +56,39 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_channels_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_channels_args args = {};
        stasis_http_get_channels(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_list(response->message,
+                               ari_validate_channel);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels.
@@ -67,9 +98,14 @@ static void stasis_http_get_channels_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_originate_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_originate_args args = {};
        struct ast_variable *i;
 
@@ -101,6 +137,29 @@ static void stasis_http_originate_cb(
                {}
        }
        stasis_http_originate(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}.
@@ -110,9 +169,14 @@ static void stasis_http_originate_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_channel_args args = {};
        struct ast_variable *i;
 
@@ -123,6 +187,30 @@ static void stasis_http_get_channel_cb(
                {}
        }
        stasis_http_get_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_channel(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}.
@@ -132,9 +220,14 @@ static void stasis_http_get_channel_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_delete_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_delete_channel_args args = {};
        struct ast_variable *i;
 
@@ -145,6 +238,30 @@ static void stasis_http_delete_channel_cb(
                {}
        }
        stasis_http_delete_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/dial.
@@ -154,9 +271,14 @@ static void stasis_http_delete_channel_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_dial_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_dial_args args = {};
        struct ast_variable *i;
 
@@ -182,6 +304,31 @@ static void stasis_http_dial_cb(
                {}
        }
        stasis_http_dial(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel not in a Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_dialed(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/dial\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/dial\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/continue.
@@ -191,9 +338,14 @@ static void stasis_http_dial_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_continue_in_dialplan_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_continue_in_dialplan_args args = {};
        struct ast_variable *i;
 
@@ -216,6 +368,31 @@ static void stasis_http_continue_in_dialplan_cb(
                {}
        }
        stasis_http_continue_in_dialplan(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel not in a Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/continue\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/continue\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/answer.
@@ -225,9 +402,14 @@ static void stasis_http_continue_in_dialplan_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_answer_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_answer_channel_args args = {};
        struct ast_variable *i;
 
@@ -238,6 +420,31 @@ static void stasis_http_answer_channel_cb(
                {}
        }
        stasis_http_answer_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel not in a Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/answer\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/answer\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/mute.
@@ -247,9 +454,14 @@ static void stasis_http_answer_channel_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_mute_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_mute_channel_args args = {};
        struct ast_variable *i;
 
@@ -266,6 +478,31 @@ static void stasis_http_mute_channel_cb(
                {}
        }
        stasis_http_mute_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel not in a Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/mute\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/mute\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/unmute.
@@ -275,9 +512,14 @@ static void stasis_http_mute_channel_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_unmute_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_unmute_channel_args args = {};
        struct ast_variable *i;
 
@@ -294,6 +536,31 @@ static void stasis_http_unmute_channel_cb(
                {}
        }
        stasis_http_unmute_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel not in a Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/unmute\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/unmute\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/hold.
@@ -303,9 +570,14 @@ static void stasis_http_unmute_channel_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_hold_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_hold_channel_args args = {};
        struct ast_variable *i;
 
@@ -316,6 +588,31 @@ static void stasis_http_hold_channel_cb(
                {}
        }
        stasis_http_hold_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel not in a Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/hold\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/hold\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/unhold.
@@ -325,9 +622,14 @@ static void stasis_http_hold_channel_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_unhold_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_unhold_channel_args args = {};
        struct ast_variable *i;
 
@@ -338,6 +640,31 @@ static void stasis_http_unhold_channel_cb(
                {}
        }
        stasis_http_unhold_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel not in a Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/unhold\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/unhold\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/play.
@@ -347,9 +674,14 @@ static void stasis_http_unhold_channel_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_play_on_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_play_on_channel_args args = {};
        struct ast_variable *i;
 
@@ -375,6 +707,31 @@ static void stasis_http_play_on_channel_cb(
                {}
        }
        stasis_http_play_on_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel not in a Stasis application */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_playback(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/play\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/play\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /channels/{channelId}/record.
@@ -384,9 +741,14 @@ static void stasis_http_play_on_channel_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_record_channel_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_record_channel_args args = {};
        struct ast_variable *i;
 
@@ -421,6 +783,31 @@ static void stasis_http_record_channel_cb(
                {}
        }
        stasis_http_record_channel(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 404: /* Channel not found */
+       case 409: /* Channel is not in a Stasis application, or the channel is currently bridged with other channels. */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/record\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/record\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 
 /*! \brief REST handler for /api-docs/channels.{format} */
index 81cdfeb..3323330 100644 (file)
@@ -44,6 +44,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app.h"
 #include "stasis_http/resource_endpoints.h"
+#if defined(AST_DEVMODE)
+#include "stasis_http/ari_model_validators.h"
+#endif
 
 /*!
  * \brief Parameter parsing callback for /endpoints.
@@ -53,11 +56,39 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_endpoints_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_endpoints_args args = {};
        stasis_http_get_endpoints(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_list(response->message,
+                               ari_validate_endpoint);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /endpoints\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /endpoints\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /endpoints/{tech}.
@@ -67,9 +98,14 @@ static void stasis_http_get_endpoints_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_endpoints_by_tech_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_endpoints_by_tech_args args = {};
        struct ast_variable *i;
 
@@ -80,6 +116,29 @@ static void stasis_http_get_endpoints_by_tech_cb(
                {}
        }
        stasis_http_get_endpoints_by_tech(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_list(response->message,
+                               ari_validate_endpoint);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /endpoints/{tech}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /endpoints/{tech}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /endpoints/{tech}/{resource}.
@@ -89,9 +148,14 @@ static void stasis_http_get_endpoints_by_tech_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_endpoint_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_endpoint_args args = {};
        struct ast_variable *i;
 
@@ -105,6 +169,29 @@ static void stasis_http_get_endpoint_cb(
                {}
        }
        stasis_http_get_endpoint(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_endpoint(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /endpoints/{tech}/{resource}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /endpoints/{tech}/{resource}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 
 /*! \brief REST handler for /api-docs/endpoints.{format} */
index 909c2d6..4217263 100644 (file)
@@ -44,6 +44,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app.h"
 #include "stasis_http/resource_events.h"
+#if defined(AST_DEVMODE)
+#include "stasis_http/ari_model_validators.h"
+#endif
 
 static void stasis_http_event_websocket_ws_cb(struct ast_websocket *ws_session,
        struct ast_variable *get_params, struct ast_variable *headers)
@@ -59,7 +62,12 @@ static void stasis_http_event_websocket_ws_cb(struct ast_websocket *ws_session,
                } else
                {}
        }
-       session = ari_websocket_session_create(ws_session);
+#if defined(AST_DEVMODE)
+       session = ari_websocket_session_create(ws_session,
+               ari_validate_event);
+#else
+       session = ari_websocket_session_create(ws_session, NULL);
+#endif
        if (!session) {
                ast_log(LOG_ERROR, "Failed to create ARI session\n");
                return;
index 4608686..0e56e62 100644 (file)
@@ -44,6 +44,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app.h"
 #include "stasis_http/resource_playback.h"
+#if defined(AST_DEVMODE)
+#include "stasis_http/ari_model_validators.h"
+#endif
 
 /*!
  * \brief Parameter parsing callback for /playback/{playbackId}.
@@ -53,9 +56,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_playback_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_playback_args args = {};
        struct ast_variable *i;
 
@@ -66,6 +74,29 @@ static void stasis_http_get_playback_cb(
                {}
        }
        stasis_http_get_playback(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_playback(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /playback/{playbackId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /playback/{playbackId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /playback/{playbackId}.
@@ -75,9 +106,14 @@ static void stasis_http_get_playback_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_stop_playback_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_stop_playback_args args = {};
        struct ast_variable *i;
 
@@ -88,6 +124,29 @@ static void stasis_http_stop_playback_cb(
                {}
        }
        stasis_http_stop_playback(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_playback(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /playback/{playbackId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /playback/{playbackId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /playback/{playbackId}/control.
@@ -97,9 +156,14 @@ static void stasis_http_stop_playback_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_control_playback_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_control_playback_args args = {};
        struct ast_variable *i;
 
@@ -116,6 +180,32 @@ static void stasis_http_control_playback_cb(
                {}
        }
        stasis_http_control_playback(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+       case 400: /* The provided operation parameter was invalid */
+       case 404: /* The playback cannot be found */
+       case 409: /* The operation cannot be performed in the playback's current state */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_playback(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /playback/{playbackId}/control\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /playback/{playbackId}/control\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 
 /*! \brief REST handler for /api-docs/playback.{format} */
index 7d89393..4aa43c9 100644 (file)
@@ -44,22 +44,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app.h"
 #include "stasis_http/resource_recordings.h"
+#if defined(AST_DEVMODE)
+#include "stasis_http/ari_model_validators.h"
+#endif
 
 /*!
- * \brief Parameter parsing callback for /recordings.
- * \param get_params GET parameters in the HTTP request.
- * \param path_vars Path variables extracted from the request.
- * \param headers HTTP headers.
- * \param[out] response Response to the HTTP request.
- */
-static void stasis_http_get_recordings_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
-{
-       struct ast_get_recordings_args args = {};
-       stasis_http_get_recordings(headers, &args, response);
-}
-/*!
  * \brief Parameter parsing callback for /recordings/stored.
  * \param get_params GET parameters in the HTTP request.
  * \param path_vars Path variables extracted from the request.
@@ -67,11 +56,39 @@ static void stasis_http_get_recordings_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_stored_recordings_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_stored_recordings_args args = {};
        stasis_http_get_stored_recordings(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_list(response->message,
+                               ari_validate_stored_recording);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/stored\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/stored\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/stored/{recordingId}.
@@ -81,9 +98,14 @@ static void stasis_http_get_stored_recordings_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_stored_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_stored_recording_args args = {};
        struct ast_variable *i;
 
@@ -94,6 +116,29 @@ static void stasis_http_get_stored_recording_cb(
                {}
        }
        stasis_http_get_stored_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_stored_recording(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/stored/{recordingId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/stored/{recordingId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/stored/{recordingId}.
@@ -103,9 +148,14 @@ static void stasis_http_get_stored_recording_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_delete_stored_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_delete_stored_recording_args args = {};
        struct ast_variable *i;
 
@@ -116,6 +166,29 @@ static void stasis_http_delete_stored_recording_cb(
                {}
        }
        stasis_http_delete_stored_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/stored/{recordingId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/stored/{recordingId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/live.
@@ -125,11 +198,39 @@ static void stasis_http_delete_stored_recording_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_live_recordings_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_live_recordings_args args = {};
        stasis_http_get_live_recordings(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_list(response->message,
+                               ari_validate_live_recording);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/live\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/live\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/live/{recordingId}.
@@ -139,9 +240,14 @@ static void stasis_http_get_live_recordings_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_live_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_live_recording_args args = {};
        struct ast_variable *i;
 
@@ -152,6 +258,29 @@ static void stasis_http_get_live_recording_cb(
                {}
        }
        stasis_http_get_live_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_live_recording(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/live/{recordingId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/live/{recordingId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/live/{recordingId}.
@@ -161,9 +290,14 @@ static void stasis_http_get_live_recording_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_cancel_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_cancel_recording_args args = {};
        struct ast_variable *i;
 
@@ -174,6 +308,29 @@ static void stasis_http_cancel_recording_cb(
                {}
        }
        stasis_http_cancel_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/live/{recordingId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/live/{recordingId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/live/{recordingId}/stop.
@@ -183,9 +340,14 @@ static void stasis_http_cancel_recording_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_stop_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_stop_recording_args args = {};
        struct ast_variable *i;
 
@@ -196,6 +358,29 @@ static void stasis_http_stop_recording_cb(
                {}
        }
        stasis_http_stop_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/live/{recordingId}/stop\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/live/{recordingId}/stop\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/live/{recordingId}/pause.
@@ -205,9 +390,14 @@ static void stasis_http_stop_recording_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_pause_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_pause_recording_args args = {};
        struct ast_variable *i;
 
@@ -218,6 +408,29 @@ static void stasis_http_pause_recording_cb(
                {}
        }
        stasis_http_pause_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/live/{recordingId}/pause\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/live/{recordingId}/pause\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/live/{recordingId}/unpause.
@@ -227,9 +440,14 @@ static void stasis_http_pause_recording_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_unpause_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_unpause_recording_args args = {};
        struct ast_variable *i;
 
@@ -240,6 +458,29 @@ static void stasis_http_unpause_recording_cb(
                {}
        }
        stasis_http_unpause_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/live/{recordingId}/unpause\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/live/{recordingId}/unpause\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/live/{recordingId}/mute.
@@ -249,9 +490,14 @@ static void stasis_http_unpause_recording_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_mute_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_mute_recording_args args = {};
        struct ast_variable *i;
 
@@ -262,6 +508,29 @@ static void stasis_http_mute_recording_cb(
                {}
        }
        stasis_http_mute_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/live/{recordingId}/mute\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/live/{recordingId}/mute\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /recordings/live/{recordingId}/unmute.
@@ -271,9 +540,14 @@ static void stasis_http_mute_recording_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_unmute_recording_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_unmute_recording_args args = {};
        struct ast_variable *i;
 
@@ -284,6 +558,29 @@ static void stasis_http_unmute_recording_cb(
                {}
        }
        stasis_http_unmute_recording(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_void(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /recordings/live/{recordingId}/unmute\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /recordings/live/{recordingId}/unmute\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 
 /*! \brief REST handler for /api-docs/recordings.{format} */
@@ -375,7 +672,6 @@ static struct stasis_rest_handlers recordings_live = {
 static struct stasis_rest_handlers recordings = {
        .path_segment = "recordings",
        .callbacks = {
-               [AST_HTTP_GET] = stasis_http_get_recordings_cb,
        },
        .num_children = 2,
        .children = { &recordings_stored,&recordings_live, }
index 975ca03..da02062 100644 (file)
@@ -44,6 +44,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/stasis_app.h"
 #include "stasis_http/resource_sounds.h"
+#if defined(AST_DEVMODE)
+#include "stasis_http/ari_model_validators.h"
+#endif
 
 /*!
  * \brief Parameter parsing callback for /sounds.
@@ -53,9 +56,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_sounds_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_sounds_args args = {};
        struct ast_variable *i;
 
@@ -69,6 +77,29 @@ static void stasis_http_get_sounds_cb(
                {}
        }
        stasis_http_get_sounds(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_list(response->message,
+                               ari_validate_sound);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /sounds\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /sounds\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 /*!
  * \brief Parameter parsing callback for /sounds/{soundId}.
@@ -78,9 +109,14 @@ static void stasis_http_get_sounds_cb(
  * \param[out] response Response to the HTTP request.
  */
 static void stasis_http_get_stored_sound_cb(
-    struct ast_variable *get_params, struct ast_variable *path_vars,
-    struct ast_variable *headers, struct stasis_http_response *response)
+       struct ast_variable *get_params, struct ast_variable *path_vars,
+       struct ast_variable *headers, struct stasis_http_response *response)
 {
+#if defined(AST_DEVMODE)
+       int is_valid;
+       int code;
+#endif /* AST_DEVMODE */
+
        struct ast_get_stored_sound_args args = {};
        struct ast_variable *i;
 
@@ -91,6 +127,29 @@ static void stasis_http_get_stored_sound_cb(
                {}
        }
        stasis_http_get_stored_sound(headers, &args, response);
+#if defined(AST_DEVMODE)
+       code = response->response_code;
+
+       switch (code) {
+       case 500: /* Internal server error */
+               is_valid = 1;
+               break;
+       default:
+               if (200 <= code && code <= 299) {
+                       is_valid = ari_validate_sound(
+                               response->message);
+               } else {
+                       ast_log(LOG_ERROR, "Invalid error response %d for /sounds/{soundId}\n", code);
+                       is_valid = 0;
+               }
+       }
+
+       if (!is_valid) {
+               ast_log(LOG_ERROR, "Response validation failed for /sounds/{soundId}\n");
+               stasis_http_response_error(response, 500,
+                       "Internal Server Error", "Response validation failed");
+       }
+#endif /* AST_DEVMODE */
 }
 
 /*! \brief REST handler for /api-docs/sounds.{format} */
diff --git a/res/res_stasis_json_asterisk.c b/res/res_stasis_json_asterisk.c
deleted file mode 100644 (file)
index 830a2cf..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2012 - 2013, Digium, Inc.
- *
- * David M. Lee, II <dlee@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * !!!!!                               DO NOT EDIT                        !!!!!
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/res_stasis_http_resource.c.mustache
- */
-
-/*! \file
- *
- * \brief Asterisk resources
- *
- * \author David M. Lee, II <dlee@digium.com>
- */
-
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/json.h"
-#include "stasis_json/resource_asterisk.h"
-static int load_module(void)
-{
-       return 0;
-}
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis JSON Generators and Validators - Asterisk resources",
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_DEFAULT,
-       );
diff --git a/res/res_stasis_json_asterisk.exports.in b/res/res_stasis_json_asterisk.exports.in
deleted file mode 100644 (file)
index 5e76754..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-       local:
-               *;
-};
diff --git a/res/res_stasis_json_bridges.c b/res/res_stasis_json_bridges.c
deleted file mode 100644 (file)
index 90977bf..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2012 - 2013, Digium, Inc.
- *
- * David M. Lee, II <dlee@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * !!!!!                               DO NOT EDIT                        !!!!!
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/res_stasis_http_resource.c.mustache
- */
-
-/*! \file
- *
- * \brief Bridge resources
- *
- * \author David M. Lee, II <dlee@digium.com>
- */
-
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/json.h"
-#include "stasis_json/resource_bridges.h"
-static int load_module(void)
-{
-       return 0;
-}
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis JSON Generators and Validators - Bridge resources",
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_DEFAULT,
-       );
diff --git a/res/res_stasis_json_bridges.exports.in b/res/res_stasis_json_bridges.exports.in
deleted file mode 100644 (file)
index 5e76754..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-       local:
-               *;
-};
diff --git a/res/res_stasis_json_channels.c b/res/res_stasis_json_channels.c
deleted file mode 100644 (file)
index 3f85736..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2012 - 2013, Digium, Inc.
- *
- * David M. Lee, II <dlee@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * !!!!!                               DO NOT EDIT                        !!!!!
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/res_stasis_http_resource.c.mustache
- */
-
-/*! \file
- *
- * \brief Channel resources
- *
- * \author David M. Lee, II <dlee@digium.com>
- */
-
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/json.h"
-#include "stasis_json/resource_channels.h"
-static int load_module(void)
-{
-       return 0;
-}
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis JSON Generators and Validators - Channel resources",
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_DEFAULT,
-       );
diff --git a/res/res_stasis_json_channels.exports.in b/res/res_stasis_json_channels.exports.in
deleted file mode 100644 (file)
index 5e76754..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-       local:
-               *;
-};
diff --git a/res/res_stasis_json_endpoints.c b/res/res_stasis_json_endpoints.c
deleted file mode 100644 (file)
index be214e0..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2012 - 2013, Digium, Inc.
- *
- * David M. Lee, II <dlee@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * !!!!!                               DO NOT EDIT                        !!!!!
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/res_stasis_http_resource.c.mustache
- */
-
-/*! \file
- *
- * \brief Endpoint resources
- *
- * \author David M. Lee, II <dlee@digium.com>
- */
-
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/json.h"
-#include "stasis_json/resource_endpoints.h"
-static int load_module(void)
-{
-       return 0;
-}
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis JSON Generators and Validators - Endpoint resources",
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_DEFAULT,
-       );
diff --git a/res/res_stasis_json_endpoints.exports.in b/res/res_stasis_json_endpoints.exports.in
deleted file mode 100644 (file)
index 5e76754..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-       local:
-               *;
-};
diff --git a/res/res_stasis_json_events.c b/res/res_stasis_json_events.c
deleted file mode 100644 (file)
index 4b966e2..0000000
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2012 - 2013, Digium, Inc.
- *
- * David M. Lee, II <dlee@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * !!!!!                               DO NOT EDIT                        !!!!!
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/res_stasis_http_resource.c.mustache
- */
-
-/*! \file
- *
- * \brief WebSocket resource
- *
- * \author David M. Lee, II <dlee@digium.com>
- */
-
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/json.h"
-#include "stasis_json/resource_events.h"
-#include "asterisk/stasis_channels.h"
-#include "asterisk/stasis_bridging.h"
-
-struct ast_json *stasis_json_event_channel_userevent_create(
-       struct ast_channel_snapshot *channel_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "channel") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "eventname");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_userevent", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_bridge_created_create(
-       struct ast_bridge_snapshot *bridge_snapshot
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       int ret;
-
-       ast_assert(bridge_snapshot != NULL);
-
-       event = ast_json_object_create();
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "bridge", ast_bridge_snapshot_to_json(bridge_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "bridge_created", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_playback_finished_create(
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "playback");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "playback_finished", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_snapshot_create(
-       struct ast_channel_snapshot *channel_snapshot
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-
-       event = ast_json_object_create();
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_snapshot", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_caller_id_create(
-       struct ast_channel_snapshot *channel_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "channel") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "caller_presentation_txt");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       validator = ast_json_object_get(blob, "caller_presentation");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_caller_id", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_playback_started_create(
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "playback");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "playback_started", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_varset_create(
-       struct ast_channel_snapshot *channel_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "channel") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "variable");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       validator = ast_json_object_get(blob, "value");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_varset", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_bridge_destroyed_create(
-       struct ast_bridge_snapshot *bridge_snapshot
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       int ret;
-
-       ast_assert(bridge_snapshot != NULL);
-
-       event = ast_json_object_create();
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "bridge", ast_bridge_snapshot_to_json(bridge_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "bridge_destroyed", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_application_replaced_create(
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "application");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "application_replaced", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_destroyed_create(
-       struct ast_channel_snapshot *channel_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "channel") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "cause");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       validator = ast_json_object_get(blob, "cause_txt");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_destroyed", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_bridge_merged_create(
-       struct ast_bridge_snapshot *bridge_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(bridge_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "bridge") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "bridge_from");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "bridge", ast_bridge_snapshot_to_json(bridge_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "bridge_merged", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_left_bridge_create(
-       struct ast_bridge_snapshot *bridge_snapshot,
-       struct ast_channel_snapshot *channel_snapshot
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(bridge_snapshot != NULL);
-
-       event = ast_json_object_create();
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "bridge", ast_bridge_snapshot_to_json(bridge_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_left_bridge", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_created_create(
-       struct ast_channel_snapshot *channel_snapshot
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-
-       event = ast_json_object_create();
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_created", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_stasis_start_create(
-       struct ast_channel_snapshot *channel_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "channel") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "args");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "stasis_start", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_dialplan_create(
-       struct ast_channel_snapshot *channel_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "channel") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "application");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       validator = ast_json_object_get(blob, "application_data");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_dialplan", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_state_change_create(
-       struct ast_channel_snapshot *channel_snapshot
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-
-       event = ast_json_object_create();
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_state_change", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_hangup_request_create(
-       struct ast_channel_snapshot *channel_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "channel") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "soft");
-       if (validator) {
-               /* do validation? XXX */
-       }
-
-       validator = ast_json_object_get(blob, "cause");
-       if (validator) {
-               /* do validation? XXX */
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_hangup_request", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_entered_bridge_create(
-       struct ast_bridge_snapshot *bridge_snapshot,
-       struct ast_channel_snapshot *channel_snapshot
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(bridge_snapshot != NULL);
-
-       event = ast_json_object_create();
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "bridge", ast_bridge_snapshot_to_json(bridge_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_entered_bridge", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_channel_dtmf_received_create(
-       struct ast_channel_snapshot *channel_snapshot,
-       struct ast_json *blob
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       struct ast_json *validator;
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-       ast_assert(blob != NULL);
-       ast_assert(ast_json_object_get(blob, "channel") == NULL);
-       ast_assert(ast_json_object_get(blob, "type") == NULL);
-
-       validator = ast_json_object_get(blob, "digit");
-       if (validator) {
-               /* do validation? XXX */
-       } else {
-               /* fail message generation if the required parameter doesn't exist */
-               return NULL;
-       }
-
-       event = ast_json_deep_copy(blob);
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "channel_dtmf_received", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-struct ast_json *stasis_json_event_stasis_end_create(
-       struct ast_channel_snapshot *channel_snapshot
-       )
-{
-       RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
-       RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
-       int ret;
-
-       ast_assert(channel_snapshot != NULL);
-
-       event = ast_json_object_create();
-       if (!event) {
-               return NULL;
-       }
-
-       ret = ast_json_object_set(event,
-               "channel", ast_channel_snapshot_to_json(channel_snapshot));
-       if (ret) {
-               return NULL;
-       }
-
-       message = ast_json_pack("{s: o}", "stasis_end", ast_json_ref(event));
-       if (!message) {
-               return NULL;
-       }
-
-       return ast_json_ref(message);
-}
-
-static int load_module(void)
-{
-       return 0;
-}
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis JSON Generators and Validators - WebSocket resource",
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_DEFAULT,
-       );
diff --git a/res/res_stasis_json_events.exports.in b/res/res_stasis_json_events.exports.in
deleted file mode 100644 (file)
index 5865c02..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-       global:
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_userevent_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_bridge_created_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_playback_finished_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_snapshot_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_caller_id_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_playback_started_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_varset_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_bridge_destroyed_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_application_replaced_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_destroyed_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_bridge_merged_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_left_bridge_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_created_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_stasis_start_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_dialplan_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_state_change_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_hangup_request_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_entered_bridge_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_channel_dtmf_received_create;
-               LINKER_SYMBOL_PREFIXstasis_json_event_stasis_end_create;
-       local:
-               *;
-};
diff --git a/res/res_stasis_json_playback.c b/res/res_stasis_json_playback.c
deleted file mode 100644 (file)
index 16218c9..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2012 - 2013, Digium, Inc.
- *
- * David M. Lee, II <dlee@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * !!!!!                               DO NOT EDIT                        !!!!!
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/res_stasis_http_resource.c.mustache
- */
-
-/*! \file
- *
- * \brief Playback control resources
- *
- * \author David M. Lee, II <dlee@digium.com>
- */
-
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/json.h"
-#include "stasis_json/resource_playback.h"
-static int load_module(void)
-{
-       return 0;
-}
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis JSON Generators and Validators - Playback control resources",
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_DEFAULT,
-       );
diff --git a/res/res_stasis_json_playback.exports.in b/res/res_stasis_json_playback.exports.in
deleted file mode 100644 (file)
index 5e76754..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-       local:
-               *;
-};
diff --git a/res/res_stasis_json_recordings.c b/res/res_stasis_json_recordings.c
deleted file mode 100644 (file)
index 73935de..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2012 - 2013, Digium, Inc.
- *
- * David M. Lee, II <dlee@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * !!!!!                               DO NOT EDIT                        !!!!!
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/res_stasis_http_resource.c.mustache
- */
-
-/*! \file
- *
- * \brief Recording resources
- *
- * \author David M. Lee, II <dlee@digium.com>
- */
-
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/json.h"
-#include "stasis_json/resource_recordings.h"
-static int load_module(void)
-{
-       return 0;
-}
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis JSON Generators and Validators - Recording resources",
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_DEFAULT,
-       );
diff --git a/res/res_stasis_json_recordings.exports.in b/res/res_stasis_json_recordings.exports.in
deleted file mode 100644 (file)
index 5e76754..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-       local:
-               *;
-};
diff --git a/res/res_stasis_json_sounds.c b/res/res_stasis_json_sounds.c
deleted file mode 100644 (file)
index cc6d5ae..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2012 - 2013, Digium, Inc.
- *
- * David M. Lee, II <dlee@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-/*
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * !!!!!                               DO NOT EDIT                        !!!!!
- * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- * This file is generated by a mustache template. Please see the original
- * template in rest-api-templates/res_stasis_http_resource.c.mustache
- */
-
-/*! \file
- *
- * \brief Sound resources
- *
- * \author David M. Lee, II <dlee@digium.com>
- */
-
-/*** MODULEINFO
-       <support_level>core</support_level>
- ***/
-
-#include "asterisk.h"
-
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
-#include "asterisk/module.h"
-#include "asterisk/json.h"
-#include "stasis_json/resource_sounds.h"
-static int load_module(void)
-{
-       return 0;
-}
-
-static int unload_module(void)
-{
-       return 0;
-}
-
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Stasis JSON Generators and Validators - Sound resources",
-       .load = load_module,
-       .unload = unload_module,
-       .load_pri = AST_MODPRI_DEFAULT,
-       );
diff --git a/res/res_stasis_json_sounds.exports.in b/res/res_stasis_json_sounds.exports.in
deleted file mode 100644 (file)
index 5e76754..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-       local:
-               *;
-};
diff --git a/res/stasis_http/ari_model_validators.c b/res/stasis_http/ari_model_validators.c
new file mode 100644 (file)
index 0000000..b41c154
--- /dev/null
@@ -0,0 +1,2567 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Generated file - Build validators for ARI model objects.
+ */
+
+ /*
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * !!!!!                               DO NOT EDIT                        !!!!!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * This file is generated by a mustache template. Please see the original
+ * template in rest-api-templates/ari_model_validators.h.mustache
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "ari_model_validators.h"
+
+int ari_validate_asterisk_info(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI AsteriskInfo has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       return res;
+}
+
+int ari_validate_endpoint(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_channel_ids = 0;
+       int has_resource = 0;
+       int has_technology = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("channel_ids", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel_ids = 1;
+                       prop_is_valid = ari_validate_list(
+                               ast_json_object_iter_value(iter),
+                               ari_validate_string);
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Endpoint field channel_ids failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("resource", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_resource = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Endpoint field resource failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("state", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Endpoint field state failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("technology", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_technology = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Endpoint field technology failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI Endpoint has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_channel_ids) {
+               ast_log(LOG_ERROR, "ARI Endpoint missing required field channel_ids\n");
+               res = 0;
+       }
+
+       if (!has_resource) {
+               ast_log(LOG_ERROR, "ARI Endpoint missing required field resource\n");
+               res = 0;
+       }
+
+       if (!has_technology) {
+               ast_log(LOG_ERROR, "ARI Endpoint missing required field technology\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_caller_id(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_name = 0;
+       int has_number = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("name", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_name = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI CallerID field name failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("number", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_number = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI CallerID field number failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI CallerID has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_name) {
+               ast_log(LOG_ERROR, "ARI CallerID missing required field name\n");
+               res = 0;
+       }
+
+       if (!has_number) {
+               ast_log(LOG_ERROR, "ARI CallerID missing required field number\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_accountcode = 0;
+       int has_caller = 0;
+       int has_connected = 0;
+       int has_creationtime = 0;
+       int has_dialplan = 0;
+       int has_id = 0;
+       int has_name = 0;
+       int has_state = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("accountcode", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_accountcode = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Channel field accountcode failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("caller", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_caller = 1;
+                       prop_is_valid = ari_validate_caller_id(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Channel field caller failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("connected", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_connected = 1;
+                       prop_is_valid = ari_validate_caller_id(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Channel field connected failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("creationtime", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_creationtime = 1;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Channel field creationtime failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("dialplan", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_dialplan = 1;
+                       prop_is_valid = ari_validate_dialplan_cep(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Channel field dialplan failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("id", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_id = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Channel field id failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("name", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_name = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Channel field name failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("state", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_state = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Channel field state failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI Channel has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_accountcode) {
+               ast_log(LOG_ERROR, "ARI Channel missing required field accountcode\n");
+               res = 0;
+       }
+
+       if (!has_caller) {
+               ast_log(LOG_ERROR, "ARI Channel missing required field caller\n");
+               res = 0;
+       }
+
+       if (!has_connected) {
+               ast_log(LOG_ERROR, "ARI Channel missing required field connected\n");
+               res = 0;
+       }
+
+       if (!has_creationtime) {
+               ast_log(LOG_ERROR, "ARI Channel missing required field creationtime\n");
+               res = 0;
+       }
+
+       if (!has_dialplan) {
+               ast_log(LOG_ERROR, "ARI Channel missing required field dialplan\n");
+               res = 0;
+       }
+
+       if (!has_id) {
+               ast_log(LOG_ERROR, "ARI Channel missing required field id\n");
+               res = 0;
+       }
+
+       if (!has_name) {
+               ast_log(LOG_ERROR, "ARI Channel missing required field name\n");
+               res = 0;
+       }
+
+       if (!has_state) {
+               ast_log(LOG_ERROR, "ARI Channel missing required field state\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_dialed(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI Dialed has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       return res;
+}
+
+int ari_validate_dialplan_cep(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_context = 0;
+       int has_exten = 0;
+       int has_priority = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("context", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_context = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI DialplanCEP field context failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("exten", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_exten = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI DialplanCEP field exten failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("priority", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_priority = 1;
+                       prop_is_valid = ari_validate_long(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI DialplanCEP field priority failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI DialplanCEP has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_context) {
+               ast_log(LOG_ERROR, "ARI DialplanCEP missing required field context\n");
+               res = 0;
+       }
+
+       if (!has_exten) {
+               ast_log(LOG_ERROR, "ARI DialplanCEP missing required field exten\n");
+               res = 0;
+       }
+
+       if (!has_priority) {
+               ast_log(LOG_ERROR, "ARI DialplanCEP missing required field priority\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_bridge(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_bridge_class = 0;
+       int has_bridge_type = 0;
+       int has_channels = 0;
+       int has_id = 0;
+       int has_technology = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("bridge_class", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_bridge_class = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Bridge field bridge_class failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("bridge_type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_bridge_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Bridge field bridge_type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channels", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channels = 1;
+                       prop_is_valid = ari_validate_list(
+                               ast_json_object_iter_value(iter),
+                               ari_validate_string);
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Bridge field channels failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("id", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_id = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Bridge field id failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("technology", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_technology = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Bridge field technology failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI Bridge has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_bridge_class) {
+               ast_log(LOG_ERROR, "ARI Bridge missing required field bridge_class\n");
+               res = 0;
+       }
+
+       if (!has_bridge_type) {
+               ast_log(LOG_ERROR, "ARI Bridge missing required field bridge_type\n");
+               res = 0;
+       }
+
+       if (!has_channels) {
+               ast_log(LOG_ERROR, "ARI Bridge missing required field channels\n");
+               res = 0;
+       }
+
+       if (!has_id) {
+               ast_log(LOG_ERROR, "ARI Bridge missing required field id\n");
+               res = 0;
+       }
+
+       if (!has_technology) {
+               ast_log(LOG_ERROR, "ARI Bridge missing required field technology\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_live_recording(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_id = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("id", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_id = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI LiveRecording field id failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI LiveRecording has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_id) {
+               ast_log(LOG_ERROR, "ARI LiveRecording missing required field id\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_stored_recording(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_formats = 0;
+       int has_id = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("duration_seconds", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_int(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI StoredRecording field duration_seconds failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("formats", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_formats = 1;
+                       prop_is_valid = ari_validate_list(
+                               ast_json_object_iter_value(iter),
+                               ari_validate_string);
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI StoredRecording field formats failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("id", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_id = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI StoredRecording field id failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("time", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI StoredRecording field time failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI StoredRecording has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_formats) {
+               ast_log(LOG_ERROR, "ARI StoredRecording missing required field formats\n");
+               res = 0;
+       }
+
+       if (!has_id) {
+               ast_log(LOG_ERROR, "ARI StoredRecording missing required field id\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_format_lang_pair(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_format = 0;
+       int has_language = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("format", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_format = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI FormatLangPair field format failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("language", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_language = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI FormatLangPair field language failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI FormatLangPair has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_format) {
+               ast_log(LOG_ERROR, "ARI FormatLangPair missing required field format\n");
+               res = 0;
+       }
+
+       if (!has_language) {
+               ast_log(LOG_ERROR, "ARI FormatLangPair missing required field language\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_sound(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_formats = 0;
+       int has_id = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("formats", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_formats = 1;
+                       prop_is_valid = ari_validate_list(
+                               ast_json_object_iter_value(iter),
+                               ari_validate_format_lang_pair);
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Sound field formats failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("id", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_id = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Sound field id failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("text", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Sound field text failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI Sound has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_formats) {
+               ast_log(LOG_ERROR, "ARI Sound missing required field formats\n");
+               res = 0;
+       }
+
+       if (!has_id) {
+               ast_log(LOG_ERROR, "ARI Sound missing required field id\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_playback(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_id = 0;
+       int has_media_uri = 0;
+       int has_state = 0;
+       int has_target_uri = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("id", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_id = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Playback field id failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("language", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Playback field language failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("media_uri", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_media_uri = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Playback field media_uri failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("state", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_state = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Playback field state failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("target_uri", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_target_uri = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Playback field target_uri failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI Playback has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_id) {
+               ast_log(LOG_ERROR, "ARI Playback missing required field id\n");
+               res = 0;
+       }
+
+       if (!has_media_uri) {
+               ast_log(LOG_ERROR, "ARI Playback missing required field media_uri\n");
+               res = 0;
+       }
+
+       if (!has_state) {
+               ast_log(LOG_ERROR, "ARI Playback missing required field state\n");
+               res = 0;
+       }
+
+       if (!has_target_uri) {
+               ast_log(LOG_ERROR, "ARI Playback missing required field target_uri\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_application_replaced(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ApplicationReplaced field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ApplicationReplaced field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ApplicationReplaced field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ApplicationReplaced has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ApplicationReplaced missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ApplicationReplaced missing required field type\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_bridge_created(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_bridge = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeCreated field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeCreated field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeCreated field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_bridge = 1;
+                       prop_is_valid = ari_validate_bridge(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeCreated field bridge failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI BridgeCreated has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI BridgeCreated missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI BridgeCreated missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_bridge) {
+               ast_log(LOG_ERROR, "ARI BridgeCreated missing required field bridge\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_bridge_destroyed(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_bridge = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeDestroyed field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeDestroyed field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeDestroyed field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_bridge = 1;
+                       prop_is_valid = ari_validate_bridge(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeDestroyed field bridge failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI BridgeDestroyed has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI BridgeDestroyed missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI BridgeDestroyed missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_bridge) {
+               ast_log(LOG_ERROR, "ARI BridgeDestroyed missing required field bridge\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_bridge_merged(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_bridge = 0;
+       int has_bridge_from = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeMerged field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeMerged field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeMerged field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_bridge = 1;
+                       prop_is_valid = ari_validate_bridge(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeMerged field bridge failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("bridge_from", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_bridge_from = 1;
+                       prop_is_valid = ari_validate_bridge(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeMerged field bridge_from failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI BridgeMerged has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI BridgeMerged missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI BridgeMerged missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_bridge) {
+               ast_log(LOG_ERROR, "ARI BridgeMerged missing required field bridge\n");
+               res = 0;
+       }
+
+       if (!has_bridge_from) {
+               ast_log(LOG_ERROR, "ARI BridgeMerged missing required field bridge_from\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_caller_id(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_caller_presentation = 0;
+       int has_caller_presentation_txt = 0;
+       int has_channel = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCallerId field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCallerId field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCallerId field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("caller_presentation", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_caller_presentation = 1;
+                       prop_is_valid = ari_validate_int(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCallerId field caller_presentation failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("caller_presentation_txt", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_caller_presentation_txt = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCallerId field caller_presentation_txt failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCallerId field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelCallerId has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_caller_presentation) {
+               ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field caller_presentation\n");
+               res = 0;
+       }
+
+       if (!has_caller_presentation_txt) {
+               ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field caller_presentation_txt\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelCallerId missing required field channel\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_created(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_channel = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCreated field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCreated field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCreated field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelCreated field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelCreated has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelCreated missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelCreated missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelCreated missing required field channel\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_destroyed(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_cause = 0;
+       int has_cause_txt = 0;
+       int has_channel = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDestroyed field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDestroyed field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDestroyed field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("cause", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_cause = 1;
+                       prop_is_valid = ari_validate_int(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDestroyed field cause failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("cause_txt", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_cause_txt = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDestroyed field cause_txt failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDestroyed field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelDestroyed has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_cause) {
+               ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field cause\n");
+               res = 0;
+       }
+
+       if (!has_cause_txt) {
+               ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field cause_txt\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelDestroyed missing required field channel\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_dialplan(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_channel = 0;
+       int has_dialplan_app = 0;
+       int has_dialplan_app_data = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDialplan field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDialplan field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDialplan field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDialplan field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("dialplan_app", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_dialplan_app = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDialplan field dialplan_app failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("dialplan_app_data", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_dialplan_app_data = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDialplan field dialplan_app_data failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelDialplan has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field channel\n");
+               res = 0;
+       }
+
+       if (!has_dialplan_app) {
+               ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field dialplan_app\n");
+               res = 0;
+       }
+
+       if (!has_dialplan_app_data) {
+               ast_log(LOG_ERROR, "ARI ChannelDialplan missing required field dialplan_app_data\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_dtmf_received(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_channel = 0;
+       int has_digit = 0;
+       int has_duration_ms = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("digit", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_digit = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field digit failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("duration_ms", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_duration_ms = 1;
+                       prop_is_valid = ari_validate_int(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived field duration_ms failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelDtmfReceived has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field channel\n");
+               res = 0;
+       }
+
+       if (!has_digit) {
+               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field digit\n");
+               res = 0;
+       }
+
+       if (!has_duration_ms) {
+               ast_log(LOG_ERROR, "ARI ChannelDtmfReceived missing required field duration_ms\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_entered_bridge(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_bridge = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_bridge = 1;
+                       prop_is_valid = ari_validate_bridge(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field bridge failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelEnteredBridge field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelEnteredBridge has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelEnteredBridge missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelEnteredBridge missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_bridge) {
+               ast_log(LOG_ERROR, "ARI ChannelEnteredBridge missing required field bridge\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_hangup_request(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_channel = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelHangupRequest field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelHangupRequest field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelHangupRequest field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("cause", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_int(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelHangupRequest field cause failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelHangupRequest field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("soft", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_boolean(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelHangupRequest field soft failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelHangupRequest has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelHangupRequest missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelHangupRequest missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelHangupRequest missing required field channel\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_left_bridge(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_bridge = 0;
+       int has_channel = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelLeftBridge field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelLeftBridge field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelLeftBridge field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("bridge", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_bridge = 1;
+                       prop_is_valid = ari_validate_bridge(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelLeftBridge field bridge failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelLeftBridge field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelLeftBridge has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_bridge) {
+               ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field bridge\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelLeftBridge missing required field channel\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_state_change(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_channel = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelStateChange field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelStateChange field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelStateChange field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelStateChange field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelStateChange has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelStateChange missing required field channel\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_userevent(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_channel = 0;
+       int has_eventname = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelUserevent field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelUserevent field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelUserevent field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelUserevent field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("eventname", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_eventname = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelUserevent field eventname failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelUserevent has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelUserevent missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelUserevent missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI ChannelUserevent missing required field channel\n");
+               res = 0;
+       }
+
+       if (!has_eventname) {
+               ast_log(LOG_ERROR, "ARI ChannelUserevent missing required field eventname\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_channel_varset(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_value = 0;
+       int has_variable = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelVarset field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelVarset field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelVarset field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelVarset field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("value", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_value = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelVarset field value failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("variable", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_variable = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI ChannelVarset field variable failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI ChannelVarset has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI ChannelVarset missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI ChannelVarset missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_value) {
+               ast_log(LOG_ERROR, "ARI ChannelVarset missing required field value\n");
+               res = 0;
+       }
+
+       if (!has_variable) {
+               ast_log(LOG_ERROR, "ARI ChannelVarset missing required field variable\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_event(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       const char *discriminator;
+
+       discriminator = ast_json_string_get(ast_json_object_get(json, "type"));
+       if (!discriminator) {
+               ast_log(LOG_ERROR, "ARI Event missing required field type");
+               return 0;
+       }
+
+       if (strcmp("Event", discriminator) == 0) {
+               /* Self type; fall through */
+       } else
+       if (strcmp("ApplicationReplaced", discriminator) == 0) {
+               return ari_validate_application_replaced(json);
+       } else
+       if (strcmp("BridgeCreated", discriminator) == 0) {
+               return ari_validate_bridge_created(json);
+       } else
+       if (strcmp("BridgeDestroyed", discriminator) == 0) {
+               return ari_validate_bridge_destroyed(json);
+       } else
+       if (strcmp("BridgeMerged", discriminator) == 0) {
+               return ari_validate_bridge_merged(json);
+       } else
+       if (strcmp("ChannelCallerId", discriminator) == 0) {
+               return ari_validate_channel_caller_id(json);
+       } else
+       if (strcmp("ChannelCreated", discriminator) == 0) {
+               return ari_validate_channel_created(json);
+       } else
+       if (strcmp("ChannelDestroyed", discriminator) == 0) {
+               return ari_validate_channel_destroyed(json);
+       } else
+       if (strcmp("ChannelDialplan", discriminator) == 0) {
+               return ari_validate_channel_dialplan(json);
+       } else
+       if (strcmp("ChannelDtmfReceived", discriminator) == 0) {
+               return ari_validate_channel_dtmf_received(json);
+       } else
+       if (strcmp("ChannelEnteredBridge", discriminator) == 0) {
+               return ari_validate_channel_entered_bridge(json);
+       } else
+       if (strcmp("ChannelHangupRequest", discriminator) == 0) {
+               return ari_validate_channel_hangup_request(json);
+       } else
+       if (strcmp("ChannelLeftBridge", discriminator) == 0) {
+               return ari_validate_channel_left_bridge(json);
+       } else
+       if (strcmp("ChannelStateChange", discriminator) == 0) {
+               return ari_validate_channel_state_change(json);
+       } else
+       if (strcmp("ChannelUserevent", discriminator) == 0) {
+               return ari_validate_channel_userevent(json);
+       } else
+       if (strcmp("ChannelVarset", discriminator) == 0) {
+               return ari_validate_channel_varset(json);
+       } else
+       if (strcmp("PlaybackFinished", discriminator) == 0) {
+               return ari_validate_playback_finished(json);
+       } else
+       if (strcmp("PlaybackStarted", discriminator) == 0) {
+               return ari_validate_playback_started(json);
+       } else
+       if (strcmp("StasisEnd", discriminator) == 0) {
+               return ari_validate_stasis_end(json);
+       } else
+       if (strcmp("StasisStart", discriminator) == 0) {
+               return ari_validate_stasis_start(json);
+       } else
+       {
+               ast_log(LOG_ERROR, "ARI Event has undocumented subtype %s\n",
+                       discriminator);
+               res = 0;
+       }
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Event field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Event field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI Event field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI Event has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI Event missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI Event missing required field type\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_playback_finished(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_playback = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PlaybackFinished field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PlaybackFinished field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PlaybackFinished field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("playback", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_playback = 1;
+                       prop_is_valid = ari_validate_playback(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PlaybackFinished field playback failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI PlaybackFinished has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI PlaybackFinished missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI PlaybackFinished missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_playback) {
+               ast_log(LOG_ERROR, "ARI PlaybackFinished missing required field playback\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_playback_started(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_playback = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PlaybackStarted field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PlaybackStarted field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PlaybackStarted field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("playback", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_playback = 1;
+                       prop_is_valid = ari_validate_playback(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI PlaybackStarted field playback failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI PlaybackStarted has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI PlaybackStarted missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI PlaybackStarted missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_playback) {
+               ast_log(LOG_ERROR, "ARI PlaybackStarted missing required field playback\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_stasis_end(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_channel = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+               if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_application = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI StasisEnd field application failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ari_validate_date(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI StasisEnd field timestamp failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_type = 1;
+                       prop_is_valid = ari_validate_string(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI StasisEnd field type failed validation\n");
+                               res = 0;
+                       }
+               } else
+               if (strcmp("channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       has_channel = 1;
+                       prop_is_valid = ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI StasisEnd field channel failed validation\n");
+                               res = 0;
+                       }
+               } else
+               {
+                       ast_log(LOG_ERROR,
+                               "ARI StasisEnd has undocumented field %s\n",
+                               ast_json_object_iter_key(iter));
+                       res = 0;
+               }
+       }
+
+       if (!has_application) {
+               ast_log(LOG_ERROR, "ARI StasisEnd missing required field application\n");
+               res = 0;
+       }
+
+       if (!has_type) {
+               ast_log(LOG_ERROR, "ARI StasisEnd missing required field type\n");
+               res = 0;
+       }
+
+       if (!has_channel) {
+               ast_log(LOG_ERROR, "ARI StasisEnd missing required field channel\n");
+               res = 0;
+       }
+
+       return res;
+}
+
+int ari_validate_stasis_start(struct ast_json *json)
+{
+       int res = 1;
+       struct ast_json_iter *iter;
+       int has_application = 0;
+       int has_type = 0;
+       int has_args = 0;
+       int has_channel = 0;
+
+       for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+       &nbs