diff --git a/Makefile b/Makefile index 6cca3aafa581100985a3385284819db48f9708d8..a51534424296bd5d2f145728aede045e6b570527 100644 --- 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) diff --git a/include/asterisk/json.h b/include/asterisk/json.h index 61685fd9f533f77ca8b1ae2347c7989aaa6c8974..0584c99afce65b8444d145fb944c4f31e4314cd3 100644 --- a/include/asterisk/json.h +++ b/include/asterisk/json.h @@ -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); + /*!@}*/ /*!@{*/ diff --git a/include/asterisk/stasis_http.h b/include/asterisk/stasis_http.h index 05e9dded7cbc6b86b91e65397953e4fc781ff046..8d5a74ee7a604986b239d80a1ace670512aab7d6 100644 --- a/include/asterisk/stasis_http.h +++ b/include/asterisk/stasis_http.h @@ -33,6 +33,12 @@ #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. diff --git a/main/json.c b/main/json.c index dff35dbab94ef15ccc68216115cd66287c46261d..c185b053fdc9775193bee20ebe7f7d8212f89aad 100644 --- a/main/json.c +++ b/main/json.c @@ -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(); diff --git a/main/stasis_bridging.c b/main/stasis_bridging.c index 39a8ba892e3a26136f931f4f6ff7596dce9ac1ff..dcf4275aefd10531dfa97b5bb376ce03b4f4fd69 100644 --- a/main/stasis_bridging.c +++ b/main/stasis_bridging.c @@ -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; diff --git a/main/stasis_channels.c b/main/stasis_channels.c index 6dddb0a5ec992611869625822bb8677e296b86ba..d121279d88ce8e63cefc29a2de40b965efaf847e 100644 --- a/main/stasis_channels.c +++ b/main/stasis_channels.c @@ -32,10 +32,11 @@ 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); diff --git a/main/stasis_endpoints.c b/main/stasis_endpoints.c index 90d96856759db13d2781384b3df729a19f7473e6..a6756182c78dd4956151fc4812cf80d73a928aaf 100644 --- a/main/stasis_endpoints.c +++ b/main/stasis_endpoints.c @@ -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, diff --git a/res/Makefile b/res/Makefile index c69862802a2a0db842e212be2d7a8d64a6275337..1310dae3a08666d191f13296564a3fd7f6226126 100644 --- a/res/Makefile +++ b/res/Makefile @@ -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 index 0000000000000000000000000000000000000000..fd2ec6493ff965fadd5f4461727c11a74d609ee3 --- /dev/null +++ b/res/res_ari_model.c @@ -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 index 0000000000000000000000000000000000000000..160e23f43ea79b5e45a311c5cabda1b4325ac93e --- /dev/null +++ b/res/res_ari_model.exports.in @@ -0,0 +1,6 @@ +{ + global: + LINKER_SYMBOL_PREFIXari_*; + local: + *; +}; diff --git a/res/res_stasis.c b/res/res_stasis.c index de432e409bd2dcb6acc7f6457b7004b1d9e81c96..ed3823051cd1d4c88dbf8f404284b1662b7d86e4 100644 --- a/res/res_stasis.c +++ b/res/res_stasis.c @@ -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; } diff --git a/res/res_stasis_http.c b/res/res_stasis_http.c index fce10814635f593fc57ca5eb99ba4a91e144b11f..3ff6482b5bcd1daaa5e20c0670d7b09da35682f3 100644 --- a/res/res_stasis_http.c +++ b/res/res_stasis_http.c @@ -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"; diff --git a/res/res_stasis_http_asterisk.c b/res/res_stasis_http_asterisk.c index 9f4fd63e1516945b290d36072b420b04143d920c..01f082ad69c4fdb5933f6bd1bc585750fe993560 100644 --- a/res/res_stasis_http_asterisk.c +++ b/res/res_stasis_http_asterisk.c @@ -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} */ diff --git a/res/res_stasis_http_bridges.c b/res/res_stasis_http_bridges.c index 717b2f83fa6f81d47fa757178ddc3256714ca269..a4801df13dd97ff35b9ba0d1967a6afa5cfe85a0 100644 --- a/res/res_stasis_http_bridges.c +++ b/res/res_stasis_http_bridges.c @@ -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} */ diff --git a/res/res_stasis_http_channels.c b/res/res_stasis_http_channels.c index c865b393176fb8d509a1da0b2123830d746da9a1..ebcc9e8800e8cae1bfccfe39d547197b44acd82f 100644 --- a/res/res_stasis_http_channels.c +++ b/res/res_stasis_http_channels.c @@ -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} */ diff --git a/res/res_stasis_http_endpoints.c b/res/res_stasis_http_endpoints.c index 81cdfeb0fb2ae9dfaa98080080b3dd2196676de6..332333030abf9d11bbb980d339a16239917f5f01 100644 --- a/res/res_stasis_http_endpoints.c +++ b/res/res_stasis_http_endpoints.c @@ -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} */ diff --git a/res/res_stasis_http_events.c b/res/res_stasis_http_events.c index 909c2d659799cfb72a66a7a4fb5f3e97dd26c3a0..4217263129ff591056744366db03c629208a79b8 100644 --- a/res/res_stasis_http_events.c +++ b/res/res_stasis_http_events.c @@ -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; diff --git a/res/res_stasis_http_playback.c b/res/res_stasis_http_playback.c index 4608686bc7097e7f2d8bbf6b16fc159fc9f82587..0e56e622924576d3af613c087bda9b5444c086ad 100644 --- a/res/res_stasis_http_playback.c +++ b/res/res_stasis_http_playback.c @@ -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} */ diff --git a/res/res_stasis_http_recordings.c b/res/res_stasis_http_recordings.c index 7d89393bcaa9be7359db3641da42180271b1b079..4aa43c9be3b8e47d695d4940ca4819e34b7bd4c8 100644 --- a/res/res_stasis_http_recordings.c +++ b/res/res_stasis_http_recordings.c @@ -44,21 +44,10 @@ 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. @@ -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, } diff --git a/res/res_stasis_http_sounds.c b/res/res_stasis_http_sounds.c index 975ca038877bed9a4e0863dd29eca321704fa751..da0206223bb8fa07a1a63491cddfeebe7bb2f298 100644 --- a/res/res_stasis_http_sounds.c +++ b/res/res_stasis_http_sounds.c @@ -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 index 830a2cfabec12fcad79d75d37b5fd22fd00f6ade..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_asterisk.c +++ /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 index 5e767549c47d22e877c78bf7d7feac6d0744bddf..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_asterisk.exports.in +++ /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 index 90977bff4dc326fa6efecdfb2b462adb320841b5..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_bridges.c +++ /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 index 5e767549c47d22e877c78bf7d7feac6d0744bddf..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_bridges.exports.in +++ /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 index 3f85736b35096074e7e54cf186dbd1b849b87c5d..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_channels.c +++ /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 index 5e767549c47d22e877c78bf7d7feac6d0744bddf..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_channels.exports.in +++ /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 index be214e038d9862c739286c5092ea6081f646a3ee..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_endpoints.c +++ /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 index 5e767549c47d22e877c78bf7d7feac6d0744bddf..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_endpoints.exports.in +++ /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 index 4b966e235d507fb3324dfef83744ed9be2b1aaf5..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_events.c +++ /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 index 5865c026b9bfd66e6b4ef4392d7c92b415e9a47b..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_events.exports.in +++ /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 index 16218c92d63cd413e5eedb480ebc2bbddf268300..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_playback.c +++ /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 index 5e767549c47d22e877c78bf7d7feac6d0744bddf..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_playback.exports.in +++ /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 index 73935dede23c9837a0a4748c7b2eb9ce6b37479b..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_recordings.c +++ /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 index 5e767549c47d22e877c78bf7d7feac6d0744bddf..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_recordings.exports.in +++ /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 index cc6d5ae17079286faeaad09366ff4becd3b1888b..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_sounds.c +++ /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 index 5e767549c47d22e877c78bf7d7feac6d0744bddf..0000000000000000000000000000000000000000 --- a/res/res_stasis_json_sounds.exports.in +++ /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 index 0000000000000000000000000000000000000000..b41c15473a007872a3171260d50b085bb3a6b009 --- /dev/null +++ b/res/stasis_http/ari_model_validators.c @@ -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)) { + 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 StasisStart 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 StasisStart 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 StasisStart field type failed validation\n"); + res = 0; + } + } else + if (strcmp("args", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + has_args = 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 StasisStart field args 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 StasisStart field channel failed validation\n"); + res = 0; + } + } else + { + ast_log(LOG_ERROR, + "ARI StasisStart has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + + if (!has_application) { + ast_log(LOG_ERROR, "ARI StasisStart missing required field application\n"); + res = 0; + } + + if (!has_type) { + ast_log(LOG_ERROR, "ARI StasisStart missing required field type\n"); + res = 0; + } + + if (!has_args) { + ast_log(LOG_ERROR, "ARI StasisStart missing required field args\n"); + res = 0; + } + + if (!has_channel) { + ast_log(LOG_ERROR, "ARI StasisStart missing required field channel\n"); + res = 0; + } + + return res; +} diff --git a/res/stasis_http/ari_model_validators.h b/res/stasis_http/ari_model_validators.h new file mode 100644 index 0000000000000000000000000000000000000000..c4d0f27c28aef2073cd4359cad4aa5ea7f9275e1 --- /dev/null +++ b/res/stasis_http/ari_model_validators.h @@ -0,0 +1,659 @@ +/* + * 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 + */ + +#ifndef _ASTERISK_ARI_MODEL_H +#define _ASTERISK_ARI_MODEL_H + +#include "asterisk/json.h" + +/*! @{ */ + +/*! + * \brief Validator for native Swagger void. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_void(struct ast_json *json); + +/*! + * \brief Validator for native Swagger byte. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_byte(struct ast_json *json); + +/*! + * \brief Validator for native Swagger boolean. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_boolean(struct ast_json *json); + +/*! + * \brief Validator for native Swagger int. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_int(struct ast_json *json); + +/*! + * \brief Validator for native Swagger long. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_long(struct ast_json *json); + +/*! + * \brief Validator for native Swagger float. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_float(struct ast_json *json); + +/*! + * \brief Validator for native Swagger double. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_double(struct ast_json *json); + +/*! + * \brief Validator for native Swagger string. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_string(struct ast_json *json); + +/*! + * \brief Validator for native Swagger date. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_date(struct ast_json *json); + +/*! + * \brief Validator for a Swagger List[]/JSON array. + * + * \param json JSON object to validate. + * \param fn Validator to call on every element in the array. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_list(struct ast_json *json, int (*fn)(struct ast_json *)); + +/*! @} */ + +/*! + * \brief Validator for AsteriskInfo. + * + * Asterisk system information + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_asterisk_info(struct ast_json *json); + +/*! + * \brief Validator for Endpoint. + * + * An external device that may offer/accept calls to/from Asterisk. + * + * Unlike most resources, which have a single unique identifier, an endpoint is uniquely identified by the technology/resource pair. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_endpoint(struct ast_json *json); + +/*! + * \brief Validator for CallerID. + * + * Caller identification + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_caller_id(struct ast_json *json); + +/*! + * \brief Validator for Channel. + * + * A specific communication connection between Asterisk and an Endpoint. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel(struct ast_json *json); + +/*! + * \brief Validator for Dialed. + * + * Dialed channel information. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_dialed(struct ast_json *json); + +/*! + * \brief Validator for DialplanCEP. + * + * Dialplan location (context/extension/priority) + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_dialplan_cep(struct ast_json *json); + +/*! + * \brief Validator for Bridge. + * + * The merging of media from one or more channels. + * + * Everyone on the bridge receives the same audio. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_bridge(struct ast_json *json); + +/*! + * \brief Validator for LiveRecording. + * + * A recording that is in progress + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_live_recording(struct ast_json *json); + +/*! + * \brief Validator for StoredRecording. + * + * A past recording that may be played back. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_stored_recording(struct ast_json *json); + +/*! + * \brief Validator for FormatLangPair. + * + * Identifies the format and language of a sound file + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_format_lang_pair(struct ast_json *json); + +/*! + * \brief Validator for Sound. + * + * A media file that may be played back. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_sound(struct ast_json *json); + +/*! + * \brief Validator for Playback. + * + * Object representing the playback of media to a channel + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_playback(struct ast_json *json); + +/*! + * \brief Validator for ApplicationReplaced. + * + * Notification that another WebSocket has taken over for an application. + * + * An application may only be subscribed to by a single WebSocket at a time. If multiple WebSockets attempt to subscribe to the same application, the newer WebSocket wins, and the older one receives this event. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_application_replaced(struct ast_json *json); + +/*! + * \brief Validator for BridgeCreated. + * + * Notification that a bridge has been created. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_bridge_created(struct ast_json *json); + +/*! + * \brief Validator for BridgeDestroyed. + * + * Notification that a bridge has been destroyed. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_bridge_destroyed(struct ast_json *json); + +/*! + * \brief Validator for BridgeMerged. + * + * Notification that one bridge has merged into another. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_bridge_merged(struct ast_json *json); + +/*! + * \brief Validator for ChannelCallerId. + * + * Channel changed Caller ID. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_caller_id(struct ast_json *json); + +/*! + * \brief Validator for ChannelCreated. + * + * Notification that a channel has been created. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_created(struct ast_json *json); + +/*! + * \brief Validator for ChannelDestroyed. + * + * Notification that a channel has been destroyed. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_destroyed(struct ast_json *json); + +/*! + * \brief Validator for ChannelDialplan. + * + * Channel changed location in the dialplan. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_dialplan(struct ast_json *json); + +/*! + * \brief Validator for ChannelDtmfReceived. + * + * DTMF received on a channel. + * + * This event is sent when the DTMF ends. There is no notification about the start of DTMF + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_dtmf_received(struct ast_json *json); + +/*! + * \brief Validator for ChannelEnteredBridge. + * + * Notification that a channel has entered a bridge. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_entered_bridge(struct ast_json *json); + +/*! + * \brief Validator for ChannelHangupRequest. + * + * A hangup was requested on the channel. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_hangup_request(struct ast_json *json); + +/*! + * \brief Validator for ChannelLeftBridge. + * + * Notification that a channel has left a bridge. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_left_bridge(struct ast_json *json); + +/*! + * \brief Validator for ChannelStateChange. + * + * Notification of a channel's state change. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_state_change(struct ast_json *json); + +/*! + * \brief Validator for ChannelUserevent. + * + * User-generated event with additional user-defined fields in the object. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_userevent(struct ast_json *json); + +/*! + * \brief Validator for ChannelVarset. + * + * Channel variable changed. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_channel_varset(struct ast_json *json); + +/*! + * \brief Validator for Event. + * + * Base type for asynchronous events from Asterisk. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_event(struct ast_json *json); + +/*! + * \brief Validator for PlaybackFinished. + * + * Event showing the completion of a media playback operation. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_playback_finished(struct ast_json *json); + +/*! + * \brief Validator for PlaybackStarted. + * + * Event showing the start of a media playback operation. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_playback_started(struct ast_json *json); + +/*! + * \brief Validator for StasisEnd. + * + * Notification that a channel has left a Stasis appliction. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_stasis_end(struct ast_json *json); + +/*! + * \brief Validator for StasisStart. + * + * Notification that a channel has entered a Stasis appliction. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_stasis_start(struct ast_json *json); + +/* + * JSON models + * + * AsteriskInfo + * Endpoint + * - channel_ids: List[string] (required) + * - resource: string (required) + * - state: string + * - technology: string (required) + * CallerID + * - name: string (required) + * - number: string (required) + * Channel + * - accountcode: string (required) + * - caller: CallerID (required) + * - connected: CallerID (required) + * - creationtime: Date (required) + * - dialplan: DialplanCEP (required) + * - id: string (required) + * - name: string (required) + * - state: string (required) + * Dialed + * DialplanCEP + * - context: string (required) + * - exten: string (required) + * - priority: long (required) + * Bridge + * - bridge_class: string (required) + * - bridge_type: string (required) + * - channels: List[string] (required) + * - id: string (required) + * - technology: string (required) + * LiveRecording + * - id: string (required) + * StoredRecording + * - duration_seconds: int + * - formats: List[string] (required) + * - id: string (required) + * - time: Date + * FormatLangPair + * - format: string (required) + * - language: string (required) + * Sound + * - formats: List[FormatLangPair] (required) + * - id: string (required) + * - text: string + * Playback + * - id: string (required) + * - language: string + * - media_uri: string (required) + * - state: string (required) + * - target_uri: string (required) + * ApplicationReplaced + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * BridgeCreated + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - bridge: Bridge (required) + * BridgeDestroyed + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - bridge: Bridge (required) + * BridgeMerged + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - bridge: Bridge (required) + * - bridge_from: Bridge (required) + * ChannelCallerId + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - caller_presentation: int (required) + * - caller_presentation_txt: string (required) + * - channel: Channel (required) + * ChannelCreated + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - channel: Channel (required) + * ChannelDestroyed + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - cause: int (required) + * - cause_txt: string (required) + * - channel: Channel (required) + * ChannelDialplan + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - channel: Channel (required) + * - dialplan_app: string (required) + * - dialplan_app_data: string (required) + * ChannelDtmfReceived + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - channel: Channel (required) + * - digit: string (required) + * - duration_ms: int (required) + * ChannelEnteredBridge + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - bridge: Bridge (required) + * - channel: Channel + * ChannelHangupRequest + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - cause: int + * - channel: Channel (required) + * - soft: boolean + * ChannelLeftBridge + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - bridge: Bridge (required) + * - channel: Channel (required) + * ChannelStateChange + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - channel: Channel (required) + * ChannelUserevent + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - channel: Channel (required) + * - eventname: string (required) + * ChannelVarset + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - channel: Channel + * - value: string (required) + * - variable: string (required) + * Event + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * PlaybackFinished + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - playback: Playback (required) + * PlaybackStarted + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - playback: Playback (required) + * StasisEnd + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - channel: Channel (required) + * StasisStart + * - application: string (required) + * - timestamp: Date + * - type: string (required) + * - args: List[string] (required) + * - channel: Channel (required) + */ + +#endif /* _ASTERISK_ARI_MODEL_H */ diff --git a/res/stasis_http/ari_websockets.c b/res/stasis_http/ari_websockets.c index e6b316b5724ea8ecbb24799f986f318c18b1ee43..60a18465706a58df3d541bda69acdb33b8fa881f 100644 --- a/res/stasis_http/ari_websockets.c +++ b/res/stasis_http/ari_websockets.c @@ -31,6 +31,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") struct ari_websocket_session { struct ast_websocket *ws_session; + int (*validator)(struct ast_json *); }; static void websocket_session_dtor(void *obj) @@ -41,8 +42,16 @@ static void websocket_session_dtor(void *obj) session->ws_session = NULL; } +/*! + * \brief Validator that always succeeds. + */ +static int null_validator(struct ast_json *json) +{ + return 1; +} + struct ari_websocket_session *ari_websocket_session_create( - struct ast_websocket *ws_session) + struct ast_websocket *ws_session, int (*validator)(struct ast_json *)) { RAII_VAR(struct ari_websocket_session *, session, NULL, ao2_cleanup); @@ -50,6 +59,10 @@ struct ari_websocket_session *ari_websocket_session_create( return NULL; } + if (validator == NULL) { + validator = null_validator; + } + if (ast_websocket_set_nonblock(ws_session) != 0) { ast_log(LOG_ERROR, "Stasis web socket failed to set nonblock; closing\n"); @@ -63,6 +76,7 @@ struct ari_websocket_session *ari_websocket_session_create( ao2_ref(ws_session, +1); session->ws_session = ws_session; + session->validator = validator; ao2_ref(session, +1); return session; @@ -109,10 +123,24 @@ struct ast_json *ari_websocket_session_read( return ast_json_ref(message); } +#define VALIDATION_FAILED \ + "{ \"error\": \"Outgoing message failed validation\" }" + int ari_websocket_session_write(struct ari_websocket_session *session, struct ast_json *message) { - RAII_VAR(char *, str, ast_json_dump_string(message), ast_free); + RAII_VAR(char *, str, NULL, ast_free); + +#ifdef AST_DEVMODE + if (!session->validator(message)) { + ast_log(LOG_ERROR, "Outgoing message failed validation\n"); + return ast_websocket_write(session->ws_session, + AST_WEBSOCKET_OPCODE_TEXT, VALIDATION_FAILED, + strlen(VALIDATION_FAILED)); + } +#endif + + str = ast_json_dump_string_format(message, stasis_http_json_format()); if (str == NULL) { ast_log(LOG_ERROR, "Failed to encode JSON object\n"); diff --git a/res/stasis_http/resource_recordings.c b/res/stasis_http/resource_recordings.c index 2400a6876b091cfc6b00436802a1c022abe33ecd..7d31c42aa20faa80d749618cacc36ea9a3244aa7 100644 --- a/res/stasis_http/resource_recordings.c +++ b/res/stasis_http/resource_recordings.c @@ -29,10 +29,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "resource_recordings.h" -void stasis_http_get_recordings(struct ast_variable *headers, struct ast_get_recordings_args *args, struct stasis_http_response *response) -{ - ast_log(LOG_ERROR, "TODO: stasis_http_get_recordings\n"); -} void stasis_http_get_stored_recordings(struct ast_variable *headers, struct ast_get_stored_recordings_args *args, struct stasis_http_response *response) { ast_log(LOG_ERROR, "TODO: stasis_http_get_stored_recordings\n"); diff --git a/res/stasis_http/resource_recordings.h b/res/stasis_http/resource_recordings.h index ee48e43b778054d4687e1f11de0f4a8add8a0af8..acccc124bb1ed6acfa1f85ce17cb759349e0a69e 100644 --- a/res/stasis_http/resource_recordings.h +++ b/res/stasis_http/resource_recordings.h @@ -39,17 +39,6 @@ #include "asterisk/stasis_http.h" -/*! \brief Argument struct for stasis_http_get_recordings() */ -struct ast_get_recordings_args { -}; -/*! - * \brief List all recordings. - * - * \param headers HTTP headers - * \param args Swagger parameters - * \param[out] response HTTP response - */ -void stasis_http_get_recordings(struct ast_variable *headers, struct ast_get_recordings_args *args, struct stasis_http_response *response); /*! \brief Argument struct for stasis_http_get_stored_recordings() */ struct ast_get_stored_recordings_args { }; diff --git a/res/stasis_json/resource_asterisk.h b/res/stasis_json/resource_asterisk.h deleted file mode 100644 index 5a717d005bc3b1f3d042e1068f131c0c70182dad..0000000000000000000000000000000000000000 --- a/res/stasis_json/resource_asterisk.h +++ /dev/null @@ -1,46 +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. - */ - -/*! \file - * - * \brief Generated file - declares stubs to be implemented in - * res/stasis_json/resource_asterisk.c - * - * Asterisk resources - * - * \author David M. Lee, II <dlee@digium.com> - */ - -/* - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT EDIT !!!!! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_ASTERISK_H -#define _ASTERISK_RESOURCE_ASTERISK_H - -/* - * JSON models - * - * AsteriskInfo - */ - -#endif /* _ASTERISK_RESOURCE_ASTERISK_H */ diff --git a/res/stasis_json/resource_bridges.h b/res/stasis_json/resource_bridges.h deleted file mode 100644 index cf2d03dc7f346759e080c468ba116338fe3538f2..0000000000000000000000000000000000000000 --- a/res/stasis_json/resource_bridges.h +++ /dev/null @@ -1,48 +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. - */ - -/*! \file - * - * \brief Generated file - declares stubs to be implemented in - * res/stasis_json/resource_bridges.c - * - * Bridge resources - * - * \author David M. Lee, II <dlee@digium.com> - */ - -/* - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT EDIT !!!!! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_BRIDGES_H -#define _ASTERISK_RESOURCE_BRIDGES_H - -/* - * JSON models - * - * Bridge - * - channels: List[string] (required) - * - bridgeType: string (required) - */ - -#endif /* _ASTERISK_RESOURCE_BRIDGES_H */ diff --git a/res/stasis_json/resource_channels.h b/res/stasis_json/resource_channels.h deleted file mode 100644 index c98743c36b55a064c7e497ea9b2861d68275d981..0000000000000000000000000000000000000000 --- a/res/stasis_json/resource_channels.h +++ /dev/null @@ -1,75 +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. - */ - -/*! \file - * - * \brief Generated file - declares stubs to be implemented in - * res/stasis_json/resource_channels.c - * - * Channel resources - * - * \author David M. Lee, II <dlee@digium.com> - */ - -/* - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT EDIT !!!!! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_CHANNELS_H -#define _ASTERISK_RESOURCE_CHANNELS_H - -/* - * JSON models - * - * DialplanCEP - * - priority: long (required) - * - exten: string (required) - * - context: string (required) - * Playback - * - language: string - * - media_uri: string (required) - * - id: string (required) - * - target_uri: string (required) - * - state: string (required) - * Channel - * - accountcode: string (required) - * - linkedid: string (required) - * - name: string (required) - * - userfield: string (required) - * - caller: CallerID (required) - * - creationtime: Date (required) - * - state: string (required) - * - parkinglot: string (required) - * - peeraccount: string (required) - * - appl: string (required) - * - connected: CallerID (required) - * - uniqueid: string (required) - * - hangupsource: string (required) - * - dialplan: DialplanCEP (required) - * - data: string (required) - * CallerID - * - name: string (required) - * - number: string (required) - * Dialed - */ - -#endif /* _ASTERISK_RESOURCE_CHANNELS_H */ diff --git a/res/stasis_json/resource_endpoints.h b/res/stasis_json/resource_endpoints.h deleted file mode 100644 index 7f2e4233c5bef774e0e6d168a56c1af1b58f034c..0000000000000000000000000000000000000000 --- a/res/stasis_json/resource_endpoints.h +++ /dev/null @@ -1,48 +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. - */ - -/*! \file - * - * \brief Generated file - declares stubs to be implemented in - * res/stasis_json/resource_endpoints.c - * - * Endpoint resources - * - * \author David M. Lee, II <dlee@digium.com> - */ - -/* - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT EDIT !!!!! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_ENDPOINTS_H -#define _ASTERISK_RESOURCE_ENDPOINTS_H - -/* - * JSON models - * - * Endpoint - * - resource: string (required) - * - technology: string (required) - */ - -#endif /* _ASTERISK_RESOURCE_ENDPOINTS_H */ diff --git a/res/stasis_json/resource_events.h b/res/stasis_json/resource_events.h deleted file mode 100644 index a2af30daaae0915ed56b694563a0684fab3315d5..0000000000000000000000000000000000000000 --- a/res/stasis_json/resource_events.h +++ /dev/null @@ -1,386 +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. - */ - -/*! \file - * - * \brief Generated file - declares stubs to be implemented in - * res/stasis_json/resource_events.c - * - * WebSocket resource - * - * \author David M. Lee, II <dlee@digium.com> - */ - -/* - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT EDIT !!!!! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_EVENTS_H -#define _ASTERISK_RESOURCE_EVENTS_H - -struct ast_channel_snapshot; -struct ast_bridge_snapshot; - -/*! - * \brief User-generated event with additional user-defined fields in the object. - * - * \param channel The channel that signaled the user event. - * \param blob JSON blob containing the following parameters: - * - eventname: string - The name of the user event. (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_userevent_create( - struct ast_channel_snapshot *channel_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Notification that a bridge has been created. - * - * \param bridge The bridge to be used to generate this event - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_bridge_created_create( - struct ast_bridge_snapshot *bridge_snapshot - ); - -/*! - * \brief Event showing the completion of a media playback operation. - * - * \param blob JSON blob containing the following parameters: - * - playback: Playback - Playback control object (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_playback_finished_create( - struct ast_json *blob - ); - -/*! - * \brief Some part of channel state changed. - * - * \param channel The channel to be used to generate this event - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_snapshot_create( - struct ast_channel_snapshot *channel_snapshot - ); - -/*! - * \brief Channel changed Caller ID. - * - * \param channel The channel that changed Caller ID. - * \param blob JSON blob containing the following parameters: - * - caller_presentation_txt: string - The text representation of the Caller Presentation value. (required) - * - caller_presentation: integer - The integer representation of the Caller Presentation value. (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_caller_id_create( - struct ast_channel_snapshot *channel_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Event showing the start of a media playback operation. - * - * \param blob JSON blob containing the following parameters: - * - playback: Playback - Playback control object (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_playback_started_create( - struct ast_json *blob - ); - -/*! - * \brief Channel variable changed. - * - * \param channel The channel on which the variable was set. - * \param blob JSON blob containing the following parameters: - * - variable: string - The variable that changed. (required) - * - value: string - The new value of the variable. (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_varset_create( - struct ast_channel_snapshot *channel_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Notification that a bridge has been destroyed. - * - * \param bridge The bridge to be used to generate this event - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_bridge_destroyed_create( - struct ast_bridge_snapshot *bridge_snapshot - ); - -/*! - * \brief Notification that another WebSocket has taken over for an application. - * - * \param blob JSON blob containing the following parameters: - * - application: string (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_application_replaced_create( - struct ast_json *blob - ); - -/*! - * \brief Notification that a channel has been destroyed. - * - * \param channel The channel to be used to generate this event - * \param blob JSON blob containing the following parameters: - * - cause: integer - Integer representation of the cause of the hangup (required) - * - cause_txt: string - Text representation of the cause of the hangup (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_destroyed_create( - struct ast_channel_snapshot *channel_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Notification that one bridge has merged into another. - * - * \param bridge The bridge to be used to generate this event - * \param blob JSON blob containing the following parameters: - * - bridge_from: Bridge (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_bridge_merged_create( - struct ast_bridge_snapshot *bridge_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Notification that a channel has left a bridge. - * - * \param channel The channel to be used to generate this event - * \param bridge The bridge to be used to generate this event - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_left_bridge_create( - struct ast_bridge_snapshot *bridge_snapshot, - struct ast_channel_snapshot *channel_snapshot - ); - -/*! - * \brief Notification that a channel has been created. - * - * \param channel The channel to be used to generate this event - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_created_create( - struct ast_channel_snapshot *channel_snapshot - ); - -/*! - * \brief Notification that a channel has entered a Stasis appliction. - * - * \param channel The channel to be used to generate this event - * \param blob JSON blob containing the following parameters: - * - args: List[string] - Arguments to the application (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_stasis_start_create( - struct ast_channel_snapshot *channel_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Channel changed location in the dialplan. - * - * \param channel The channel that changed dialplan location. - * \param blob JSON blob containing the following parameters: - * - application: string - The application that the channel is currently in. (required) - * - application_data: string - The data that was passed to the application when it was invoked. (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_dialplan_create( - struct ast_channel_snapshot *channel_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Notification of a channel's state change. - * - * \param channel The channel to be used to generate this event - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_state_change_create( - struct ast_channel_snapshot *channel_snapshot - ); - -/*! - * \brief A hangup was requested on the channel. - * - * \param channel The channel on which the hangup was requested. - * \param blob JSON blob containing the following parameters: - * - soft: boolean - Whether the hangup request was a soft hangup request. - * - cause: integer - Integer representation of the cause of the hangup. - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_hangup_request_create( - struct ast_channel_snapshot *channel_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Notification that a channel has entered a bridge. - * - * \param channel The channel to be used to generate this event - * \param bridge The bridge to be used to generate this event - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_entered_bridge_create( - struct ast_bridge_snapshot *bridge_snapshot, - struct ast_channel_snapshot *channel_snapshot - ); - -/*! - * \brief DTMF received on a channel. - * - * \param channel The channel on which DTMF was received - * \param blob JSON blob containing the following parameters: - * - digit: string - DTMF digit received (0-9, A-E, # or *) (required) - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_channel_dtmf_received_create( - struct ast_channel_snapshot *channel_snapshot, - struct ast_json *blob - ); - -/*! - * \brief Notification that a channel has left a Stasis appliction. - * - * \param channel The channel to be used to generate this event - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -struct ast_json *stasis_json_event_stasis_end_create( - struct ast_channel_snapshot *channel_snapshot - ); - -/* - * JSON models - * - * ChannelUserevent - * - eventname: string (required) - * BridgeCreated - * PlaybackFinished - * - playback: Playback (required) - * ChannelSnapshot - * ChannelCallerId - * - caller_presentation_txt: string (required) - * - caller_presentation: integer (required) - * PlaybackStarted - * - playback: Playback (required) - * ChannelVarset - * - variable: string (required) - * - value: string (required) - * BridgeDestroyed - * ApplicationReplaced - * - application: string (required) - * ChannelDestroyed - * - cause: integer (required) - * - cause_txt: string (required) - * BridgeMerged - * - bridge_from: Bridge (required) - * ChannelLeftBridge - * ChannelCreated - * StasisStart - * - args: List[string] (required) - * ChannelDialplan - * - application: string (required) - * - application_data: string (required) - * ChannelStateChange - * ChannelHangupRequest - * - soft: boolean - * - cause: integer - * ChannelEnteredBridge - * ChannelDtmfReceived - * - digit: string (required) - * Event - * - channel_varset: ChannelVarset - * - channel_created: ChannelCreated - * - channel_destroyed: ChannelDestroyed - * - channel_entered_bridge: ChannelEnteredBridge - * - channel_left_bridge: ChannelLeftBridge - * - bridge_merged: BridgeMerged - * - channel_dialplan: ChannelDialplan - * - application_replaced: ApplicationReplaced - * - channel_state_change: ChannelStateChange - * - bridge_created: BridgeCreated - * - application: string (required) - * - channel_hangup_request: ChannelHangupRequest - * - channel_userevent: ChannelUserevent - * - stasis_start: StasisStart - * - channel_snapshot: ChannelSnapshot - * - channel_dtmf_received: ChannelDtmfReceived - * - channel_caller_id: ChannelCallerId - * - bridge_destroyed: BridgeDestroyed - * - playback_started: PlaybackStarted - * - playback_finished: PlaybackFinished - * - stasis_end: StasisEnd - * StasisEnd - */ - -#endif /* _ASTERISK_RESOURCE_EVENTS_H */ diff --git a/res/stasis_json/resource_playback.h b/res/stasis_json/resource_playback.h deleted file mode 100644 index e84e6de0dfa1f202b9d64153cdbee650dcd0458e..0000000000000000000000000000000000000000 --- a/res/stasis_json/resource_playback.h +++ /dev/null @@ -1,47 +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. - */ - -/*! \file - * - * \brief Generated file - declares stubs to be implemented in - * res/stasis_json/resource_playback.c - * - * Playback control resources - * - * \author David M. Lee, II <dlee@digium.com> - */ - -/* - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT EDIT !!!!! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_PLAYBACK_H -#define _ASTERISK_RESOURCE_PLAYBACK_H - -/* - * JSON models - * - * Playback - * - id: string (required) - */ - -#endif /* _ASTERISK_RESOURCE_PLAYBACK_H */ diff --git a/res/stasis_json/resource_recordings.h b/res/stasis_json/resource_recordings.h deleted file mode 100644 index b460fb7696527959f8b95303a857cf9f2db1f965..0000000000000000000000000000000000000000 --- a/res/stasis_json/resource_recordings.h +++ /dev/null @@ -1,54 +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. - */ - -/*! \file - * - * \brief Generated file - declares stubs to be implemented in - * res/stasis_json/resource_recordings.c - * - * Recording resources - * - * \author David M. Lee, II <dlee@digium.com> - */ - -/* - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT EDIT !!!!! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_RECORDINGS_H -#define _ASTERISK_RESOURCE_RECORDINGS_H - -/* - * JSON models - * - * Recording - * - id: string (required) - * StoredRecording - * - durationSeconds: int - * - time: Date - * - id: string (required) - * - formats: List[string] (required) - * LiveRecording - * - id: string (required) - */ - -#endif /* _ASTERISK_RESOURCE_RECORDINGS_H */ diff --git a/res/stasis_json/resource_sounds.h b/res/stasis_json/resource_sounds.h deleted file mode 100644 index d7f8714e692dfa50ef35484b2802a82a597cba47..0000000000000000000000000000000000000000 --- a/res/stasis_json/resource_sounds.h +++ /dev/null @@ -1,52 +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. - */ - -/*! \file - * - * \brief Generated file - declares stubs to be implemented in - * res/stasis_json/resource_sounds.c - * - * Sound resources - * - * \author David M. Lee, II <dlee@digium.com> - */ - -/* - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * !!!!! DO NOT EDIT !!!!! - * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_SOUNDS_H -#define _ASTERISK_RESOURCE_SOUNDS_H - -/* - * JSON models - * - * Sound - * - text: string - * - id: string (required) - * - formats: List[FormatLangPair] (required) - * FormatLangPair - * - language: string (required) - * - format: string (required) - */ - -#endif /* _ASTERISK_RESOURCE_SOUNDS_H */ diff --git a/rest-api-templates/api.wiki.mustache b/rest-api-templates/api.wiki.mustache new file mode 100644 index 0000000000000000000000000000000000000000..c70e58fc367092bce8b30ae258962bfc673f209a --- /dev/null +++ b/rest-api-templates/api.wiki.mustache @@ -0,0 +1,47 @@ +{{#api_declaration}} +h1. {{name_title}} + +|| Method || Path || Return Model || Summary || +{{#apis}} +{{#operations}} +| {{http_method}} | [{{wiki_path}}|#{{nickname}}] | {{#response_class}}{{#is_primitive}}{{name}}{{/is_primitive}}{{^is_primitive}}[{{wiki_name}}|{{wiki_prefix}} REST Data Models#{{singular_name}}]{{/is_primitive}}{{/response_class}} | {{summary}} | +{{/operations}} +{{/apis}} +{{#apis}} +{{#operations}} + +{anchor:{{nickname}}} +h2. {{http_method}} {{wiki_path}} + +{{{summary}}}{{#notes}} {{{notes}}}{{/notes}} +{{#has_path_parameters}} + +h3. Path parameters +{{#path_parameters}} +* {{name}}: {{data_type}}{{#default_value}} = {{default_value}}{{/default_value}} - {{description}} +{{/path_parameters}} +{{/has_path_parameters}} +{{#has_query_parameters}} + +h3. Query parameters +{{#query_parameters}} +* {{name}}: {{data_type}}{{#default_value}} = {{default_value}}{{/default_value}} -{{#required}} *(required)*{{/required}} {{description}} +{{/query_parameters}} +{{/has_query_parameters}} +{{#has_header_parameters}} + +h3. Header parameters +{{#header_parameters}} +* {{name}}: {{data_type}}{{#default_value}} = {{default_value}}{{/default_value}} -{{#required}} *(required)*{{/required}} {{description}} +{{/header_parameters}} +{{/has_header_parameters}} +{{#has_error_responses}} + +h3. Error Responses +{{#error_responses}} +* {{code}} - {{{reason}}} +{{/error_responses}} +{{/has_error_responses}} +{{/operations}} +{{/apis}} +{{/api_declaration}} diff --git a/rest-api-templates/ari_model_validators.c.mustache b/rest-api-templates/ari_model_validators.c.mustache new file mode 100644 index 0000000000000000000000000000000000000000..0e87f8e248df188d53edff0fc5e8af256946611a --- /dev/null +++ b/rest-api-templates/ari_model_validators.c.mustache @@ -0,0 +1,117 @@ +/* + * 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" +{{#apis}} +{{#api_declaration}} +{{#models}} + +int ari_validate_{{c_id}}(struct ast_json *json) +{ + int res = 1; + struct ast_json_iter *iter; +{{#properties}} +{{#required}} + int has_{{name}} = 0; +{{/required}} +{{/properties}} +{{#has_subtypes}} + const char *discriminator; + + discriminator = ast_json_string_get(ast_json_object_get(json, "{{discriminator.name}}")); + if (!discriminator) { + ast_log(LOG_ERROR, "ARI {{id}} missing required field {{discriminator.name}}"); + return 0; + } + + if (strcmp("{{id}}", discriminator) == 0) { + /* Self type; fall through */ + } else +{{#subtypes}} + if (strcmp("{{id}}", discriminator) == 0) { + return ari_validate_{{c_id}}(json); + } else +{{/subtypes}} + { + ast_log(LOG_ERROR, "ARI {{id}} has undocumented subtype %s\n", + discriminator); + res = 0; + } +{{/has_subtypes}} + + for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) { +{{#properties}} + if (strcmp("{{name}}", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; +{{#required}} + has_{{name}} = 1; +{{/required}} +{{#type}} +{{#is_list}} + prop_is_valid = ari_validate_list( + ast_json_object_iter_value(iter), + ari_validate_{{c_singular_name}}); +{{/is_list}} +{{^is_list}} + prop_is_valid = ari_validate_{{c_name}}( + ast_json_object_iter_value(iter)); +{{/is_list}} +{{/type}} + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI {{id}} field {{name}} failed validation\n"); + res = 0; + } + } else +{{/properties}} + { + ast_log(LOG_ERROR, + "ARI {{id}} has undocumented field %s\n", + ast_json_object_iter_key(iter)); + res = 0; + } + } + +{{#properties}} +{{#required}} + if (!has_{{name}}) { + ast_log(LOG_ERROR, "ARI {{id}} missing required field {{name}}\n"); + res = 0; + } + +{{/required}} +{{/properties}} + return res; +} +{{/models}} +{{/api_declaration}} +{{/apis}} diff --git a/rest-api-templates/ari_model_validators.h.mustache b/rest-api-templates/ari_model_validators.h.mustache new file mode 100644 index 0000000000000000000000000000000000000000..65efbbd85a3f3548c898d4316b95a5f09e954a08 --- /dev/null +++ b/rest-api-templates/ari_model_validators.h.mustache @@ -0,0 +1,159 @@ +/* + * 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 + */ + +#ifndef _ASTERISK_ARI_MODEL_H +#define _ASTERISK_ARI_MODEL_H + +#include "asterisk/json.h" + +/*! @{ */ + +/*! + * \brief Validator for native Swagger void. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_void(struct ast_json *json); + +/*! + * \brief Validator for native Swagger byte. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_byte(struct ast_json *json); + +/*! + * \brief Validator for native Swagger boolean. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_boolean(struct ast_json *json); + +/*! + * \brief Validator for native Swagger int. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_int(struct ast_json *json); + +/*! + * \brief Validator for native Swagger long. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_long(struct ast_json *json); + +/*! + * \brief Validator for native Swagger float. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_float(struct ast_json *json); + +/*! + * \brief Validator for native Swagger double. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_double(struct ast_json *json); + +/*! + * \brief Validator for native Swagger string. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_string(struct ast_json *json); + +/*! + * \brief Validator for native Swagger date. + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_date(struct ast_json *json); + +/*! + * \brief Validator for a Swagger List[]/JSON array. + * + * \param json JSON object to validate. + * \param fn Validator to call on every element in the array. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_list(struct ast_json *json, int (*fn)(struct ast_json *)); + +/*! @} */ +{{#apis}} +{{#api_declaration}} +{{#models}} + +/*! + * \brief Validator for {{id}}. + * + * {{{description_dox}}} + * + * \param json JSON object to validate. + * \returns True (non-zero) if valid. + * \returns False (zero) if invalid. + */ +int ari_validate_{{c_id}}(struct ast_json *json); +{{/models}} +{{/api_declaration}} +{{/apis}} + +/* + * JSON models + * +{{#apis}} +{{#api_declaration}} +{{#models}} + * {{id}} +{{#properties}} + * - {{name}}: {{type.name}}{{#required}} (required){{/required}} +{{/properties}} +{{/models}} +{{/api_declaration}} +{{/apis}} */ + +#endif /* _ASTERISK_ARI_MODEL_H */ diff --git a/rest-api-templates/asterisk_processor.py b/rest-api-templates/asterisk_processor.py index af5f5bdfe8df9303ab22983898e22521dea8240b..0260b6b55bab02c80088062afc57804df7e327b1 100644 --- a/rest-api-templates/asterisk_processor.py +++ b/rest-api-templates/asterisk_processor.py @@ -24,6 +24,11 @@ import re from swagger_model import * +try: + from collections import OrderedDict +except ImportError: + from odict import OrderedDict + def simple_name(name): """Removes the {markers} from a path segement. @@ -35,6 +40,14 @@ def simple_name(name): return name +def wikify(str): + """Escapes a string for the wiki. + + @param str: String to escape + """ + return re.sub(r'([{}\[\]])', r'\\\1', str) + + def snakify(name): """Helper to take a camelCase or dash-seperated name and make it snake_case. @@ -107,6 +120,7 @@ class PathSegment(Stringify): """ return len(self.__children) + class AsteriskProcessor(SwaggerPostProcessor): """A SwaggerPostProcessor which adds fields needed to generate Asterisk RESTful HTTP binding code. @@ -131,12 +145,17 @@ class AsteriskProcessor(SwaggerPostProcessor): 'double': 'atof', } - def process_api(self, resource_api, context): + def __init__(self, wiki_prefix): + self.wiki_prefix = wiki_prefix + + def process_resource_api(self, resource_api, context): + resource_api.wiki_prefix = self.wiki_prefix # Derive a resource name from the API declaration's filename resource_api.name = re.sub('\..*', '', os.path.basename(resource_api.path)) - # Now in all caps, from include guard + # Now in all caps, for include guard resource_api.name_caps = resource_api.name.upper() + resource_api.name_title = resource_api.name.capitalize() # Construct the PathSegement tree for the API. if resource_api.api_declaration: resource_api.root_path = PathSegment('', None) @@ -145,17 +164,6 @@ class AsteriskProcessor(SwaggerPostProcessor): for operation in api.operations: segment.operations.append(operation) api.full_name = segment.full_name - resource_api.api_declaration.has_events = False - for model in resource_api.api_declaration.models: - if model.id == "Event": - resource_api.api_declaration.has_events = True - break - if resource_api.api_declaration.has_events: - resource_api.api_declaration.events = \ - [self.process_model(model, context) for model in \ - resource_api.api_declaration.models if model.id != "Event"] - else: - resource_api.api_declaration.events = [] # Since every API path should start with /[resource], root should # have exactly one child. @@ -169,6 +177,9 @@ class AsteriskProcessor(SwaggerPostProcessor): "API declaration name should match", context) resource_api.root_full_name = resource_api.root_path.full_name + def process_api(self, api, context): + api.wiki_path = wikify(api.path) + def process_operation(self, operation, context): # Nicknames are camelcase, Asterisk coding is snake case operation.c_nickname = snakify(operation.nickname) @@ -179,7 +190,7 @@ class AsteriskProcessor(SwaggerPostProcessor): def process_parameter(self, parameter, context): if not parameter.data_type in self.type_mapping: raise SwaggerError( - "Invalid parameter type %s" % paramter.data_type, context) + "Invalid parameter type %s" % parameter.data_type, context) # Parameter names are camelcase, Asterisk convention is snake case parameter.c_name = snakify(parameter.name) parameter.c_data_type = self.type_mapping[parameter.data_type] @@ -191,41 +202,19 @@ class AsteriskProcessor(SwaggerPostProcessor): parameter.c_space = ' ' def process_model(self, model, context): + model.description_dox = model.description.replace('\n', '\n * ') + model.description_dox = re.sub(' *\n', '\n', model.description_dox) model.c_id = snakify(model.id) - model.channel = False - model.channel_desc = "" - model.bridge = False - model.bridge_desc = "" - model.properties = [self.process_property(model, prop, context) for prop in model.properties] - model.properties = [prop for prop in model.properties if prop] - model.has_properties = (len(model.properties) != 0) return model - def process_property(self, model, prop, context): - # process channel separately since it will be pulled out - if prop.name == 'channel' and prop.type == 'Channel': - model.channel = True - model.channel_desc = prop.description or "" - return None - - # process bridge separately since it will be pulled out - if prop.name == 'bridge' and prop.type == 'Bridge': - model.bridge = True - model.bridge_desc = prop.description or "" - return None - - prop.c_name = snakify(prop.name) - if prop.type in self.type_mapping: - prop.c_type = self.type_mapping[prop.type] - prop.c_convert = self.convert_mapping[prop.c_type] - else: - prop.c_type = "Property type %s not mappable to a C type" % (prop.type) - prop.c_convert = "Property type %s not mappable to a C conversion" % (prop.type) - #raise SwaggerError( - # "Invalid property type %s" % prop.type, context) - # You shouldn't put a space between 'char *' and the variable - if prop.c_type.endswith('*'): - prop.c_space = '' - else: - prop.c_space = ' ' - return prop + def process_property(self, prop, context): + if "-" in prop.name: + raise SwaggerError("Property names cannot have dashes", context) + if prop.name != prop.name.lower(): + raise SwaggerError("Property name should be all lowercase", + context) + + def process_type(self, swagger_type, context): + swagger_type.c_name = snakify(swagger_type.name) + swagger_type.c_singular_name = snakify(swagger_type.singular_name) + swagger_type.wiki_name = wikify(swagger_type.name) diff --git a/rest-api-templates/event_function_decl.mustache b/rest-api-templates/event_function_decl.mustache deleted file mode 100644 index fd2c7eb5bbc1a0d4fc0ddb9e0b7debe2819e133b..0000000000000000000000000000000000000000 --- a/rest-api-templates/event_function_decl.mustache +++ /dev/null @@ -1,10 +0,0 @@ -struct ast_json *stasis_json_event_{{c_id}}_create( -{{#bridge}} - struct ast_bridge_snapshot *bridge_snapshot{{#channel}},{{/channel}}{{^channel}}{{#has_properties}},{{/has_properties}}{{/channel}} -{{/bridge}} -{{#channel}} - struct ast_channel_snapshot *channel_snapshot{{#has_properties}},{{/has_properties}} -{{/channel}} -{{#has_properties}} - struct ast_json *blob -{{/has_properties}} diff --git a/rest-api-templates/make_stasis_http_stubs.py b/rest-api-templates/make_ari_stubs.py similarity index 73% rename from rest-api-templates/make_stasis_http_stubs.py rename to rest-api-templates/make_ari_stubs.py index 1114ea46e179c3f3147c221ea5fb9601cd917eb5..6f59e3813aa3950412c2514a714a4327968ef4f9 100755 --- a/rest-api-templates/make_stasis_http_stubs.py +++ b/rest-api-templates/make_ari_stubs.py @@ -22,7 +22,6 @@ except ImportError: print >> sys.stderr, "Pystache required. Please sudo pip install pystache." import os.path -import pystache import sys from asterisk_processor import AsteriskProcessor @@ -40,23 +39,27 @@ def rel(file): """ return os.path.join(TOPDIR, file) +WIKI_PREFIX = 'Asterisk 12' + API_TRANSFORMS = [ + Transform(rel('api.wiki.mustache'), + 'doc/rest-api/%s {{name_title}} REST API.wiki' % WIKI_PREFIX), Transform(rel('res_stasis_http_resource.c.mustache'), - 'res_stasis_http_{{name}}.c'), + 'res/res_stasis_http_{{name}}.c'), Transform(rel('stasis_http_resource.h.mustache'), - 'stasis_http/resource_{{name}}.h'), + 'res/stasis_http/resource_{{name}}.h'), Transform(rel('stasis_http_resource.c.mustache'), - 'stasis_http/resource_{{name}}.c', False), - Transform(rel('res_stasis_json_resource.c.mustache'), - 'res_stasis_json_{{name}}.c'), - Transform(rel('res_stasis_json_resource.exports.mustache'), - 'res_stasis_json_{{name}}.exports.in'), - Transform(rel('stasis_json_resource.h.mustache'), - 'stasis_json/resource_{{name}}.h'), + 'res/stasis_http/resource_{{name}}.c', overwrite=False), ] RESOURCES_TRANSFORMS = [ - Transform(rel('stasis_http.make.mustache'), 'stasis_http.make'), + Transform(rel('models.wiki.mustache'), + 'doc/rest-api/%s REST Data Models.wiki' % WIKI_PREFIX), + Transform(rel('stasis_http.make.mustache'), 'res/stasis_http.make'), + Transform(rel('ari_model_validators.h.mustache'), + 'res/stasis_http/ari_model_validators.h'), + Transform(rel('ari_model_validators.c.mustache'), + 'res/stasis_http/ari_model_validators.c'), ] @@ -71,7 +74,7 @@ def main(argv): source = args[1] dest_dir = args[2] renderer = pystache.Renderer(search_dirs=[TOPDIR], missing_tags='strict') - processor = AsteriskProcessor() + processor = AsteriskProcessor(wiki_prefix=WIKI_PREFIX) # Build the models base_dir = os.path.dirname(source) diff --git a/rest-api-templates/models.wiki.mustache b/rest-api-templates/models.wiki.mustache new file mode 100644 index 0000000000000000000000000000000000000000..e3d3eb95caf98333d2524a0897182de03394fd8e --- /dev/null +++ b/rest-api-templates/models.wiki.mustache @@ -0,0 +1,22 @@ +{toc} + +{{#apis}} +{{#api_declaration}} +{{#models}} +h1. {{id}} +{{#extends}}Base type: [{{extends}}|#{{extends}}]{{/extends}} +{{#has_subtypes}}Subtypes:{{#subtypes}} [{{id}}|#{{id}}]{{/subtypes}}{{/has_subtypes}} +{{#description}} + +{{{description}}} +{{/description}} +{code:language=javascript|collapse=true} +{{{model_json}}} +{code} +{{#properties}} +* {{name}}: {{#type}}{{#is_primitive}}{{wiki_name}}{{/is_primitive}}{{^is_primitive}}[{{wiki_name}}|#{{singular_name}}]{{/is_primitive}}{{/type}}{{^required}} _(optional)_{{/required}}{{#description}} - {{{description}}}{{/description}} +{{/properties}} + +{{/models}} +{{/api_declaration}} +{{/apis}} diff --git a/rest-api-templates/res_stasis_http_resource.c.mustache b/rest-api-templates/res_stasis_http_resource.c.mustache index 0bdc1d0140714333ee51f0ba58e9ee11494f9d22..0f0535bcf0d3d6edf92e8da7ebeffbf950a647ad 100644 --- a/rest-api-templates/res_stasis_http_resource.c.mustache +++ b/rest-api-templates/res_stasis_http_resource.c.mustache @@ -49,6 +49,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/module.h" #include "asterisk/stasis_app.h" #include "stasis_http/resource_{{name}}.h" +#if defined(AST_DEVMODE) +#include "stasis_http/ari_model_validators.h" +#endif {{#apis}} {{#operations}} @@ -61,11 +64,50 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") * \param[out] response Response to the HTTP request. */ static void stasis_http_{{c_nickname}}_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 */ + {{> param_parsing}} stasis_http_{{c_nickname}}(headers, &args, response); +#if defined(AST_DEVMODE) + code = response->response_code; + + switch (code) { + case 500: /* Internal server error */ +{{#error_responses}} + case {{code}}: /* {{{reason}}} */ +{{/error_responses}} + is_valid = 1; + break; + default: + if (200 <= code && code <= 299) { +{{#response_class}} +{{#is_list}} + is_valid = ari_validate_list(response->message, + ari_validate_{{c_singular_name}}); +{{/is_list}} +{{^is_list}} + is_valid = ari_validate_{{c_name}}( + response->message); +{{/is_list}} +{{/response_class}} + } else { + ast_log(LOG_ERROR, "Invalid error response %d for {{path}}\n", code); + is_valid = 0; + } + } + + if (!is_valid) { + ast_log(LOG_ERROR, "Response validation failed for {{path}}\n"); + stasis_http_response_error(response, 500, + "Internal Server Error", "Response validation failed"); + } +#endif /* AST_DEVMODE */ } {{/is_req}} {{#is_websocket}} @@ -81,7 +123,12 @@ static void stasis_http_{{c_nickname}}_ws_cb(struct ast_websocket *ws_session, struct ast_variable *path_vars = NULL; {{/has_path_parameters}} {{> param_parsing}} - session = ari_websocket_session_create(ws_session); +#if defined(AST_DEVMODE) + session = ari_websocket_session_create(ws_session, + ari_validate_{{response_class.c_name}}); +#else + session = ari_websocket_session_create(ws_session, NULL); +#endif if (!session) { ast_log(LOG_ERROR, "Failed to create ARI session\n"); return; diff --git a/rest-api-templates/res_stasis_json_resource.c.mustache b/rest-api-templates/res_stasis_json_resource.c.mustache deleted file mode 100644 index a25bdc22821261c7fa68715ba9f5264fccb0f5de..0000000000000000000000000000000000000000 --- a/rest-api-templates/res_stasis_json_resource.c.mustache +++ /dev/null @@ -1,151 +0,0 @@ -{{#api_declaration}} -/* - * Asterisk -- An open source telephony toolkit. - * - * {{{copyright}}} - * - * {{{author}}} -{{! Template Copyright - * Copyright (C) 2013, Digium, Inc. - * - * Kinsey Moore <kmoore@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. - */ - -{{! Template for rendering the res_ module for an HTTP resource. }} -/* -{{> 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 {{{description}}} - * - * \author {{{author}}} - */ - -/*** 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_{{name}}.h" -{{#has_events}} -#include "asterisk/stasis_channels.h" -#include "asterisk/stasis_bridging.h" - -{{#events}} -{{> event_function_decl}} - ) -{ - RAII_VAR(struct ast_json *, message, NULL, ast_json_unref); - RAII_VAR(struct ast_json *, event, NULL, ast_json_unref); -{{#has_properties}} - struct ast_json *validator; -{{/has_properties}} -{{#channel}} - int ret; -{{/channel}} -{{#bridge}} -{{^channel}} - int ret; -{{/channel}} -{{/bridge}} - -{{#channel}} - ast_assert(channel_snapshot != NULL); -{{/channel}} -{{#bridge}} - ast_assert(bridge_snapshot != NULL); -{{/bridge}} -{{#has_properties}} - ast_assert(blob != NULL); -{{#channel}} - ast_assert(ast_json_object_get(blob, "channel") == NULL); -{{/channel}} -{{#bridge}} - ast_assert(ast_json_object_get(blob, "bridge") == NULL); -{{/bridge}} - ast_assert(ast_json_object_get(blob, "type") == NULL); -{{#properties}} - - validator = ast_json_object_get(blob, "{{name}}"); - if (validator) { - /* do validation? XXX */ -{{#required}} - } else { - /* fail message generation if the required parameter doesn't exist */ - return NULL; -{{/required}} - } -{{/properties}} - - event = ast_json_deep_copy(blob); -{{/has_properties}} -{{^has_properties}} - - event = ast_json_object_create(); -{{/has_properties}} - if (!event) { - return NULL; - } - -{{#channel}} - ret = ast_json_object_set(event, - "channel", ast_channel_snapshot_to_json(channel_snapshot)); - if (ret) { - return NULL; - } - -{{/channel}} -{{#bridge}} - ret = ast_json_object_set(event, - "bridge", ast_bridge_snapshot_to_json(bridge_snapshot)); - if (ret) { - return NULL; - } - -{{/bridge}} - message = ast_json_pack("{s: o}", "{{c_id}}", ast_json_ref(event)); - if (!message) { - return NULL; - } - - return ast_json_ref(message); -} - -{{/events}} -{{/has_events}} -static int load_module(void) -{ - return 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 - {{{description}}}", - .load = load_module, - .unload = unload_module, - .load_pri = AST_MODPRI_DEFAULT, - ); -{{/api_declaration}} diff --git a/rest-api-templates/res_stasis_json_resource.exports.mustache b/rest-api-templates/res_stasis_json_resource.exports.mustache deleted file mode 100644 index 0f958fa04890c3f4a9ba4c382208fe03dcce7f8e..0000000000000000000000000000000000000000 --- a/rest-api-templates/res_stasis_json_resource.exports.mustache +++ /dev/null @@ -1,12 +0,0 @@ -{ -{{#api_declaration}} -{{#has_events}} - global: -{{#events}} - LINKER_SYMBOL_PREFIXstasis_json_event_{{c_id}}_create; -{{/events}} -{{/has_events}} -{{/api_declaration}} - local: - *; -}; diff --git a/rest-api-templates/stasis_json_resource.h.mustache b/rest-api-templates/stasis_json_resource.h.mustache deleted file mode 100644 index 8cfd2c1f7e185b0a635d39ecaa99bd08dd06aff8..0000000000000000000000000000000000000000 --- a/rest-api-templates/stasis_json_resource.h.mustache +++ /dev/null @@ -1,83 +0,0 @@ -{{#api_declaration}} -/* - * Asterisk -- An open source telephony toolkit. - * - * {{{copyright}}} - * - * {{{author}}} - * - * 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 - declares stubs to be implemented in - * res/stasis_json/resource_{{name}}.c - * - * {{{description}}} - * - * \author {{{author}}} - */ - -/* -{{> do-not-edit}} - * This file is generated by a mustache template. Please see the original - * template in rest-api-templates/stasis_http_resource.h.mustache - */ - -#ifndef _ASTERISK_RESOURCE_{{name_caps}}_H -#define _ASTERISK_RESOURCE_{{name_caps}}_H - -{{#has_events}} -struct ast_channel_snapshot; -struct ast_bridge_snapshot; - -{{#events}} -/*! - * \brief {{description}} -{{#notes}} - * - * {{{notes}}} -{{/notes}} - * -{{#channel}} - * \param channel {{#channel_desc}}{{channel_desc}}{{/channel_desc}}{{^channel_desc}}The channel to be used to generate this event{{/channel_desc}} -{{/channel}} -{{#bridge}} - * \param bridge {{#bridge_desc}}{{bridge_desc}}{{/bridge_desc}}{{^bridge_desc}}The bridge to be used to generate this event{{/bridge_desc}} -{{/bridge}} -{{#has_properties}} - * \param blob JSON blob containing the following parameters: -{{/has_properties}} -{{#properties}} - * - {{name}}: {{type}} {{#description}}- {{description}}{{/description}}{{#required}} (required){{/required}} -{{/properties}} - * - * \retval NULL on error - * \retval JSON (ast_json) describing the event - */ -{{> event_function_decl}} - ); - -{{/events}} -{{/has_events}} -/* - * JSON models - * -{{#models}} - * {{id}} -{{#properties}} - * - {{name}}: {{type}}{{#required}} (required){{/required}} -{{/properties}} -{{/models}} */ - -#endif /* _ASTERISK_RESOURCE_{{name_caps}}_H */ -{{/api_declaration}} diff --git a/rest-api-templates/swagger_model.py b/rest-api-templates/swagger_model.py index 47461b40682077881a99ab53bad99e5b83147902..2907688c5231680a2584a32f2c147f1ef3462e5e 100644 --- a/rest-api-templates/swagger_model.py +++ b/rest-api-templates/swagger_model.py @@ -29,16 +29,101 @@ See https://github.com/wordnik/swagger-core/wiki/API-Declaration for the spec. import json import os.path import pprint +import re import sys import traceback -try: - from collections import OrderedDict -except ImportError: - from odict import OrderedDict +# I'm not quite sure what was in Swagger 1.2, but apparently I missed it +SWAGGER_VERSIONS = ["1.1", "1.3"] +SWAGGER_PRIMITIVES = [ + 'void', + 'string', + 'boolean', + 'number', + 'int', + 'long', + 'double', + 'float', + 'Date', +] -SWAGGER_VERSION = "1.1" + +class Stringify(object): + """Simple mix-in to make the repr of the model classes more meaningful. + """ + def __repr__(self): + return "%s(%s)" % (self.__class__, pprint.saferepr(self.__dict__)) + + +def compare_versions(lhs, rhs): + '''Performs a lexicographical comparison between two version numbers. + + This properly handles simple major.minor.whatever.sure.why.not version + numbers, but fails miserably if there's any letters in there. + + For reference: + 1.0 == 1.0 + 1.0 < 1.0.1 + 1.2 < 1.10 + + @param lhs Left hand side of the comparison + @param rhs Right hand side of the comparison + @return < 0 if lhs < rhs + @return == 0 if lhs == rhs + @return > 0 if lhs > rhs + ''' + lhs = [int(v) for v in lhs.split('.')] + rhs = [int(v) for v in rhs.split('.')] + return cmp(lhs, rhs) + + +class ParsingContext(object): + """Context information for parsing. + + This object is immutable. To change contexts (like adding an item to the + stack), use the next() and next_stack() functions to build a new one. + """ + + def __init__(self, swagger_version, stack): + self.__swagger_version = swagger_version + self.__stack = stack + + def __repr__(self): + return "ParsingContext(swagger_version=%s, stack=%s)" % ( + self.swagger_version, self.stack) + + def get_swagger_version(self): + return self.__swagger_version + + def get_stack(self): + return self.__stack + + swagger_version = property(get_swagger_version) + + stack = property(get_stack) + + def version_less_than(self, ver): + return compare_versions(self.swagger_version, ver) < 0 + + def next_stack(self, json, id_field): + """Returns a new item pushed to the stack. + + @param json: Current JSON object. + @param id_field: Field identifying this object. + @return New context with additional item in the stack. + """ + if not id_field in json: + raise SwaggerError("Missing id_field: %s" % id_field, self) + new_stack = self.stack + ['%s=%s' % (id_field, str(json[id_field]))] + return ParsingContext(self.swagger_version, new_stack) + + def next(self, version=None, stack=None): + if version is None: + version = self.version + if stack is None: + stack = self.stack + return ParsingContext(version, stack) class SwaggerError(Exception): @@ -50,7 +135,7 @@ class SwaggerError(Exception): """Ctor. @param msg: String message for the error. - @param context: Array of strings for current context in the API. + @param context: ParsingContext object @param cause: Optional exception that caused this one. """ super(Exception, self).__init__(msg, context, cause) @@ -61,7 +146,7 @@ class SwaggerPostProcessor(object): fields to model objects for additional information to use in the templates. """ - def process_api(self, resource_api, context): + def process_resource_api(self, resource_api, context): """Post process a ResourceApi object. @param resource_api: ResourceApi object. @@ -69,6 +154,14 @@ class SwaggerPostProcessor(object): """ pass + def process_api(self, api, context): + """Post process an Api object. + + @param api: Api object. + @param context: Current context in the API. + """ + pass + def process_operation(self, operation, context): """Post process a Operation object. @@ -85,12 +178,37 @@ class SwaggerPostProcessor(object): """ pass + def process_model(self, model, context): + """Post process a Model object. -class Stringify(object): - """Simple mix-in to make the repr of the model classes more meaningful. - """ - def __repr__(self): - return "%s(%s)" % (self.__class__, pprint.saferepr(self.__dict__)) + @param model: Model object. + @param context: Current context in the API. + """ + pass + + def process_property(self, property, context): + """Post process a Property object. + + @param property: Property object. + @param context: Current context in the API. + """ + pass + + def process_type(self, swagger_type, context): + """Post process a SwaggerType object. + + @param swagger_type: ResourceListing object. + @param context: Current context in the API. + """ + pass + + def process_resource_listing(self, resource_listing, context): + """Post process the overall ResourceListing object. + + @param resource_listing: ResourceListing object. + @param context: Current context in the API. + """ + pass class AllowableRange(Stringify): @@ -158,17 +276,22 @@ class Parameter(Stringify): self.allow_multiple = None def load(self, parameter_json, processor, context): - context = add_context(context, parameter_json, 'name') + context = context.next_stack(parameter_json, 'name') validate_required_fields(parameter_json, self.required_fields, context) self.name = parameter_json.get('name') self.param_type = parameter_json.get('paramType') self.description = parameter_json.get('description') or '' self.data_type = parameter_json.get('dataType') self.required = parameter_json.get('required') or False + self.default_value = parameter_json.get('defaultValue') self.allowable_values = load_allowable_values( parameter_json.get('allowableValues'), context) self.allow_multiple = parameter_json.get('allowMultiple') or False processor.process_parameter(self, context) + if parameter_json.get('allowedValues'): + raise SwaggerError( + "Field 'allowedValues' invalid; use 'allowableValues'", + context) return self def is_type(self, other_type): @@ -188,13 +311,41 @@ class ErrorResponse(Stringify): self.reason = None def load(self, err_json, processor, context): - context = add_context(context, err_json, 'code') + context = context.next_stack(err_json, 'code') validate_required_fields(err_json, self.required_fields, context) self.code = err_json.get('code') self.reason = err_json.get('reason') return self +class SwaggerType(Stringify): + """Model of a data type. + """ + + def __init__(self): + self.name = None + self.is_discriminator = None + self.is_list = None + self.singular_name = None + self.is_primitive = None + + def load(self, type_name, processor, context): + # Some common errors + if type_name == 'integer': + raise SwaggerError("The type for integer should be 'int'", context) + + self.name = type_name + type_param = get_list_parameter_type(self.name) + self.is_list = type_param is not None + if self.is_list: + self.singular_name = type_param + else: + self.singular_name = self.name + self.is_primitive = self.singular_name in SWAGGER_PRIMITIVES + processor.process_type(self, context) + return self + + class Operation(Stringify): """Model of an operation on an API @@ -213,11 +364,14 @@ class Operation(Stringify): self.error_responses = [] def load(self, op_json, processor, context): - context = add_context(context, op_json, 'nickname') + context = context.next_stack(op_json, 'nickname') validate_required_fields(op_json, self.required_fields, context) self.http_method = op_json.get('httpMethod') self.nickname = op_json.get('nickname') - self.response_class = op_json.get('responseClass') + response_class = op_json.get('responseClass') + self.response_class = response_class and SwaggerType().load( + response_class, processor, context) + # Specifying WebSocket URL's is our own extension self.is_websocket = op_json.get('upgrade') == 'websocket' self.is_req = not self.is_websocket @@ -247,6 +401,7 @@ class Operation(Stringify): err_json = op_json.get('errorResponses') or [] self.error_responses = [ ErrorResponse().load(j, processor, context) for j in err_json] + self.has_error_responses = self.error_responses != [] processor.process_operation(self, context) return self @@ -265,7 +420,7 @@ class Api(Stringify): self.operations = [] def load(self, api_json, processor, context): - context = add_context(context, api_json, 'path') + context = context.next_stack(api_json, 'path') validate_required_fields(api_json, self.required_fields, context) self.path = api_json.get('path') self.description = api_json.get('description') @@ -274,9 +429,20 @@ class Api(Stringify): Operation().load(j, processor, context) for j in op_json] self.has_websocket = \ filter(lambda op: op.is_websocket, self.operations) != [] + processor.process_api(self, context) return self +def get_list_parameter_type(type_string): + """Returns the type parameter if the given type_string is List[]. + + @param type_string: Type string to parse + @returns Type parameter of the list, or None if not a List. + """ + list_match = re.match('^List\[(.*)\]$', type_string) + return list_match and list_match.group(1) + + class Property(Stringify): """Model of a Swagger property. @@ -293,9 +459,15 @@ class Property(Stringify): def load(self, property_json, processor, context): validate_required_fields(property_json, self.required_fields, context) - self.type = property_json.get('type') + # Bit of a hack, but properties do not self-identify + context = context.next_stack({'name': self.name}, 'name') self.description = property_json.get('description') or '' self.required = property_json.get('required') or False + + type = property_json.get('type') + self.type = type and SwaggerType().load(type, processor, context) + + processor.process_property(self, context) return self @@ -305,24 +477,95 @@ class Model(Stringify): See https://github.com/wordnik/swagger-core/wiki/datatypes """ + required_fields = ['description', 'properties'] + def __init__(self): self.id = None + self.extends = None + self.extends_type = None self.notes = None self.description = None - self.properties = None + self.__properties = None + self.__discriminator = None + self.__subtypes = [] def load(self, id, model_json, processor, context): - context = add_context(context, model_json, 'id') - # This arrangement is required by the Swagger API spec + context = context.next_stack(model_json, 'id') + validate_required_fields(model_json, self.required_fields, context) + # The duplication of the model's id is required by the Swagger spec. self.id = model_json.get('id') if id != self.id: - raise SwaggerError("Model id doesn't match name", c) + raise SwaggerError("Model id doesn't match name", context) + self.extends = model_json.get('extends') + if self.extends and context.version_less_than("1.3"): + raise SwaggerError("Type extension support added in Swagger 1.3", + context) self.description = model_json.get('description') props = model_json.get('properties').items() or [] - self.properties = [ + self.__properties = [ Property(k).load(j, processor, context) for (k, j) in props] + self.__properties = sorted(self.__properties, key=lambda p: p.name) + + discriminator = model_json.get('discriminator') + + if discriminator: + if context.version_less_than("1.3"): + raise SwaggerError("Discriminator support added in Swagger 1.3", + context) + + discr_props = [p for p in self.__properties if p.name == discriminator] + if not discr_props: + raise SwaggerError( + "Discriminator '%s' does not name a property of '%s'" % ( + discriminator, self.id), + context) + + self.__discriminator = discr_props[0] + + self.model_json = json.dumps(model_json, + indent=2, separators=(',', ': ')) + + processor.process_model(self, context) return self + def add_subtype(self, subtype): + """Add subtype to this model. + + @param subtype: Model instance for the subtype. + """ + self.__subtypes.append(subtype) + + def set_extends_type(self, extends_type): + self.extends_type = extends_type + + def discriminator(self): + """Returns the discriminator, digging through base types if needed. + """ + return self.__discriminator or \ + self.extends_type and self.extends_type.discriminator() + + def properties(self): + base_props = [] + if self.extends_type: + base_props = self.extends_type.properties() + return base_props + self.__properties + + def has_properties(self): + return len(self.properties()) > 0 + + def subtypes(self): + """Returns the full list of all subtypes. + """ + res = self.__subtypes + \ + [subsubtypes for subtype in self.__subtypes + for subsubtypes in subtype.subtypes()] + return sorted(res, key=lambda m: m.id) + + def has_subtypes(self): + """Returns True if type has any subtypes. + """ + return len(self.subtypes()) > 0 + class ApiDeclaration(Stringify): """Model class for an API Declaration. @@ -345,8 +588,8 @@ class ApiDeclaration(Stringify): self.apis = [] self.models = [] - def load_file(self, api_declaration_file, processor, context=[]): - context = context + [api_declaration_file] + def load_file(self, api_declaration_file, processor): + context = ParsingContext(None, [api_declaration_file]) try: return self.__load_file(api_declaration_file, processor, context) except SwaggerError: @@ -376,9 +619,10 @@ class ApiDeclaration(Stringify): """ # If the version doesn't match, all bets are off. self.swagger_version = api_decl_json.get('swaggerVersion') - if self.swagger_version != SWAGGER_VERSION: + context = context.next(version=self.swagger_version) + if not self.swagger_version in SWAGGER_VERSIONS: raise SwaggerError( - "Unsupported Swagger version %s" % swagger_version, context) + "Unsupported Swagger version %s" % self.swagger_version, context) validate_required_fields(api_decl_json, self.required_fields, context) @@ -391,9 +635,19 @@ class ApiDeclaration(Stringify): self.apis = [ Api().load(j, processor, context) for j in api_json] models = api_decl_json.get('models').items() or [] - self.models = [ - Model().load(k, j, processor, context) for (k, j) in models] - + self.models = [Model().load(id, json, processor, context) + for (id, json) in models] + self.models = sorted(self.models, key=lambda m: m.id) + # Now link all base/extended types + model_dict = dict((m.id, m) for m in self.models) + for m in self.models: + if m.extends: + extends_type = model_dict.get(m.extends) + if not extends_type: + raise SwaggerError("%s extends non-existing model %s", + m.id, m.extends) + extends_type.add_subtype(m) + m.set_extends_type(extends_type) return self @@ -409,20 +663,20 @@ class ResourceApi(Stringify): self.api_declaration = None def load(self, api_json, processor, context): - context = add_context(context, api_json, 'path') + context = context.next_stack(api_json, 'path') validate_required_fields(api_json, self.required_fields, context) self.path = api_json['path'] self.description = api_json['description'] if not self.path or self.path[0] != '/': raise SwaggerError("Path must start with /", context) - processor.process_api(self, context) + processor.process_resource_api(self, context) return self def load_api_declaration(self, base_dir, processor): self.file = (base_dir + self.path).replace('{format}', 'json') self.api_declaration = ApiDeclaration().load_file(self.file, processor) - processor.process_api(self, [self.file]) + processor.process_resource_api(self, [self.file]) class ResourceListing(Stringify): @@ -438,7 +692,7 @@ class ResourceListing(Stringify): self.apis = None def load_file(self, resource_file, processor): - context = [resource_file] + context = ParsingContext(None, [resource_file]) try: return self.__load_file(resource_file, processor, context) except SwaggerError: @@ -455,7 +709,7 @@ class ResourceListing(Stringify): def load(self, resources_json, processor, context): # If the version doesn't match, all bets are off. self.swagger_version = resources_json.get('swaggerVersion') - if self.swagger_version != SWAGGER_VERSION: + if not self.swagger_version in SWAGGER_VERSIONS: raise SwaggerError( "Unsupported Swagger version %s" % swagger_version, context) @@ -465,6 +719,7 @@ class ResourceListing(Stringify): apis_json = resources_json['apis'] self.apis = [ ResourceApi().load(j, processor, context) for j in apis_json] + processor.process_resource_listing(self, context) return self @@ -482,16 +737,3 @@ def validate_required_fields(json, required_fields, context): if missing_fields: raise SwaggerError( "Missing fields: %s" % ', '.join(missing_fields), context) - - -def add_context(context, json, id_field): - """Returns a new context with a new item added to it. - - @param context: Old context. - @param json: Current JSON object. - @param id_field: Field identifying this object. - @return New context with additional item. - """ - if not id_field in json: - raise SwaggerError("Missing id_field: %s" % id_field, context) - return context + ['%s=%s' % (id_field, str(json[id_field]))] diff --git a/rest-api-templates/transform.py b/rest-api-templates/transform.py index d0ef3c4a162eb243514363db33dd69f5dd3fa1ed..fc12efe85938107a8810009519858d785f83b735 100644 --- a/rest-api-templates/transform.py +++ b/rest-api-templates/transform.py @@ -16,8 +16,11 @@ # at the top of the source tree. # +import filecmp import os.path import pystache +import shutil +import tempfile class Transform(object): @@ -46,8 +49,14 @@ class Transform(object): """ dest_file = pystache.render(self.dest_file_template, model) dest_file = os.path.join(dest_dir, dest_file) - if os.path.exists(dest_file) and not self.overwrite: + dest_exists = os.path.exists(dest_file) + if dest_exists and not self.overwrite: return - print "Rendering %s" % dest_file - with open(dest_file, "w") as out: + tmp_file = tempfile.mkstemp() + with tempfile.NamedTemporaryFile() as out: out.write(renderer.render(self.template, model)) + out.flush() + + if not dest_exists or not filecmp.cmp(out.name, dest_file): + print "Writing %s" % dest_file + shutil.copyfile(out.name, dest_file) diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json index ef6c7b864ca4c4b77b8b197ec0d66b9c6f7d768f..8ee88e4394df92f2010fe35592d95ca508a61030 100644 --- a/rest-api/api-docs/asterisk.json +++ b/rest-api/api-docs/asterisk.json @@ -41,6 +41,7 @@ "models": { "AsteriskInfo": { "id": "AsteriskInfo", + "description": "Asterisk system information", "properties": {} } } diff --git a/rest-api/api-docs/bridges.json b/rest-api/api-docs/bridges.json index 5b0cf629852958dfa0427b6a6ee5cd358783a807..87d5b3d4f52cd2054954cd914d2b0ffb1f82c67c 100644 --- a/rest-api/api-docs/bridges.json +++ b/rest-api/api-docs/bridges.json @@ -31,8 +31,8 @@ "required": false, "allowMultiple": false, "dataType": "string", - "allowedValues": { - "type": "LIST", + "allowableValues": { + "valueType": "LIST", "values": [ "mixing", "holding" @@ -61,6 +61,12 @@ "allowMultiple": false, "dataType": "string" } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Bridge not found" + } ] }, { @@ -78,6 +84,12 @@ "allowMultiple": false, "dataType": "string" } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Bridge not found" + } ] } ] @@ -108,6 +120,20 @@ "allowMultiple": true, "dataType": "string" } + ], + "errorResponses": [ + { + "code": 404, + "reason": "Bridge not found" + }, + { + "code": 409, + "reason": "Bridge not in Stasis application" + }, + { + "code": 422, + "reason": "Channel not found, or not in Stasis application" + } ] } ] @@ -231,19 +257,35 @@ "models": { "Bridge": { "id": "Bridge", + "description": "The merging of media from one or more channels.\n\nEveryone on the bridge receives the same audio.", "properties": { - "bridgeType": { + "id": { + "type": "string", + "description": "Unique identifier for this bridge", + "required": true + }, + "technology": { + "type": "string", + "description": "Name of the current bridging technology", + "required": true + }, + "bridge_type": { "type": "string", "description": "Type of bridge technology", "required": true, - "allowedValues": { - "type": "LIST", + "allowableValues": { + "valueType": "LIST", "values": [ "mixing", "holding" ] } }, + "bridge_class": { + "type": "string", + "description": "Bridging class", + "required": true + }, "channels": { "type": "List[string]", "description": "Id's of channels participating in this bridge", diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json index 623cb17bb24827352b4f81ec64f416fcd7401318..f013ef6416d3c97242b01cfd59c8b5d36b89bb71 100644 --- a/rest-api/api-docs/channels.json +++ b/rest-api/api-docs/channels.json @@ -286,6 +286,10 @@ { "code": 404, "reason": "Channel not found" + }, + { + "code": 409, + "reason": "Channel not in a Stasis application" } ] } @@ -616,11 +620,7 @@ }, { "code": 409, - "reason": "Channel is not in a Stasis application." - }, - { - "code": 409, - "reason": "The channel is currently bridged with other channels." + "reason": "Channel is not in a Stasis application, or the channel is currently bridged with other channels." } ] } @@ -630,10 +630,12 @@ "models": { "Dialed": { "id": "Dialed", + "description": "Dialed channel information.", "properties": {} }, "DialplanCEP": { "id": "DialplanCEP", + "description": "Dialplan location (context/extension/priority)", "properties": { "context": { "required": true, @@ -654,6 +656,7 @@ }, "CallerID": { "id": "CallerID", + "description": "Caller identification", "properties": { "name": { "required": true, @@ -667,11 +670,12 @@ }, "Channel": { "id": "Channel", + "description": "A specific communication connection between Asterisk and an Endpoint.", "properties": { - "uniqueid": { + "id": { "required": true, "type": "string", - "description": "Unique identifier of the channel" + "description": "Unique identifier of the channel.\n\nThis is the same as the Uniqueid field in AMI." }, "name": { "required": true, @@ -680,99 +684,47 @@ }, "state": { "required": true, - "type": "string" - }, - "accountcode": { - "required": true, - "type": "string" - }, - "peeraccount": { - "required": true, - "type": "string" - }, - "userfield": { - "required": true, - "type": "string" + "type": "string", + "allowableValues": { + "valueType": "LIST", + "values": [ + "Down", + "Rsrved", + "OffHook", + "Dialing", + "Ring", + "Ringing", + "Up", + "Busy", + "Dialing Offhook", + "Pre-ring", + "Unknown" + ] + } }, - "linkedid": { + "caller": { "required": true, - "type": "string" + "type": "CallerID" }, - "parkinglot": { + "connected": { "required": true, - "type": "string" + "type": "CallerID" }, - "hangupsource": { + "accountcode": { "required": true, "type": "string" }, - "appl": { - "required": true, - "type": "string", - "description": "Currently executing dialplan application" - }, - "data": { - "required": true, - "type": "string", - "description": "Arguments passed to appl" - }, "dialplan": { "required": true, "type": "DialplanCEP", "description": "Current location in the dialplan" }, - "caller": { - "required": true, - "type": "CallerID" - }, - "connected": { - "required": true, - "type": "CallerID" - }, "creationtime": { "required": true, "type": "Date", "description": "Timestamp when channel was created" } } - }, - "Playback": { - "id": "Playback", - "description": "Object representing the playback of media to a channel", - "properties": { - "id": { - "type": "string", - "description": "ID for this playback operation", - "required": true - }, - "media_uri": { - "type": "string", - "description": "URI for the media to play back.", - "required": true - }, - "target_uri": { - "type": "string", - "description": "URI for the channel or bridge to play the media on", - "required": true - }, - "language": { - "type": "string", - "description": "For media types that support multiple languages, the language requested for playback." - }, - "state": { - "type": "string", - "description": "Current state of the playback operation.", - "required": true, - "allowableValues": { - "valueType": "LIST", - "values": [ - "queued", - "playing", - "complete" - ] - } - } - } } } } diff --git a/rest-api/api-docs/endpoints.json b/rest-api/api-docs/endpoints.json index d3d77d84aa939d0672e4743db68cb5ebfa779f3e..9d0ff1840379ca322c6cb1e8d280bdf604e1a87f 100644 --- a/rest-api/api-docs/endpoints.json +++ b/rest-api/api-docs/endpoints.json @@ -69,7 +69,7 @@ "models": { "Endpoint": { "id": "Endpoint", - "description": "A snapshot of an endpoint. Unlike most resources, which have a single unique identifier, an endpoint is uniquely identified by the technology/resource pair.", + "description": "An external device that may offer/accept calls to/from Asterisk.\n\nUnlike most resources, which have a single unique identifier, an endpoint is uniquely identified by the technology/resource pair.", "properties": { "technology": { "type": "string", @@ -80,6 +80,24 @@ "type": "string", "description": "Identifier of the endpoint, specific to the given technology.", "required": true + }, + "state": { + "type": "string", + "description": "Endpoint's state", + "required": false, + "allowableValues": { + "valueType": "LIST", + "values": [ + "unknown", + "offline", + "online" + ] + } + }, + "channel_ids": { + "type": "List[string]", + "description": "Id's of channels associated with this endpoint", + "required": true } } } diff --git a/rest-api/api-docs/events.json b/rest-api/api-docs/events.json index 56a05e4ee9d49622ecbea1121a07478d5adac39b..79908eff79f6b28fb3362ddcafe72d09a76fc563 100644 --- a/rest-api/api-docs/events.json +++ b/rest-api/api-docs/events.json @@ -3,7 +3,7 @@ "_author": "David M. Lee, II <dlee@digium.com>", "_svn_revision": "$Revision$", "apiVersion": "0.0.1", - "swaggerVersion": "1.1", + "swaggerVersion": "1.3", "basePath": "http://localhost:8088/stasis", "resourcePath": "/api-docs/events.{format}", "apis": [ @@ -35,37 +35,29 @@ "models": { "Event": { "id": "Event", - "description": "Asynchronous events from Asterisk. The non-required fields of this object are mutually exclusive.", + "description": "Base type for asynchronous events from Asterisk.", + "discriminator": "type", "properties": { + "type": { + "type": "string", + "required": true, + "description": "Indicates the type of this event." + }, "application": { "type": "string", "description": "Name of the application receiving the event.", "required": true }, - "application_replaced": { "type": "ApplicationReplaced" }, - "bridge_created": { "type": "BridgeCreated" }, - "bridge_destroyed": { "type": "BridgeDestroyed" }, - "bridge_merged": { "type": "BridgeMerged" }, - "channel_created": { "type": "ChannelCreated" }, - "channel_destroyed": { "type": "ChannelDestroyed" }, - "channel_snapshot": { "type": "ChannelSnapshot" }, - "channel_entered_bridge": { "type": "ChannelEnteredBridge" }, - "channel_left_bridge": { "type": "ChannelLeftBridge" }, - "channel_state_change": { "type": "ChannelStateChange" }, - "channel_dtmf_received": { "type": "ChannelDtmfReceived" }, - "channel_dialplan": { "type": "ChannelDialplan" }, - "channel_caller_id": { "type": "ChannelCallerId" }, - "channel_userevent": { "type": "ChannelUserevent" }, - "channel_hangup_request": { "type": "ChannelHangupRequest" }, - "channel_varset": { "type": "ChannelVarset" }, - "stasis_end": { "type": "StasisEnd" }, - "stasis_start": { "type": "StasisStart" }, - "playback_started": { "type": "PlaybackStarted" }, - "playback_finished": { "type": "PlaybackFinished" } + "timestamp": { + "type": "Date", + "description": "Time at which this event was created.", + "required": false + } } }, "PlaybackStarted": { "id": "PlaybackStarted", + "extends": "Event", "description": "Event showing the start of a media playback operation.", "properties": { "playback": { @@ -77,6 +69,7 @@ }, "PlaybackFinished": { "id": "PlaybackFinished", + "extends": "Event", "description": "Event showing the completion of a media playback operation.", "properties": { "playback": { @@ -88,17 +81,13 @@ }, "ApplicationReplaced": { "id": "ApplicationReplaced", - "description": "Notification that another WebSocket has taken over for an application.", - "notes": "An application may only be subscribed to by a single WebSocket at a time. If multiple WebSockets attempt to subscribe to the same application, the newer WebSocket wins, and the older one receives this event.", - "properties": { - "application": { - "required": true, - "type": "string" - } - } + "extends": "Event", + "description": "Notification that another WebSocket has taken over for an application.\n\nAn application may only be subscribed to by a single WebSocket at a time. If multiple WebSockets attempt to subscribe to the same application, the newer WebSocket wins, and the older one receives this event.", + "properties": {} }, "BridgeCreated": { "id": "BridgeCreated", + "extends": "Event", "description": "Notification that a bridge has been created.", "properties": { "bridge": { @@ -109,6 +98,7 @@ }, "BridgeDestroyed": { "id": "BridgeDestroyed", + "extends": "Event", "description": "Notification that a bridge has been destroyed.", "properties": { "bridge": { @@ -119,6 +109,7 @@ }, "BridgeMerged": { "id": "BridgeMerged", + "extends": "Event", "description": "Notification that one bridge has merged into another.", "properties": { "bridge": { @@ -133,6 +124,7 @@ }, "ChannelCreated": { "id": "ChannelCreated", + "extends": "Event", "description": "Notification that a channel has been created.", "properties": { "channel": { @@ -141,24 +133,15 @@ } } }, - "ChannelSnapshot": { - "id": "ChannelSnapshot", - "description": "Some part of channel state changed.", - "properties": { - "channel": { - "required": true, - "type": "Channel" - } - } - }, "ChannelDestroyed": { "id": "ChannelDestroyed", + "extends": "Event", "description": "Notification that a channel has been destroyed.", "properties": { "cause": { "required": true, "description": "Integer representation of the cause of the hangup", - "type": "integer" + "type": "int" }, "cause_txt": { "required": true, @@ -173,6 +156,7 @@ }, "ChannelEnteredBridge": { "id": "ChannelEnteredBridge", + "extends": "Event", "description": "Notification that a channel has entered a bridge.", "properties": { "bridge": { @@ -186,6 +170,7 @@ }, "ChannelLeftBridge": { "id": "ChannelLeftBridge", + "extends": "Event", "description": "Notification that a channel has left a bridge.", "properties": { "bridge": { @@ -200,6 +185,7 @@ }, "ChannelStateChange": { "id": "ChannelStateChange", + "extends": "Event", "description": "Notification of a channel's state change.", "properties": { "channel": { @@ -210,14 +196,19 @@ }, "ChannelDtmfReceived": { "id": "ChannelDtmfReceived", - "description": "DTMF received on a channel.", - "notes": "This event is sent when the DTMF ends. There is no notification about the start of DTMF", + "extends": "Event", + "description": "DTMF received on a channel.\n\nThis event is sent when the DTMF ends. There is no notification about the start of DTMF", "properties": { "digit": { "required": true, "type": "string", "description": "DTMF digit received (0-9, A-E, # or *)" }, + "duration_ms": { + "required": true, + "type": "int", + "description": "Number of milliseconds DTMF was received" + }, "channel": { "required": true, "type": "Channel", @@ -227,32 +218,34 @@ }, "ChannelDialplan": { "id": "ChannelDialplan", + "extends": "Event", "description": "Channel changed location in the dialplan.", "properties": { - "application": { + "channel": { "required": true, - "type": "string", - "description": "The application that the channel is currently in." + "type": "Channel", + "description": "The channel that changed dialplan location." }, - "application_data": { + "dialplan_app": { "required": true, "type": "string", - "description": "The data that was passed to the application when it was invoked." + "description": "The application about to be executed." }, - "channel": { + "dialplan_app_data": { "required": true, - "type": "Channel", - "description": "The channel that changed dialplan location." + "type": "string", + "description": "The data to be passed to the application." } } }, "ChannelCallerId": { "id": "ChannelCallerId", + "extends": "Event", "description": "Channel changed Caller ID.", "properties": { "caller_presentation": { "required": true, - "type": "integer", + "type": "int", "description": "The integer representation of the Caller Presentation value." }, "caller_presentation_txt": { @@ -269,6 +262,7 @@ }, "ChannelUserevent": { "id": "ChannelUserevent", + "extends": "Event", "description": "User-generated event with additional user-defined fields in the object.", "properties": { "eventname": { @@ -285,10 +279,11 @@ }, "ChannelHangupRequest": { "id": "ChannelHangupRequest", + "extends": "Event", "description": "A hangup was requested on the channel.", "properties": { "cause": { - "type": "integer", + "type": "int", "description": "Integer representation of the cause of the hangup." }, "soft": { @@ -304,6 +299,7 @@ }, "ChannelVarset": { "id": "ChannelVarset", + "extends": "Event", "description": "Channel variable changed.", "properties": { "variable": { @@ -317,14 +313,15 @@ "description": "The new value of the variable." }, "channel": { - "required": true, + "required": false, "type": "Channel", - "description": "The channel on which the variable was set." + "description": "The channel on which the variable was set.\n\nIf missing, the variable is a global variable." } } }, "StasisEnd": { "id": "StasisEnd", + "extends": "Event", "description": "Notification that a channel has left a Stasis appliction.", "properties": { "channel": { @@ -335,6 +332,7 @@ }, "StasisStart": { "id": "StasisStart", + "extends": "Event", "description": "Notification that a channel has entered a Stasis appliction.", "properties": { "args": { diff --git a/rest-api/api-docs/playback.json b/rest-api/api-docs/playback.json index 38ca5e1a7aafbb817661cc997fb510472e2fb820..884c0db2605a1f24bc89406aafa2c6b0fa9fa76d 100644 --- a/rest-api/api-docs/playback.json +++ b/rest-api/api-docs/playback.json @@ -103,11 +103,39 @@ "models": { "Playback": { "id": "Playback", + "description": "Object representing the playback of media to a channel", "properties": { "id": { + "type": "string", + "description": "ID for this playback operation", + "required": true + }, + "media_uri": { + "type": "string", + "description": "URI for the media to play back.", + "required": true + }, + "target_uri": { + "type": "string", + "description": "URI for the channel or bridge to play the media on", + "required": true + }, + "language": { + "type": "string", + "description": "For media types that support multiple languages, the language requested for playback." + }, + "state": { + "type": "string", + "description": "Current state of the playback operation.", "required": true, - "description": "Playback's identifier.", - "type": "string" + "allowableValues": { + "valueType": "LIST", + "values": [ + "queued", + "playing", + "complete" + ] + } } } } diff --git a/rest-api/api-docs/recordings.json b/rest-api/api-docs/recordings.json index 2f5f92a08848a0fd4f8710ed6fd317c343251a14..ce11d17c25edb3d7c9ec9f65275673a01b21b89f 100644 --- a/rest-api/api-docs/recordings.json +++ b/rest-api/api-docs/recordings.json @@ -7,18 +7,6 @@ "basePath": "http://localhost:8088/stasis", "resourcePath": "/api-docs/recordings.{format}", "apis": [ - { - "path": "/recordings", - "description": "Recordings", - "operations": [ - { - "httpMethod": "GET", - "summary": "List all recordings.", - "nickname": "getRecordings", - "responseClass": "List[Recording]" - } - ] - }, { "path": "/recordings/stored", "description": "Recordings", @@ -226,17 +214,9 @@ } ], "models": { - "Recording": { - "id": "Recording", - "properties": { - "id": { - "required": true, - "type": "string" - } - } - }, "StoredRecording": { "id": "StoredRecording", + "description": "A past recording that may be played back.", "properties": { "id": { "required": true, @@ -246,7 +226,7 @@ "required": true, "type": "List[string]" }, - "durationSeconds": { + "duration_seconds": { "required": false, "type": "int" }, @@ -259,6 +239,7 @@ }, "LiveRecording": { "id": "LiveRecording", + "description": "A recording that is in progress", "properties": { "id": { "required": true, diff --git a/rest-api/api-docs/sounds.json b/rest-api/api-docs/sounds.json index 06d84ea7e05b368f42527dc21b0b8032b44b1062..103738c45ae6d614ec06c245d5c10d391603fcc4 100644 --- a/rest-api/api-docs/sounds.json +++ b/rest-api/api-docs/sounds.json @@ -60,6 +60,7 @@ "models": { "FormatLangPair": { "id": "FormatLangPair", + "description": "Identifies the format and language of a sound file", "properties": { "language": { "required": true, @@ -73,6 +74,7 @@ }, "Sound": { "id": "Sound", + "description": "A media file that may be played back.", "properties": { "id": { "required": true, diff --git a/tests/test_ari_model.c b/tests/test_ari_model.c new file mode 100644 index 0000000000000000000000000000000000000000..21ad80ab53c336ece390d246736de54359eb0dd1 --- /dev/null +++ b/tests/test_ari_model.c @@ -0,0 +1,431 @@ +/* + * 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 Test the native ARI JSON validators. + * + * \author David M. Lee, II <dlee@digium.com> + */ + +/*** MODULEINFO + <depend>TEST_FRAMEWORK</depend> + <depend>res_ari_model</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/utils.h" +#include "asterisk/module.h" +#include "asterisk/test.h" +#include "../res/stasis_http/ari_model_validators.h" + +AST_TEST_DEFINE(validate_byte) +{ + RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, str, NULL, ast_json_unref); + int res; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = "/ari/validators/"; + info->summary = "Test byte validation"; + info->description = + "Test byte validation"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + uut = ast_json_integer_create(-128); + ast_test_validate(test, NULL != uut); + ast_test_validate(test, ari_validate_byte(uut)); + + res = ast_json_integer_set(uut, 0); + ast_test_validate(test, 0 == res); + ast_test_validate(test, ari_validate_byte(uut)); + + res = ast_json_integer_set(uut, 255); + ast_test_validate(test, 0 == res); + ast_test_validate(test, ari_validate_byte(uut)); + + res = ast_json_integer_set(uut, -129); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_byte(uut)); + + res = ast_json_integer_set(uut, 256); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_byte(uut)); + + str = ast_json_string_create("not a byte"); + ast_test_validate(test, NULL != str); + ast_test_validate(test, !ari_validate_byte(str)); + + /* Even if the string has an integral value */ + res = ast_json_string_set(str, "0"); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_byte(str)); + + ast_test_validate(test, !ari_validate_byte(ast_json_null())); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(validate_boolean) +{ + RAII_VAR(struct ast_json *, str, NULL, ast_json_unref); + int res; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = "/ari/validators/"; + info->summary = "Test byte validation"; + info->description = + "Test byte validation"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + ast_test_validate(test, ari_validate_boolean(ast_json_true())); + ast_test_validate(test, ari_validate_boolean(ast_json_false())); + + str = ast_json_string_create("not a bool"); + ast_test_validate(test, NULL != str); + ast_test_validate(test, !ari_validate_boolean(str)); + + /* Even if the string has a boolean value */ + res = ast_json_string_set(str, "true"); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_boolean(str)); + + /* Even if the string has a boolean text in it */ + res = ast_json_string_set(str, "true"); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_boolean(str)); + + ast_test_validate(test, !ari_validate_boolean(ast_json_null())); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(validate_int) +{ + RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, str, NULL, ast_json_unref); + int res; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = "/ari/validators/"; + info->summary = "Test int validation"; + info->description = + "Test int validation"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + uut = ast_json_integer_create(-2147483648); + ast_test_validate(test, NULL != uut); + ast_test_validate(test, ari_validate_int(uut)); + + res = ast_json_integer_set(uut, 0); + ast_test_validate(test, 0 == res); + ast_test_validate(test, ari_validate_int(uut)); + + res = ast_json_integer_set(uut, 2147483647); + ast_test_validate(test, 0 == res); + ast_test_validate(test, ari_validate_int(uut)); + + res = ast_json_integer_set(uut, -2147483649LL); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_int(uut)); + + res = ast_json_integer_set(uut, 2147483648LL); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_int(uut)); + + str = ast_json_string_create("not a int"); + ast_test_validate(test, NULL != str); + ast_test_validate(test, !ari_validate_int(str)); + + /* Even if the string has an integral value */ + res = ast_json_string_set(str, "0"); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_int(str)); + + ast_test_validate(test, !ari_validate_int(ast_json_null())); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(validate_long) +{ + RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, str, NULL, ast_json_unref); + int res; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = "/ari/validators/"; + info->summary = "Test long validation"; + info->description = + "Test long validation"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + uut = ast_json_integer_create(0); + ast_test_validate(test, NULL != uut); + ast_test_validate(test, ari_validate_long(uut)); + + str = ast_json_string_create("not a long"); + ast_test_validate(test, NULL != str); + ast_test_validate(test, !ari_validate_long(str)); + + /* Even if the string has an integral value */ + res = ast_json_string_set(str, "0"); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_long(str)); + + ast_test_validate(test, !ari_validate_long(ast_json_null())); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(validate_string) +{ + RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, str, NULL, ast_json_unref); + int res; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = "/ari/validators/"; + info->summary = "Test string validation"; + info->description = + "Test string validation"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + uut = ast_json_string_create("text"); + ast_test_validate(test, NULL != uut); + ast_test_validate(test, ari_validate_string(uut)); + + res = ast_json_string_set(uut, ""); + ast_test_validate(test, 0 == res); + ast_test_validate(test, ari_validate_string(uut)); + + ast_test_validate(test, !ari_validate_string(ast_json_null())); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(validate_date) +{ + RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, str, NULL, ast_json_unref); + enum ast_test_result_state test_res; + int res; + int i; + const char *valid_dates[] = { + /* Time is optional */ + "2013-06-17", + /* Seconds are optional */ + "2013-06-17T23:59Z", + /* Subseconds are optional */ + "2013-06-17T23:59:59Z", + /* Leap seconds are valid */ + "2013-06-30T23:59:61Z", + /* Subseconds are allowed */ + "2013-06-17T23:59:59.999999Z", + /* Now with -06:00 for the timezone */ + "2013-06-17T23:59-06:00", + "2013-06-17T23:59:59-06:00", + "2013-06-30T23:59:61-06:00", + "2013-06-17T23:59:59.999999-06:00", + /* Again, with +06:30 for the timezone */ + "2013-06-17T23:59+06:30", + "2013-06-17T23:59:59+06:30", + "2013-06-30T23:59:61+06:30", + "2013-06-17T23:59:59.999999+06:30", + /* So the colon in the timezone is optional */ + "2013-06-17T23:59-0600", + "2013-06-17T23:59:59-0600", + "2013-06-30T23:59:61-0600", + "2013-06-17T23:59:59.999999-0600", + /* Sure, why not */ + "2013-06-17T23:59+0630", + "2013-06-17T23:59:59+0630", + "2013-06-30T23:59:61+0630", + "2013-06-17T23:59:59.999999+0630", + "9999-12-31T23:59:61.999999Z", + /* In fact, you don't even have to specify minutes */ + "2013-06-17T23:59-06", + "2013-06-17T23:59:59-06", + "2013-06-30T23:59:61-06", + "2013-06-17T23:59:59.999999-06", + }; + + /* There are lots of invalid dates that the validator lets through. + * Those would be strings properly formatted as a ridiculous date. Such + * as 0000-00-00, or 9999-19-39. Those are harder to catch with a regex, + * and actually aren't as important. So long as the valid dates pass the + * validator, and poorly formatted dates are rejected, it's fine. + * Catching the occasional ridiculous date is just bonus. + */ + const char *invalid_dates[] = { + "", + "Not a date", + "2013-06-17T", /* Missing time, but has T */ + "2013-06-17T23:59:59.Z", /* Missing subsecond, but has dot */ + "2013-06-17T23:59", /* Missing timezone, but has time */ + "2013-06-17T23:59:59.999999", /* Missing timezone */ + "9999-99-31T23:59:61.999999Z", /* Invalid month */ + "9999-12-99T23:59:61.999999Z", /* Invalid day */ + "9999-12-31T99:59:61.999999Z", /* Invalid hour */ + "9999-12-31T23:99:61.999999Z", /* Invalid minute */ + "9999-12-31T23:59:99.999999Z", /* Invalid second */ + "2013-06-17T23:59:59.999999-99:00", /* Invalid timezone */ + "2013-06-17T23:59:59.999999-06:99", /* Invalid timezone */ + "2013-06-17T23:59:59.999999-06:", /* Invalid timezone */ + "2013-06-17T23:59:59.999999-06:0", /* Invalid timezone */ + "2013-06-17T23:59:59.999999-060", /* Invalid timezone */ + }; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = "/ari/validators/"; + info->summary = "Test date validation"; + info->description = + "Test date validation"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + uut = ast_json_string_create(""); + ast_test_validate(test, NULL != uut); + + /* Instead of using ast_test_validate, we'll collect the results from + * several test cases, since we have so many */ + test_res = AST_TEST_PASS; + for (i = 0; i < ARRAY_LEN(valid_dates); ++i) { + res = ast_json_string_set(uut, valid_dates[i]); + ast_test_validate(test, 0 == res); + if (!ari_validate_date(uut)) { + ast_test_status_update(test, + "Expected '%s' to be a valid date\n", + valid_dates[i]); + test_res = AST_TEST_FAIL; + } + } + + for (i = 0; i < ARRAY_LEN(invalid_dates); ++i) { + res = ast_json_string_set(uut, invalid_dates[i]); + ast_test_validate(test, 0 == res); + if (ari_validate_date(uut)) { + ast_test_status_update(test, + "Expected '%s' to be an invalid date\n", + invalid_dates[i]); + test_res = AST_TEST_FAIL; + } + } + + ast_test_validate(test, !ari_validate_string(ast_json_null())); + + return test_res; +} + +AST_TEST_DEFINE(validate_list) +{ + RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); + RAII_VAR(struct ast_json *, str, NULL, ast_json_unref); + int res; + + switch (cmd) { + case TEST_INIT: + info->name = __func__; + info->category = "/ari/validators/"; + info->summary = "Test list validation"; + info->description = + "Test list validation"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + uut = ast_json_array_create(); + ast_test_validate(test, NULL != uut); + ast_test_validate(test, ari_validate_list(uut, ari_validate_string)); + ast_test_validate(test, ari_validate_list(uut, ari_validate_int)); + + res = ast_json_array_append(uut, ast_json_string_create("")); + ast_test_validate(test, 0 == res); + ast_test_validate(test, ari_validate_list(uut, ari_validate_string)); + ast_test_validate(test, !ari_validate_list(uut, ari_validate_int)); + + res = ast_json_array_append(uut, ast_json_integer_create(0)); + ast_test_validate(test, 0 == res); + ast_test_validate(test, !ari_validate_list(uut, ari_validate_string)); + ast_test_validate(test, !ari_validate_list(uut, ari_validate_int)); + + ast_test_validate(test, + !ari_validate_list(ast_json_null(), ari_validate_string)); + + return AST_TEST_PASS; +} + +static int unload_module(void) +{ + AST_TEST_UNREGISTER(validate_byte); + AST_TEST_UNREGISTER(validate_boolean); + AST_TEST_UNREGISTER(validate_int); + AST_TEST_UNREGISTER(validate_long); + AST_TEST_UNREGISTER(validate_string); + AST_TEST_UNREGISTER(validate_date); + AST_TEST_UNREGISTER(validate_list); + return 0; +} + +static int load_module(void) +{ + AST_TEST_REGISTER(validate_byte); + AST_TEST_REGISTER(validate_boolean); + AST_TEST_REGISTER(validate_int); + AST_TEST_REGISTER(validate_long); + AST_TEST_REGISTER(validate_string); + AST_TEST_REGISTER(validate_date); + AST_TEST_REGISTER(validate_list); + return AST_MODULE_LOAD_SUCCESS; +} + +AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Skeleton (sample) Test"); diff --git a/tests/test_res_stasis.c b/tests/test_res_stasis.c index 321f40f3d59b1015ae968aab2c6aac680243eee1..5865f0951dd88179d254d99577bcb8181a8fd111 100644 --- a/tests/test_res_stasis.c +++ b/tests/test_res_stasis.c @@ -157,7 +157,9 @@ AST_TEST_DEFINE(app_replaced) stasis_app_register(app_name, test_handler, app_data1); stasis_app_register(app_name, test_handler, app_data2); - expected_message1 = ast_json_pack("[{s: {s: s}}]", "application_replaced", "application", app_name); + expected_message1 = ast_json_pack("[{s: s, s: s}]", + "type", "ApplicationReplaced", + "application", app_name); message = ast_json_pack("{ s: o }", "test-message", ast_json_null()); expected_message2 = ast_json_pack("[o]", ast_json_ref(message)); diff --git a/tests/test_stasis_channels.c b/tests/test_stasis_channels.c index 214d773963e710a27a2b35b552656ff8f908b1b9..a3f8828197c45c6312df3620f7e9bf9283d080ff 100644 --- a/tests/test_stasis_channels.c +++ b/tests/test_stasis_channels.c @@ -256,8 +256,7 @@ AST_TEST_DEFINE(channel_snapshot_json) ast_test_validate(test, NULL != snapshot); actual = ast_channel_snapshot_to_json(snapshot); - expected = 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," + expected = ast_json_pack("{ s: s, s: s, s: s, s: s," " s: { s: s, s: s, s: i }," " s: { s: s, s: s }," " s: { s: s, s: s }," @@ -266,14 +265,7 @@ AST_TEST_DEFINE(channel_snapshot_json) "name", "TEST/name", "state", "Down", "accountcode", "acctcode", - "peeraccount", "", - "userfield", "", - "uniqueid", ast_channel_uniqueid(chan), - "linkedid", ast_channel_uniqueid(chan), - "parkinglot", "", - "hangupsource", "", - "appl", "", - "data", "", + "id", ast_channel_uniqueid(chan), "dialplan", "context", "context", "exten", "exten",