diff options
Diffstat (limited to 'res/ari')
-rw-r--r-- | res/ari/ari_model_validators.c | 54 | ||||
-rw-r--r-- | res/ari/ari_model_validators.h | 21 | ||||
-rw-r--r-- | res/ari/resource_asterisk.c | 254 | ||||
-rw-r--r-- | res/ari/resource_asterisk.h | 64 |
4 files changed, 393 insertions, 0 deletions
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 */ |