summaryrefslogtreecommitdiff
path: root/res/ari
diff options
context:
space:
mode:
Diffstat (limited to 'res/ari')
-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
4 files changed, 393 insertions, 0 deletions
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index 2f54b8d17..667589601 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 1b9bf2120..4e6868bd2 100644
--- a/res/ari/resource_asterisk.c
+++ b/res/ari/resource_asterisk.c
@@ -36,8 +36,262 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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 */