mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-06 04:30:28 +00:00
Add support for registering a sorcery handler which supports multiple fields using a regex.
Review: https://reviewboard.asterisk.org/r/2332/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@382340 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -131,6 +131,17 @@ struct ast_sorcery;
|
|||||||
*/
|
*/
|
||||||
typedef int (*sorcery_field_handler)(const void *obj, const intptr_t *args, char **buf);
|
typedef int (*sorcery_field_handler)(const void *obj, const intptr_t *args, char **buf);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief A callback function for translating multiple values into an ast_variable list
|
||||||
|
*
|
||||||
|
* \param obj Object to get values from
|
||||||
|
* \param fields Pointer to store the list of fields
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
typedef int (*sorcery_fields_handler)(const void *obj, struct ast_variable **fields);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief A callback function for performing a transformation on an object set
|
* \brief A callback function for performing a transformation on an object set
|
||||||
*
|
*
|
||||||
@@ -341,6 +352,21 @@ void ast_sorcery_object_set_copy_handler(struct ast_sorcery *sorcery, const char
|
|||||||
*/
|
*/
|
||||||
void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char *type, sorcery_diff_handler diff);
|
void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char *type, sorcery_diff_handler diff);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Register a regex for multiple fields within an object
|
||||||
|
*
|
||||||
|
* \param sorcery Pointer to a sorcery structure
|
||||||
|
* \param type Type of object
|
||||||
|
* \param regex A regular expression pattern for the fields
|
||||||
|
* \param config_handler A custom handler for translating the string representation of the fields
|
||||||
|
* \param sorcery_handler A custom handler for translating the native representation of the fields
|
||||||
|
*
|
||||||
|
* \retval 0 success
|
||||||
|
* \retval -1 failure
|
||||||
|
*/
|
||||||
|
int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char *type, const char *regex, aco_option_handler config_handler,
|
||||||
|
sorcery_fields_handler sorcery_handler);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Register a field within an object
|
* \brief Register a field within an object
|
||||||
*
|
*
|
||||||
|
@@ -90,9 +90,12 @@ struct ast_sorcery_object_field {
|
|||||||
/*! \brief Name of the field */
|
/*! \brief Name of the field */
|
||||||
char name[MAX_OBJECT_FIELD];
|
char name[MAX_OBJECT_FIELD];
|
||||||
|
|
||||||
/*! \brief Callback function for translation */
|
/*! \brief Callback function for translation of a single value */
|
||||||
sorcery_field_handler handler;
|
sorcery_field_handler handler;
|
||||||
|
|
||||||
|
/*! \brief Callback function for translation of multiple values */
|
||||||
|
sorcery_fields_handler multiple_handler;
|
||||||
|
|
||||||
/*! \brief Position of the field */
|
/*! \brief Position of the field */
|
||||||
intptr_t args[];
|
intptr_t args[];
|
||||||
};
|
};
|
||||||
@@ -516,6 +519,24 @@ void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char
|
|||||||
object_type->diff = diff;
|
object_type->diff = diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ast_sorcery_object_fields_register(struct ast_sorcery *sorcery, const char *type, const char *regex, aco_option_handler config_handler, sorcery_fields_handler sorcery_handler)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup);
|
||||||
|
RAII_VAR(struct ast_sorcery_object_field *, object_field, NULL, ao2_cleanup);
|
||||||
|
|
||||||
|
if (!object_type || !object_type->type.item_alloc || !config_handler || !(object_field = ao2_alloc(sizeof(*object_field), NULL))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_copy_string(object_field->name, regex, sizeof(object_field->name));
|
||||||
|
object_field->multiple_handler = sorcery_handler;
|
||||||
|
|
||||||
|
ao2_link(object_type->fields, object_field);
|
||||||
|
__aco_option_register(object_type->info, regex, ACO_REGEX, object_type->file->types, "", OPT_CUSTOM_T, config_handler, 0, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type,
|
int __ast_sorcery_object_field_register(struct ast_sorcery *sorcery, const char *type, const char *name, const char *default_val, enum aco_option_type opt_type,
|
||||||
aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...)
|
aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...)
|
||||||
{
|
{
|
||||||
@@ -662,25 +683,31 @@ struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorc
|
|||||||
|
|
||||||
i = ao2_iterator_init(object_type->fields, 0);
|
i = ao2_iterator_init(object_type->fields, 0);
|
||||||
|
|
||||||
for (; (object_field = ao2_iterator_next(&i)); ao2_ref(object_field, -1)) {
|
for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) {
|
||||||
RAII_VAR(char *, buf, NULL, ast_free);
|
struct ast_variable *tmp = NULL;
|
||||||
struct ast_variable *tmp;
|
|
||||||
|
|
||||||
/* Any fields with no handler just get skipped */
|
if (object_field->multiple_handler) {
|
||||||
if (!object_field->handler) {
|
if ((res = object_field->multiple_handler(object, &tmp))) {
|
||||||
continue;
|
ast_variables_destroy(tmp);
|
||||||
}
|
}
|
||||||
|
} else if (object_field->handler) {
|
||||||
|
char *buf = NULL;
|
||||||
|
|
||||||
if ((res = object_field->handler(object, object_field->args, &buf)) ||
|
if ((res = object_field->handler(object, object_field->args, &buf)) ||
|
||||||
!(tmp = ast_variable_new(object_field->name, S_OR(buf, ""), ""))) {
|
!(tmp = ast_variable_new(object_field->name, S_OR(buf, ""), ""))) {
|
||||||
res = -1;
|
res = -1;
|
||||||
ao2_ref(object_field, -1);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast_free(buf);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
tmp->next = fields;
|
tmp->next = fields;
|
||||||
fields = tmp;
|
fields = tmp;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ao2_iterator_destroy(&i);
|
ao2_iterator_destroy(&i);
|
||||||
|
|
||||||
|
@@ -93,6 +93,24 @@ static int test_sorcery_diff(const void *original, const void *modified, struct
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! \brief Internal function which sets some values */
|
||||||
|
static int test_sorcery_regex_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||||
|
{
|
||||||
|
struct test_sorcery_object *test = obj;
|
||||||
|
|
||||||
|
test->bob = 256;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Internal function which creates some ast_variable structures */
|
||||||
|
static int test_sorcery_regex_fields(const void *obj, struct ast_variable **fields)
|
||||||
|
{
|
||||||
|
*fields = ast_variable_new("toast-bob", "10", "");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! \brief Test structure for caching */
|
/*! \brief Test structure for caching */
|
||||||
struct sorcery_test_caching {
|
struct sorcery_test_caching {
|
||||||
/*! \brief Whether the object has been created in the cache or not */
|
/*! \brief Whether the object has been created in the cache or not */
|
||||||
@@ -435,6 +453,55 @@ AST_TEST_DEFINE(object_field_register)
|
|||||||
return AST_TEST_PASS;
|
return AST_TEST_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AST_TEST_DEFINE(object_fields_register)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case TEST_INIT:
|
||||||
|
info->name = "object_fields_register";
|
||||||
|
info->category = "/main/sorcery/";
|
||||||
|
info->summary = "sorcery object regex fields registration unit test";
|
||||||
|
info->description =
|
||||||
|
"Test object regex fields registration in sorcery with a provided id";
|
||||||
|
return AST_TEST_NOT_RUN;
|
||||||
|
case TEST_EXECUTE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sorcery = ast_sorcery_open())) {
|
||||||
|
ast_test_status_update(test, "Failed to open sorcery structure\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
|
||||||
|
ast_test_status_update(test, "Registered a regex object field successfully when no mappings or object types exist\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL)) {
|
||||||
|
ast_test_status_update(test, "Failed to set a known wizard as a default\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
|
||||||
|
ast_test_status_update(test, "Registered a regex object field successfully when object type does not exist\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
|
||||||
|
ast_test_status_update(test, "Failed to register object type\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields)) {
|
||||||
|
ast_test_status_update(test, "Registered a regex object field successfully when no mappings or object types exist\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AST_TEST_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
AST_TEST_DEFINE(object_alloc_with_id)
|
AST_TEST_DEFINE(object_alloc_with_id)
|
||||||
{
|
{
|
||||||
int res = AST_TEST_PASS;
|
int res = AST_TEST_PASS;
|
||||||
@@ -801,6 +868,64 @@ AST_TEST_DEFINE(objectset_create)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AST_TEST_DEFINE(objectset_create_regex)
|
||||||
|
{
|
||||||
|
int res = AST_TEST_PASS;
|
||||||
|
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
|
||||||
|
RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
|
||||||
|
RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
|
||||||
|
struct ast_variable *field;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case TEST_INIT:
|
||||||
|
info->name = "objectset_create_regex";
|
||||||
|
info->category = "/main/sorcery/";
|
||||||
|
info->summary = "sorcery object set creation with regex fields unit test";
|
||||||
|
info->description =
|
||||||
|
"Test object set creation with regex fields in sorcery";
|
||||||
|
return AST_TEST_NOT_RUN;
|
||||||
|
case TEST_EXECUTE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sorcery = ast_sorcery_open())) {
|
||||||
|
ast_test_status_update(test, "Failed to open sorcery structure\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
|
||||||
|
ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
|
||||||
|
ast_test_status_update(test, "Failed to register 'test' object type\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields);
|
||||||
|
|
||||||
|
if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
|
||||||
|
ast_test_status_update(test, "Failed to allocate a known object type\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(objset = ast_sorcery_objectset_create(sorcery, obj))) {
|
||||||
|
ast_test_status_update(test, "Failed to create an object set for a known sane object\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (field = objset; field; field = field->next) {
|
||||||
|
if (!strcmp(field->name, "toast-bob")) {
|
||||||
|
if (strcmp(field->value, "10")) {
|
||||||
|
ast_test_status_update(test, "Object set failed to create proper value for 'bob'\n");
|
||||||
|
res = AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ast_test_status_update(test, "Object set created field '%s' which is unknown\n", field->name);
|
||||||
|
res = AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
AST_TEST_DEFINE(objectset_apply)
|
AST_TEST_DEFINE(objectset_apply)
|
||||||
{
|
{
|
||||||
int res = AST_TEST_PASS;
|
int res = AST_TEST_PASS;
|
||||||
@@ -1005,6 +1130,57 @@ AST_TEST_DEFINE(objectset_transform)
|
|||||||
return AST_TEST_PASS;
|
return AST_TEST_PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AST_TEST_DEFINE(objectset_apply_fields)
|
||||||
|
{
|
||||||
|
int res = AST_TEST_PASS;
|
||||||
|
RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref);
|
||||||
|
RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup);
|
||||||
|
RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy);
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case TEST_INIT:
|
||||||
|
info->name = "objectset_apply_fields";
|
||||||
|
info->category = "/main/sorcery/";
|
||||||
|
info->summary = "sorcery object apply regex fields unit test";
|
||||||
|
info->description =
|
||||||
|
"Test object set apply with regex fields in sorcery";
|
||||||
|
return AST_TEST_NOT_RUN;
|
||||||
|
case TEST_EXECUTE:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sorcery = ast_sorcery_open())) {
|
||||||
|
ast_test_status_update(test, "Failed to open sorcery structure\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_sorcery_apply_default(sorcery, "test", "memory", NULL) ||
|
||||||
|
ast_sorcery_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, test_apply_handler)) {
|
||||||
|
ast_test_status_update(test, "Failed to register 'test' object type\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ast_sorcery_object_fields_register(sorcery, "test", "^toast-", test_sorcery_regex_handler, test_sorcery_regex_fields);
|
||||||
|
|
||||||
|
if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah"))) {
|
||||||
|
ast_test_status_update(test, "Failed to allocate a known object type\n");
|
||||||
|
return AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(objset = ast_variable_new("toast-bob", "20", ""))) {
|
||||||
|
ast_test_status_update(test, "Failed to create an object set, test could not occur\n");
|
||||||
|
res = AST_TEST_FAIL;
|
||||||
|
} else if (ast_sorcery_objectset_apply(sorcery, obj, objset)) {
|
||||||
|
ast_test_status_update(test, "Failed to apply valid object set to object\n");
|
||||||
|
res = AST_TEST_FAIL;
|
||||||
|
} else if (obj->bob != 256) {
|
||||||
|
ast_test_status_update(test, "Regex field handler was not called when it should have been\n");
|
||||||
|
res = AST_TEST_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
AST_TEST_DEFINE(changeset_create)
|
AST_TEST_DEFINE(changeset_create)
|
||||||
{
|
{
|
||||||
int res = AST_TEST_PASS;
|
int res = AST_TEST_PASS;
|
||||||
@@ -2149,6 +2325,7 @@ static int unload_module(void)
|
|||||||
AST_TEST_UNREGISTER(object_register);
|
AST_TEST_UNREGISTER(object_register);
|
||||||
AST_TEST_UNREGISTER(object_register_without_mapping);
|
AST_TEST_UNREGISTER(object_register_without_mapping);
|
||||||
AST_TEST_UNREGISTER(object_field_register);
|
AST_TEST_UNREGISTER(object_field_register);
|
||||||
|
AST_TEST_UNREGISTER(object_fields_register);
|
||||||
AST_TEST_UNREGISTER(object_alloc_with_id);
|
AST_TEST_UNREGISTER(object_alloc_with_id);
|
||||||
AST_TEST_UNREGISTER(object_alloc_without_id);
|
AST_TEST_UNREGISTER(object_alloc_without_id);
|
||||||
AST_TEST_UNREGISTER(object_copy);
|
AST_TEST_UNREGISTER(object_copy);
|
||||||
@@ -2156,10 +2333,12 @@ static int unload_module(void)
|
|||||||
AST_TEST_UNREGISTER(object_diff);
|
AST_TEST_UNREGISTER(object_diff);
|
||||||
AST_TEST_UNREGISTER(object_diff_native);
|
AST_TEST_UNREGISTER(object_diff_native);
|
||||||
AST_TEST_UNREGISTER(objectset_create);
|
AST_TEST_UNREGISTER(objectset_create);
|
||||||
|
AST_TEST_UNREGISTER(objectset_create_regex);
|
||||||
AST_TEST_UNREGISTER(objectset_apply);
|
AST_TEST_UNREGISTER(objectset_apply);
|
||||||
AST_TEST_UNREGISTER(objectset_apply_handler);
|
AST_TEST_UNREGISTER(objectset_apply_handler);
|
||||||
AST_TEST_UNREGISTER(objectset_apply_invalid);
|
AST_TEST_UNREGISTER(objectset_apply_invalid);
|
||||||
AST_TEST_UNREGISTER(objectset_transform);
|
AST_TEST_UNREGISTER(objectset_transform);
|
||||||
|
AST_TEST_UNREGISTER(objectset_apply_fields);
|
||||||
AST_TEST_UNREGISTER(changeset_create);
|
AST_TEST_UNREGISTER(changeset_create);
|
||||||
AST_TEST_UNREGISTER(changeset_create_unchanged);
|
AST_TEST_UNREGISTER(changeset_create_unchanged);
|
||||||
AST_TEST_UNREGISTER(object_create);
|
AST_TEST_UNREGISTER(object_create);
|
||||||
@@ -2191,6 +2370,7 @@ static int load_module(void)
|
|||||||
AST_TEST_REGISTER(object_register);
|
AST_TEST_REGISTER(object_register);
|
||||||
AST_TEST_REGISTER(object_register_without_mapping);
|
AST_TEST_REGISTER(object_register_without_mapping);
|
||||||
AST_TEST_REGISTER(object_field_register);
|
AST_TEST_REGISTER(object_field_register);
|
||||||
|
AST_TEST_REGISTER(object_fields_register);
|
||||||
AST_TEST_REGISTER(object_alloc_with_id);
|
AST_TEST_REGISTER(object_alloc_with_id);
|
||||||
AST_TEST_REGISTER(object_alloc_without_id);
|
AST_TEST_REGISTER(object_alloc_without_id);
|
||||||
AST_TEST_REGISTER(object_copy);
|
AST_TEST_REGISTER(object_copy);
|
||||||
@@ -2198,10 +2378,12 @@ static int load_module(void)
|
|||||||
AST_TEST_REGISTER(object_diff);
|
AST_TEST_REGISTER(object_diff);
|
||||||
AST_TEST_REGISTER(object_diff_native);
|
AST_TEST_REGISTER(object_diff_native);
|
||||||
AST_TEST_REGISTER(objectset_create);
|
AST_TEST_REGISTER(objectset_create);
|
||||||
|
AST_TEST_REGISTER(objectset_create_regex);
|
||||||
AST_TEST_REGISTER(objectset_apply);
|
AST_TEST_REGISTER(objectset_apply);
|
||||||
AST_TEST_REGISTER(objectset_apply_handler);
|
AST_TEST_REGISTER(objectset_apply_handler);
|
||||||
AST_TEST_REGISTER(objectset_apply_invalid);
|
AST_TEST_REGISTER(objectset_apply_invalid);
|
||||||
AST_TEST_REGISTER(objectset_transform);
|
AST_TEST_REGISTER(objectset_transform);
|
||||||
|
AST_TEST_REGISTER(objectset_apply_fields);
|
||||||
AST_TEST_REGISTER(changeset_create);
|
AST_TEST_REGISTER(changeset_create);
|
||||||
AST_TEST_REGISTER(changeset_create_unchanged);
|
AST_TEST_REGISTER(changeset_create_unchanged);
|
||||||
AST_TEST_REGISTER(object_create);
|
AST_TEST_REGISTER(object_create);
|
||||||
|
Reference in New Issue
Block a user