Merge "ARI: Add support for push configuration of dynamic object"

This commit is contained in:
Matt Jordan 2015-07-17 09:23:48 -05:00 committed by Gerrit Code Review
commit a2bfd663a1
7 changed files with 826 additions and 2 deletions

View file

@ -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

View file

@ -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;

View file

@ -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)

View file

@ -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)

View file

@ -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 */

View file

@ -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)

View file

@ -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."
}
}
}
}
}