diff options
author | Matt Jordan <mjordan@digium.com> | 2015-07-08 16:39:35 -0500 |
---|---|---|
committer | Matt Jordan <mjordan@digium.com> | 2015-07-16 20:38:57 -0500 |
commit | 254d07b15b6f6a741c1293cf9d26a2a235e795ed (patch) | |
tree | 0a1d7d9e98f5bd166fd74f575ea6d46c8ddf2b53 /res/ari/resource_asterisk.c | |
parent | af9ee2910d4f791243fa9c6ef98dd53264acc445 (diff) |
ARI: Add support for push configuration of dynamic object
This patch adds support for push configuration of dynamic, i.e.,
sorcery, objects in Asterisk. It adds three new REST API calls to the
'asterisk' resource:
* GET /asterisk/{configClass}/{objectType}/{id}: retrieve the current
object given its ID. This returns back a list of ConfigTuples, which
define the fields and their present values that make up the object.
* PUT /asterisk/{configClass}/{objectType}/{id}: create or update an
object. A body may be passed with the request that contains fields to
populate in the object. The same format as what is retrieved using
the GET operation is used for the body, save that we specify that the
list of fields to update are contained in the "fields" attribute.
* DELETE /asterisk/{configClass}/{objectType}/{id}: remove a dynamic
object from its backing storage.
Note that the success/failure of these operations is somewhat
configuration dependent, i.e., you must be using a sorcery wizard that
supports the operation in question. If a sorcery wizard does not support
the create or delete mechanisms, then the REST API call will fail with a
403 forbidden.
ASTERISK-25238 #close
Change-Id: I28cd5c7bf6f67f8e9e437ff097f8fd171d30ff5c
Diffstat (limited to 'res/ari/resource_asterisk.c')
-rw-r--r-- | res/ari/resource_asterisk.c | 254 |
1 files changed, 254 insertions, 0 deletions
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) |