summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES7
-rw-r--r--res/ari/ari_model_validators.c54
-rw-r--r--res/ari/ari_model_validators.h21
-rw-r--r--res/ari/resource_asterisk.c254
-rw-r--r--res/ari/resource_asterisk.h64
-rw-r--r--res/res_ari_asterisk.c272
-rw-r--r--rest-api/api-docs/asterisk.json156
7 files changed, 826 insertions, 2 deletions
diff --git a/CHANGES b/CHANGES
index a72c970f6..3d117b8be 100644
--- a/CHANGES
+++ b/CHANGES
@@ -196,6 +196,13 @@ ARI
can be also be retrieved. Individual modules can be loaded to Asterisk, as
well as unloaded and reloaded.
+* A new resource has been added to the 'asterisk' resource, 'config/dynamic'.
+ This resource allows for push configuration of sorcery derived objects
+ within Asterisk. The resource supports creation, retrieval, updating, and
+ deletion. Sorcery derived objects that are manipulated by this resource
+ must have a sorcery wizard that supports the desired operations.
+
+
res_pjsip
------------------
* A new 'g726_non_standard' endpoint option has been added that, when set to
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index ca72f93bd..fa16aea20 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -308,6 +308,60 @@ ari_validator ast_ari_validate_config_info_fn(void)
return ast_ari_validate_config_info;
}
+int ast_ari_validate_config_tuple(struct ast_json *json)
+{
+ int res = 1;
+ struct ast_json_iter *iter;
+ int has_attribute = 0;
+ int has_value = 0;
+
+ for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+ if (strcmp("attribute", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ has_attribute = 1;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI ConfigTuple field attribute 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 = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI ConfigTuple field value failed validation\n");
+ res = 0;
+ }
+ } else
+ {
+ ast_log(LOG_ERROR,
+ "ARI ConfigTuple has undocumented field %s\n",
+ ast_json_object_iter_key(iter));
+ res = 0;
+ }
+ }
+
+ if (!has_attribute) {
+ ast_log(LOG_ERROR, "ARI ConfigTuple missing required field attribute\n");
+ res = 0;
+ }
+
+ if (!has_value) {
+ ast_log(LOG_ERROR, "ARI ConfigTuple missing required field value\n");
+ res = 0;
+ }
+
+ return res;
+}
+
+ari_validator ast_ari_validate_config_tuple_fn(void)
+{
+ return ast_ari_validate_config_tuple;
+}
+
int ast_ari_validate_module(struct ast_json *json)
{
int res = 1;
diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h
index 41b91791d..e122ded34 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -207,6 +207,24 @@ int ast_ari_validate_config_info(struct ast_json *json);
ari_validator ast_ari_validate_config_info_fn(void);
/*!
+ * \brief Validator for ConfigTuple.
+ *
+ * A key/value pair that makes up part of a configuration object.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_config_tuple(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_config_tuple().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_config_tuple_fn(void);
+
+/*!
* \brief Validator for Module.
*
* Details of an Asterisk module
@@ -1262,6 +1280,9 @@ ari_validator ast_ari_validate_application_fn(void);
* - max_open_files: int
* - name: string (required)
* - setid: SetId (required)
+ * ConfigTuple
+ * - attribute: string (required)
+ * - value: string (required)
* Module
* - description: string (required)
* - name: string (required)
diff --git a/res/ari/resource_asterisk.c b/res/ari/resource_asterisk.c
index 569013f85..800686204 100644
--- a/res/ari/resource_asterisk.c
+++ b/res/ari/resource_asterisk.c
@@ -36,8 +36,262 @@ ASTERISK_REGISTER_FILE()
#include "asterisk/module.h"
#include "asterisk/paths.h"
#include "asterisk/pbx.h"
+#include "asterisk/sorcery.h"
#include "resource_asterisk.h"
+static void return_sorcery_object(struct ast_sorcery *sorcery, void *sorcery_obj,
+ struct ast_ari_response *response)
+{
+ RAII_VAR(struct ast_json *, return_set, NULL, ast_json_unref);
+ struct ast_variable *change_set;
+ struct ast_variable *it_change_set;
+
+ return_set = ast_json_array_create();
+ if (!return_set) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ /* Note that we can't use the sorcery JSON change set directly,
+ * as it will hand us back an Object (with fields), and we need
+ * a more generic representation of whatever the API call asked
+ * for, i.e., a list of tuples.
+ */
+ change_set = ast_sorcery_objectset_create(sorcery, sorcery_obj);
+ if (!change_set) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ for (it_change_set = change_set; it_change_set; it_change_set = it_change_set->next) {
+ struct ast_json *tuple;
+
+ tuple = ast_json_pack("{s: s, s: s}",
+ "attribute", it_change_set->name,
+ "value", it_change_set->value);
+ if (!tuple) {
+ ast_variables_destroy(change_set);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ if (ast_json_array_append(return_set, tuple)) {
+ ast_json_unref(tuple);
+ ast_variables_destroy(change_set);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+ }
+ ast_variables_destroy(change_set);
+
+ ast_ari_response_ok(response, ast_json_ref(return_set));
+}
+
+void ast_ari_asterisk_get_object(struct ast_variable *headers,
+ struct ast_ari_asterisk_get_object_args *args,
+ struct ast_ari_response *response)
+{
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
+ RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
+
+
+ sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
+ if (!sorcery) {
+ ast_ari_response_error(
+ response, 404, "Not Found",
+ "configClass '%s' not found",
+ args->config_class);
+ return;
+ }
+
+ object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
+ if (!object_type) {
+ ast_ari_response_error(
+ response, 404, "Not Found",
+ "objectType '%s' not found",
+ args->object_type);
+ return;
+ }
+
+ sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
+ if (!sorcery_obj) {
+ ast_ari_response_error(
+ response, 404, "Not Found",
+ "Object with id '%s' not found",
+ args->id);
+ return;
+ }
+
+ return_sorcery_object(sorcery, sorcery_obj, response);
+}
+
+void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response)
+{
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
+ RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
+ struct ast_json *fields;
+ struct ast_variable *update_set = NULL;
+ int created = 0;
+
+ sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
+ if (!sorcery) {
+ ast_ari_response_error(
+ response, 404, "Not Found",
+ "configClass '%s' not found",
+ args->config_class);
+ return;
+ }
+
+ object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
+ if (!object_type) {
+ ast_ari_response_error(
+ response, 404, "Not Found",
+ "objectType '%s' not found",
+ args->object_type);
+ return;
+ }
+
+ sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
+ if (!sorcery_obj) {
+ ast_debug(5, "Sorcery object '%s' does not exist; creating it\n", args->id);
+ sorcery_obj = ast_sorcery_alloc(sorcery, args->object_type, args->id);
+ if (!sorcery_obj) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ created = 1;
+ } else {
+ void *copy;
+
+ copy = ast_sorcery_copy(sorcery, sorcery_obj);
+ if (!copy) {
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+
+ ao2_ref(sorcery_obj, -1);
+ sorcery_obj = copy;
+ }
+
+ fields = ast_json_object_get(args->fields, "fields");
+ if (!fields && !created) {
+ /* Whoops. We need data. */
+ ast_ari_response_error(
+ response, 400, "Bad request",
+ "Fields must be provided to update object '%s'",
+ args->id);
+ return;
+ } else if (fields) {
+ size_t i;
+
+ for (i = 0; i < ast_json_array_size(fields); i++) {
+ struct ast_variable *new_var;
+ struct ast_json *json_value = ast_json_array_get(fields, i);
+
+ if (!json_value) {
+ continue;
+ }
+
+ new_var = ast_variable_new(
+ ast_json_string_get(ast_json_object_get(json_value, "attribute")),
+ ast_json_string_get(ast_json_object_get(json_value, "value")),
+ "");
+ if (!new_var) {
+ ast_variables_destroy(update_set);
+ ast_ari_response_alloc_failed(response);
+ return;
+ }
+ ast_variable_list_append(&update_set, new_var);
+ }
+ }
+
+ /* APPLY! Note that a NULL update_set is fine (and necessary), as it
+ * will force validation on a newly created object.
+ */
+ if (ast_sorcery_objectset_apply(sorcery, sorcery_obj, update_set)) {
+ ast_variables_destroy(update_set);
+ ast_ari_response_error(
+ response, 400, "Bad request",
+ "%s of object '%s' failed field value validation",
+ created ? "Creation" : "Update",
+ args->id);
+ return;
+ }
+
+ ast_variables_destroy(update_set);
+
+ if (created) {
+ if (ast_sorcery_create(sorcery, sorcery_obj)) {
+ ast_ari_response_error(
+ response, 403, "Forbidden",
+ "Cannot create sorcery objects of type '%s'",
+ args->object_type);
+ return;
+ }
+ } else {
+ if (ast_sorcery_update(sorcery, sorcery_obj)) {
+ ast_ari_response_error(
+ response, 403, "Forbidden",
+ "Cannot update sorcery objects of type '%s'",
+ args->object_type);
+ return;
+ }
+ }
+
+ return_sorcery_object(sorcery, sorcery_obj, response);
+}
+
+
+void ast_ari_asterisk_delete_object(struct ast_variable *headers,
+ struct ast_ari_asterisk_delete_object_args *args,
+ struct ast_ari_response *response)
+{
+ RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
+ RAII_VAR(struct ast_sorcery_object_type *, object_type, NULL, ao2_cleanup);
+ RAII_VAR(void *, sorcery_obj, NULL, ao2_cleanup);
+
+ sorcery = ast_sorcery_retrieve_by_module_name(args->config_class);
+ if (!sorcery) {
+ ast_ari_response_error(
+ response, 404, "Not Found",
+ "configClass '%s' not found",
+ args->config_class);
+ return;
+ }
+
+ object_type = ast_sorcery_get_object_type(sorcery, args->object_type);
+ if (!object_type) {
+ ast_ari_response_error(
+ response, 404, "Not Found",
+ "objectType '%s' not found",
+ args->object_type);
+ return;
+ }
+
+ sorcery_obj = ast_sorcery_retrieve_by_id(sorcery, args->object_type, args->id);
+ if (!sorcery_obj) {
+ ast_ari_response_error(
+ response, 404, "Not Found",
+ "Object with id '%s' not found",
+ args->id);
+ return;
+ }
+
+ if (ast_sorcery_delete(sorcery, sorcery_obj)) {
+ ast_ari_response_error(
+ response, 403, "Forbidden",
+ "Could not delete object with id '%s'",
+ args->id);
+ return;
+ }
+
+ ast_ari_response_no_content(response);
+}
+
+
void ast_ari_asterisk_get_info(struct ast_variable *headers,
struct ast_ari_asterisk_get_info_args *args,
struct ast_ari_response *response)
diff --git a/res/ari/resource_asterisk.h b/res/ari/resource_asterisk.h
index 574d947e4..1afc09317 100644
--- a/res/ari/resource_asterisk.h
+++ b/res/ari/resource_asterisk.h
@@ -39,6 +39,70 @@
#include "asterisk/ari.h"
+/*! Argument struct for ast_ari_asterisk_get_object() */
+struct ast_ari_asterisk_get_object_args {
+ /*! The configuration class containing dynamic configuration objects. */
+ const char *config_class;
+ /*! The type of configuration object to retrieve. */
+ const char *object_type;
+ /*! The unique identifier of the object to retrieve. */
+ const char *id;
+};
+/*!
+ * \brief Retrieve a dynamic configuration object.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_get_object(struct ast_variable *headers, struct ast_ari_asterisk_get_object_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_update_object() */
+struct ast_ari_asterisk_update_object_args {
+ /*! The configuration class containing dynamic configuration objects. */
+ const char *config_class;
+ /*! The type of configuration object to create or update. */
+ const char *object_type;
+ /*! The unique identifier of the object to create or update. */
+ const char *id;
+ /*! The body object should have a value that is a list of ConfigTuples, which provide the fields to update. Ex. [ { "attribute": "directmedia", "value": "false" } ] */
+ struct ast_json *fields;
+};
+/*!
+ * \brief Body parsing function for /asterisk/config/dynamic/{configClass}/{objectType}/{id}.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_asterisk_update_object_parse_body(
+ struct ast_json *body,
+ struct ast_ari_asterisk_update_object_args *args);
+
+/*!
+ * \brief Create or update a dynamic configuration object.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_update_object(struct ast_variable *headers, struct ast_ari_asterisk_update_object_args *args, struct ast_ari_response *response);
+/*! Argument struct for ast_ari_asterisk_delete_object() */
+struct ast_ari_asterisk_delete_object_args {
+ /*! The configuration class containing dynamic configuration objects. */
+ const char *config_class;
+ /*! The type of configuration object to delete. */
+ const char *object_type;
+ /*! The unique identifier of the object to delete. */
+ const char *id;
+};
+/*!
+ * \brief Delete a dynamic configuration object.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_asterisk_delete_object(struct ast_variable *headers, struct ast_ari_asterisk_delete_object_args *args, struct ast_ari_response *response);
/*! Argument struct for ast_ari_asterisk_get_info() */
struct ast_ari_asterisk_get_info_args {
/*! Array of Filter information returned */
diff --git a/res/res_ari_asterisk.c b/res/res_ari_asterisk.c
index 7d938a0a4..671af59da 100644
--- a/res/res_ari_asterisk.c
+++ b/res/res_ari_asterisk.c
@@ -52,6 +52,228 @@ ASTERISK_REGISTER_FILE()
#define MAX_VALS 128
+/*!
+ * \brief Parameter parsing callback for /asterisk/config/dynamic/{configClass}/{objectType}/{id}.
+ * \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 ast_ari_asterisk_get_object_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_ari_response *response)
+{
+ struct ast_ari_asterisk_get_object_args args = {};
+ struct ast_variable *i;
+ RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = path_vars; i; i = i->next) {
+ if (strcmp(i->name, "configClass") == 0) {
+ args.config_class = (i->value);
+ } else
+ if (strcmp(i->name, "objectType") == 0) {
+ args.object_type = (i->value);
+ } else
+ if (strcmp(i->name, "id") == 0) {
+ args.id = (i->value);
+ } else
+ {}
+ }
+ ast_ari_asterisk_get_object(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ case 404: /* {configClass|objectType|id} not found */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_list(response->message,
+ ast_ari_validate_config_tuple_fn());
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+ return;
+}
+int ast_ari_asterisk_update_object_parse_body(
+ struct ast_json *body,
+ struct ast_ari_asterisk_update_object_args *args)
+{
+ /* Parse query parameters out of it */
+ return 0;
+}
+
+/*!
+ * \brief Parameter parsing callback for /asterisk/config/dynamic/{configClass}/{objectType}/{id}.
+ * \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 ast_ari_asterisk_update_object_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_ari_response *response)
+{
+ struct ast_ari_asterisk_update_object_args args = {};
+ struct ast_variable *i;
+ RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = path_vars; i; i = i->next) {
+ if (strcmp(i->name, "configClass") == 0) {
+ args.config_class = (i->value);
+ } else
+ if (strcmp(i->name, "objectType") == 0) {
+ args.object_type = (i->value);
+ } else
+ if (strcmp(i->name, "id") == 0) {
+ args.id = (i->value);
+ } else
+ {}
+ }
+ /* Look for a JSON request entity */
+ body = ast_http_get_json(ser, headers);
+ if (!body) {
+ switch (errno) {
+ case EFBIG:
+ ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
+ goto fin;
+ case ENOMEM:
+ ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
+ goto fin;
+ case EIO:
+ ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
+ goto fin;
+ }
+ }
+ args.fields = body;
+ ast_ari_asterisk_update_object(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ case 400: /* Bad request body */
+ case 403: /* Could not create or update object */
+ case 404: /* {configClass|objectType} not found */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_list(response->message,
+ ast_ari_validate_config_tuple_fn());
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+ return;
+}
+/*!
+ * \brief Parameter parsing callback for /asterisk/config/dynamic/{configClass}/{objectType}/{id}.
+ * \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 ast_ari_asterisk_delete_object_cb(
+ struct ast_tcptls_session_instance *ser,
+ struct ast_variable *get_params, struct ast_variable *path_vars,
+ struct ast_variable *headers, struct ast_ari_response *response)
+{
+ struct ast_ari_asterisk_delete_object_args args = {};
+ struct ast_variable *i;
+ RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
+#if defined(AST_DEVMODE)
+ int is_valid;
+ int code;
+#endif /* AST_DEVMODE */
+
+ for (i = path_vars; i; i = i->next) {
+ if (strcmp(i->name, "configClass") == 0) {
+ args.config_class = (i->value);
+ } else
+ if (strcmp(i->name, "objectType") == 0) {
+ args.object_type = (i->value);
+ } else
+ if (strcmp(i->name, "id") == 0) {
+ args.id = (i->value);
+ } else
+ {}
+ }
+ ast_ari_asterisk_delete_object(headers, &args, response);
+#if defined(AST_DEVMODE)
+ code = response->response_code;
+
+ switch (code) {
+ case 0: /* Implementation is still a stub, or the code wasn't set */
+ is_valid = response->message == NULL;
+ break;
+ case 500: /* Internal Server Error */
+ case 501: /* Not Implemented */
+ case 403: /* Could not delete object */
+ case 404: /* {configClass|objectType|id} not found */
+ is_valid = 1;
+ break;
+ default:
+ if (200 <= code && code <= 299) {
+ is_valid = ast_ari_validate_void(
+ response->message);
+ } else {
+ ast_log(LOG_ERROR, "Invalid error response %d for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n", code);
+ is_valid = 0;
+ }
+ }
+
+ if (!is_valid) {
+ ast_log(LOG_ERROR, "Response validation failed for /asterisk/config/dynamic/{configClass}/{objectType}/{id}\n");
+ ast_ari_response_error(response, 500,
+ "Internal Server Error", "Response validation failed");
+ }
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+ return;
+}
int ast_ari_asterisk_get_info_parse_body(
struct ast_json *body,
struct ast_ari_asterisk_get_info_args *args)
@@ -690,6 +912,52 @@ fin: __attribute__((unused))
}
/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config_dynamic_configClass_objectType_id = {
+ .path_segment = "id",
+ .is_wildcard = 1,
+ .callbacks = {
+ [AST_HTTP_GET] = ast_ari_asterisk_get_object_cb,
+ [AST_HTTP_PUT] = ast_ari_asterisk_update_object_cb,
+ [AST_HTTP_DELETE] = ast_ari_asterisk_delete_object_cb,
+ },
+ .num_children = 0,
+ .children = { }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config_dynamic_configClass_objectType = {
+ .path_segment = "objectType",
+ .is_wildcard = 1,
+ .callbacks = {
+ },
+ .num_children = 1,
+ .children = { &asterisk_config_dynamic_configClass_objectType_id, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config_dynamic_configClass = {
+ .path_segment = "configClass",
+ .is_wildcard = 1,
+ .callbacks = {
+ },
+ .num_children = 1,
+ .children = { &asterisk_config_dynamic_configClass_objectType, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config_dynamic = {
+ .path_segment = "dynamic",
+ .callbacks = {
+ },
+ .num_children = 1,
+ .children = { &asterisk_config_dynamic_configClass, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
+static struct stasis_rest_handlers asterisk_config = {
+ .path_segment = "config",
+ .callbacks = {
+ },
+ .num_children = 1,
+ .children = { &asterisk_config_dynamic, }
+};
+/*! \brief REST handler for /api-docs/asterisk.{format} */
static struct stasis_rest_handlers asterisk_info = {
.path_segment = "info",
.callbacks = {
@@ -735,8 +1003,8 @@ static struct stasis_rest_handlers asterisk = {
.path_segment = "asterisk",
.callbacks = {
},
- .num_children = 3,
- .children = { &asterisk_info,&asterisk_modules,&asterisk_variable, }
+ .num_children = 4,
+ .children = { &asterisk_config,&asterisk_info,&asterisk_modules,&asterisk_variable, }
};
static int load_module(void)
diff --git a/rest-api/api-docs/asterisk.json b/rest-api/api-docs/asterisk.json
index c6968a5ba..2705f45f6 100644
--- a/rest-api/api-docs/asterisk.json
+++ b/rest-api/api-docs/asterisk.json
@@ -8,6 +8,146 @@
"resourcePath": "/api-docs/asterisk.{format}",
"apis": [
{
+ "path": "/asterisk/config/dynamic/{configClass}/{objectType}/{id}",
+ "description": "Asterisk dynamic configuration",
+ "operations": [
+ {
+ "httpMethod": "GET",
+ "summary": "Retrieve a dynamic configuration object.",
+ "nickname": "getObject",
+ "responseClass": "List[ConfigTuple]",
+ "parameters": [
+ {
+ "name": "configClass",
+ "description": "The configuration class containing dynamic configuration objects.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "objectType",
+ "description": "The type of configuration object to retrieve.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "id",
+ "description": "The unique identifier of the object to retrieve.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 404,
+ "reason": "{configClass|objectType|id} not found"
+ }
+ ]
+ },
+ {
+ "httpMethod": "PUT",
+ "summary": "Create or update a dynamic configuration object.",
+ "nickname": "updateObject",
+ "responseClass": "List[ConfigTuple]",
+ "parameters": [
+ {
+ "name": "configClass",
+ "description": "The configuration class containing dynamic configuration objects.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "objectType",
+ "description": "The type of configuration object to create or update.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "id",
+ "description": "The unique identifier of the object to create or update.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "fields",
+ "description": "The body object should have a value that is a list of ConfigTuples, which provide the fields to update. Ex. [ { \"attribute\": \"directmedia\", \"value\": \"false\" } ]",
+ "paramType": "body",
+ "required": false,
+ "dataType": "containers",
+ "allowMultiple": false
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 400,
+ "reason": "Bad request body"
+ },
+ {
+ "code": 403,
+ "reason": "Could not create or update object"
+ },
+ {
+ "code": 404,
+ "reason": "{configClass|objectType} not found"
+ }
+ ]
+ },
+ {
+ "httpMethod": "DELETE",
+ "summary": "Delete a dynamic configuration object.",
+ "nickname": "deleteObject",
+ "responseClass": "void",
+ "parameters": [
+ {
+ "name": "configClass",
+ "description": "The configuration class containing dynamic configuration objects.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "objectType",
+ "description": "The type of configuration object to delete.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ },
+ {
+ "name": "id",
+ "description": "The unique identifier of the object to delete.",
+ "paramType": "path",
+ "required": true,
+ "allowMultiple": false,
+ "dataType": "string"
+ }
+ ],
+ "errorResponses": [
+ {
+ "code": 403,
+ "reason": "Could not delete object"
+ },
+ {
+ "code": 404,
+ "reason": "{configClass|objectType|id} not found"
+ }
+ ]
+ }
+ ]
+ },
+ {
"path": "/asterisk/info",
"description": "Asterisk system information (similar to core show settings)",
"operations": [
@@ -403,6 +543,22 @@
"description": "The value of the variable requested"
}
}
+ },
+ "ConfigTuple": {
+ "id": "ConfigTuple",
+ "description": "A key/value pair that makes up part of a configuration object.",
+ "properties": {
+ "attribute": {
+ "required": true,
+ "type": "string",
+ "description": "A configuration object attribute."
+ },
+ "value": {
+ "required": true,
+ "type": "string",
+ "description": "The value for the attribute."
+ }
+ }
}
}
}