mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +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); | ||||
|  | ||||
| /*! | ||||
|  * \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 | ||||
|  * | ||||
| @@ -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); | ||||
|  | ||||
| /*! | ||||
|  * \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 | ||||
|  * | ||||
|   | ||||
| @@ -90,9 +90,12 @@ struct ast_sorcery_object_field { | ||||
| 	/*! \brief Name of the field */ | ||||
| 	char name[MAX_OBJECT_FIELD]; | ||||
|  | ||||
| 	/*! \brief Callback function for translation */ | ||||
| 	/*! \brief Callback function for translation of a single value */ | ||||
| 	sorcery_field_handler handler; | ||||
|  | ||||
| 	/*! \brief Callback function for translation of multiple values */ | ||||
| 	sorcery_fields_handler multiple_handler; | ||||
|  | ||||
| 	/*! \brief Position of the field */ | ||||
| 	intptr_t args[]; | ||||
| }; | ||||
| @@ -516,6 +519,24 @@ void ast_sorcery_object_set_diff_handler(struct ast_sorcery *sorcery, const char | ||||
| 	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, | ||||
| 					aco_option_handler config_handler, sorcery_field_handler sorcery_handler, unsigned int flags, size_t argc, ...) | ||||
| { | ||||
| @@ -662,24 +683,30 @@ struct ast_variable *ast_sorcery_objectset_create(const struct ast_sorcery *sorc | ||||
|  | ||||
| 	i = ao2_iterator_init(object_type->fields, 0); | ||||
|  | ||||
| 	for (; (object_field = ao2_iterator_next(&i)); ao2_ref(object_field, -1)) { | ||||
| 		RAII_VAR(char *, buf, NULL, ast_free); | ||||
| 		struct ast_variable *tmp; | ||||
| 	for (; (object_field = ao2_iterator_next(&i)) && !res; ao2_ref(object_field, -1)) { | ||||
| 		struct ast_variable *tmp = NULL; | ||||
|  | ||||
| 		/* Any fields with no handler just get skipped */ | ||||
| 		if (!object_field->handler) { | ||||
| 		if (object_field->multiple_handler) { | ||||
| 			if ((res = object_field->multiple_handler(object, &tmp))) { | ||||
| 				ast_variables_destroy(tmp); | ||||
| 			} | ||||
| 		} else if (object_field->handler) { | ||||
| 			char *buf = NULL; | ||||
|  | ||||
| 			if ((res = object_field->handler(object, object_field->args, &buf)) || | ||||
| 				!(tmp = ast_variable_new(object_field->name, S_OR(buf, ""), ""))) { | ||||
| 				res = -1; | ||||
| 			} | ||||
|  | ||||
| 			ast_free(buf); | ||||
| 		} else { | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		if ((res = object_field->handler(object, object_field->args, &buf)) || | ||||
| 		    !(tmp = ast_variable_new(object_field->name, S_OR(buf, ""), ""))) { | ||||
| 			res = -1; | ||||
| 			ao2_ref(object_field, -1); | ||||
| 			break; | ||||
| 		if (!res) { | ||||
| 			tmp->next = fields; | ||||
| 			fields = tmp; | ||||
| 		} | ||||
|  | ||||
| 		tmp->next = fields; | ||||
| 		fields = tmp; | ||||
| 	} | ||||
|  | ||||
| 	ao2_iterator_destroy(&i); | ||||
|   | ||||
| @@ -93,6 +93,24 @@ static int test_sorcery_diff(const void *original, const void *modified, struct | ||||
| 	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 */ | ||||
| struct sorcery_test_caching { | ||||
| 	/*! \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; | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	int res = AST_TEST_PASS; | ||||
| @@ -801,6 +868,64 @@ AST_TEST_DEFINE(objectset_create) | ||||
| 	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) | ||||
| { | ||||
| 	int res = AST_TEST_PASS; | ||||
| @@ -1005,6 +1130,57 @@ AST_TEST_DEFINE(objectset_transform) | ||||
| 	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) | ||||
| { | ||||
| 	int res = AST_TEST_PASS; | ||||
| @@ -2149,6 +2325,7 @@ static int unload_module(void) | ||||
| 	AST_TEST_UNREGISTER(object_register); | ||||
| 	AST_TEST_UNREGISTER(object_register_without_mapping); | ||||
| 	AST_TEST_UNREGISTER(object_field_register); | ||||
| 	AST_TEST_UNREGISTER(object_fields_register); | ||||
| 	AST_TEST_UNREGISTER(object_alloc_with_id); | ||||
| 	AST_TEST_UNREGISTER(object_alloc_without_id); | ||||
| 	AST_TEST_UNREGISTER(object_copy); | ||||
| @@ -2156,10 +2333,12 @@ static int unload_module(void) | ||||
| 	AST_TEST_UNREGISTER(object_diff); | ||||
| 	AST_TEST_UNREGISTER(object_diff_native); | ||||
| 	AST_TEST_UNREGISTER(objectset_create); | ||||
| 	AST_TEST_UNREGISTER(objectset_create_regex); | ||||
| 	AST_TEST_UNREGISTER(objectset_apply); | ||||
| 	AST_TEST_UNREGISTER(objectset_apply_handler); | ||||
| 	AST_TEST_UNREGISTER(objectset_apply_invalid); | ||||
| 	AST_TEST_UNREGISTER(objectset_transform); | ||||
| 	AST_TEST_UNREGISTER(objectset_apply_fields); | ||||
| 	AST_TEST_UNREGISTER(changeset_create); | ||||
| 	AST_TEST_UNREGISTER(changeset_create_unchanged); | ||||
| 	AST_TEST_UNREGISTER(object_create); | ||||
| @@ -2191,6 +2370,7 @@ static int load_module(void) | ||||
| 	AST_TEST_REGISTER(object_register); | ||||
| 	AST_TEST_REGISTER(object_register_without_mapping); | ||||
| 	AST_TEST_REGISTER(object_field_register); | ||||
| 	AST_TEST_REGISTER(object_fields_register); | ||||
| 	AST_TEST_REGISTER(object_alloc_with_id); | ||||
| 	AST_TEST_REGISTER(object_alloc_without_id); | ||||
| 	AST_TEST_REGISTER(object_copy); | ||||
| @@ -2198,10 +2378,12 @@ static int load_module(void) | ||||
| 	AST_TEST_REGISTER(object_diff); | ||||
| 	AST_TEST_REGISTER(object_diff_native); | ||||
| 	AST_TEST_REGISTER(objectset_create); | ||||
| 	AST_TEST_REGISTER(objectset_create_regex); | ||||
| 	AST_TEST_REGISTER(objectset_apply); | ||||
| 	AST_TEST_REGISTER(objectset_apply_handler); | ||||
| 	AST_TEST_REGISTER(objectset_apply_invalid); | ||||
| 	AST_TEST_REGISTER(objectset_transform); | ||||
| 	AST_TEST_REGISTER(objectset_apply_fields); | ||||
| 	AST_TEST_REGISTER(changeset_create); | ||||
| 	AST_TEST_REGISTER(changeset_create_unchanged); | ||||
| 	AST_TEST_REGISTER(object_create); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user