diff --git a/CHANGES b/CHANGES index a72c970f6d..3d117b8bed 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 ca72f93bd9..fa16aea204 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 41b91791d3..e122ded345 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -206,6 +206,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. * @@ -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 569013f852..8006862048 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 574d947e4e..1afc093176 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 7d938a0a46..671af59da4 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) @@ -689,6 +911,52 @@ fin: __attribute__((unused)) return; } +/*! \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", @@ -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 c6968a5ba3..2705f45f65 100644 --- a/rest-api/api-docs/asterisk.json +++ b/rest-api/api-docs/asterisk.json @@ -7,6 +7,146 @@ "basePath": "http://localhost:8088/ari", "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)", @@ -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." + } + } } } }