mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	Add support for retrieving multiple objects from sorcery using a regex on their id.
Review: https://reviewboard.asterisk.org/r/2329/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@381614 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		| @@ -197,6 +197,9 @@ struct ast_sorcery_wizard { | ||||
| 	/*! \brief Callback for retrieving an object using an id */ | ||||
| 	void *(*retrieve_id)(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id); | ||||
|  | ||||
| 	/*! \brief Callback for retrieving multiple objects using a regex on their id */ | ||||
| 	void (*retrieve_regex)(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); | ||||
|  | ||||
| 	/*! \brief Optional callback for retrieving an object using fields */ | ||||
| 	void *(*retrieve_fields)(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields); | ||||
|  | ||||
| @@ -548,6 +551,20 @@ void *ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char * | ||||
|  */ | ||||
| void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields); | ||||
|  | ||||
| /*! | ||||
|  * \brief Retrieve multiple objects using a regular expression on their id | ||||
|  * | ||||
|  * \param sorcery Pointer to a sorcery structure | ||||
|  * \param type Type of object to retrieve | ||||
|  * \param regex Regular expression | ||||
|  * | ||||
|  * \retval non-NULL if error occurs | ||||
|  * \retval NULL success | ||||
|  * | ||||
|  * \note The provided regex is treated as extended case sensitive. | ||||
|  */ | ||||
| struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex); | ||||
|  | ||||
| /*! | ||||
|  * \brief Update an object | ||||
|  * | ||||
|   | ||||
| @@ -959,6 +959,30 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch | ||||
| 	return object; | ||||
| } | ||||
|  | ||||
| struct ao2_container *ast_sorcery_retrieve_by_regex(const struct ast_sorcery *sorcery, const char *type, const char *regex) | ||||
| { | ||||
| 	RAII_VAR(struct ast_sorcery_object_type *, object_type, ao2_find(sorcery->types, type, OBJ_KEY), ao2_cleanup); | ||||
| 	struct ao2_container *objects; | ||||
| 	struct ao2_iterator i; | ||||
| 	struct ast_sorcery_object_wizard *wizard; | ||||
|  | ||||
| 	if (!object_type || !(objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) { | ||||
| 		return NULL; | ||||
| 	} | ||||
|  | ||||
| 	i = ao2_iterator_init(object_type->wizards, 0); | ||||
| 	for (; (wizard = ao2_iterator_next(&i)); ao2_ref(wizard, -1)) { | ||||
| 		if (!wizard->wizard->retrieve_regex) { | ||||
| 			continue; | ||||
| 		} | ||||
|  | ||||
| 		wizard->wizard->retrieve_regex(sorcery, wizard->data, object_type->name, objects, regex); | ||||
| 	} | ||||
| 	ao2_iterator_destroy(&i); | ||||
|  | ||||
| 	return objects; | ||||
| } | ||||
|  | ||||
| /*! \brief Internal function which returns if the wizard has created the object */ | ||||
| static int sorcery_wizard_create(void *obj, void *arg, int flags) | ||||
| { | ||||
|   | ||||
| @@ -32,6 +32,8 @@ | ||||
|  | ||||
| ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
|  | ||||
| #include <regex.h> | ||||
|  | ||||
| #include "asterisk/module.h" | ||||
| #include "asterisk/sorcery.h" | ||||
| #include "asterisk/astobj2.h" | ||||
| @@ -70,6 +72,9 @@ struct sorcery_config_fields_cmp_params { | ||||
| 	/*! \brief Pointer to the fields to check */ | ||||
| 	const struct ast_variable *fields; | ||||
|  | ||||
| 	/*! \brief Regular expression for checking object id */ | ||||
| 	regex_t *regex; | ||||
|  | ||||
| 	/*! \brief Optional container to put object into */ | ||||
| 	struct ao2_container *container; | ||||
| }; | ||||
| @@ -81,6 +86,7 @@ static void *sorcery_config_retrieve_id(const struct ast_sorcery *sorcery, void | ||||
| static void *sorcery_config_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields); | ||||
| static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, | ||||
| 					     const struct ast_variable *fields); | ||||
| static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); | ||||
| static void sorcery_config_close(void *data); | ||||
|  | ||||
| static struct ast_sorcery_wizard config_object_wizard = { | ||||
| @@ -91,6 +97,7 @@ static struct ast_sorcery_wizard config_object_wizard = { | ||||
| 	.retrieve_id = sorcery_config_retrieve_id, | ||||
| 	.retrieve_fields = sorcery_config_retrieve_fields, | ||||
| 	.retrieve_multiple = sorcery_config_retrieve_multiple, | ||||
| 	.retrieve_regex = sorcery_config_retrieve_regex, | ||||
| 	.close = sorcery_config_close, | ||||
| }; | ||||
|  | ||||
| @@ -126,13 +133,19 @@ static int sorcery_config_fields_cmp(void *obj, void *arg, int flags) | ||||
| 	RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); | ||||
| 	RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy); | ||||
|  | ||||
| 	/* If we can't turn the object into an object set OR if differences exist between the fields | ||||
| 	 * passed in and what are present on the object they are not a match. | ||||
| 	 */ | ||||
| 	if (params->fields && | ||||
| 	if (params->regex) { | ||||
| 		/* If a regular expression has been provided see if it matches, otherwise move on */ | ||||
| 		if (!regexec(params->regex, ast_sorcery_object_get_id(obj), 0, NULL, 0)) { | ||||
| 			ao2_link(params->container, obj); | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} else if (params->fields && | ||||
| 	    (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) || | ||||
| 	     (ast_sorcery_changeset_create(objset, params->fields, &diff)) || | ||||
| 	     diff)) { | ||||
| 		/* If we can't turn the object into an object set OR if differences exist between the fields | ||||
| 	     * passed in and what are present on the object they are not a match. | ||||
| 	     */ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| @@ -190,6 +203,25 @@ static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, | ||||
| 	ao2_callback(config_objects, 0, sorcery_config_fields_cmp, ¶ms); | ||||
| } | ||||
|  | ||||
| static void sorcery_config_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex) | ||||
| { | ||||
| 	struct sorcery_config *config = data; | ||||
| 	RAII_VAR(struct ao2_container *, config_objects, ao2_global_obj_ref(config->objects), ao2_cleanup); | ||||
| 	regex_t expression; | ||||
| 	struct sorcery_config_fields_cmp_params params = { | ||||
| 		.sorcery = sorcery, | ||||
| 		.container = objects, | ||||
| 		.regex = &expression, | ||||
| 	}; | ||||
|  | ||||
| 	if (!config_objects || regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	ao2_callback(config_objects, 0, sorcery_config_fields_cmp, ¶ms); | ||||
| 	regfree(&expression); | ||||
| } | ||||
|  | ||||
| /*! \brief Internal function which determines if criteria has been met for considering an object set applicable */ | ||||
| static int sorcery_is_criteria_met(struct ast_variable *objset, struct ast_variable *criteria) | ||||
| { | ||||
|   | ||||
| @@ -32,6 +32,8 @@ | ||||
|  | ||||
| ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
|  | ||||
| #include <regex.h> | ||||
|  | ||||
| #include "asterisk/module.h" | ||||
| #include "asterisk/sorcery.h" | ||||
| #include "asterisk/astobj2.h" | ||||
| @@ -45,6 +47,7 @@ static void *sorcery_memory_retrieve_id(const struct ast_sorcery *sorcery, void | ||||
| static void *sorcery_memory_retrieve_fields(const struct ast_sorcery *sorcery, void *data, const char *type, const struct ast_variable *fields); | ||||
| static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, | ||||
| 					     const struct ast_variable *fields); | ||||
| static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex); | ||||
| static int sorcery_memory_update(void *data, void *object); | ||||
| static int sorcery_memory_delete(void *data, void *object); | ||||
| static void sorcery_memory_close(void *data); | ||||
| @@ -56,6 +59,7 @@ static struct ast_sorcery_wizard memory_object_wizard = { | ||||
| 	.retrieve_id = sorcery_memory_retrieve_id, | ||||
| 	.retrieve_fields = sorcery_memory_retrieve_fields, | ||||
| 	.retrieve_multiple = sorcery_memory_retrieve_multiple, | ||||
| 	.retrieve_regex = sorcery_memory_retrieve_regex, | ||||
| 	.update = sorcery_memory_update, | ||||
| 	.delete = sorcery_memory_delete, | ||||
| 	.close = sorcery_memory_close, | ||||
| @@ -69,6 +73,9 @@ struct sorcery_memory_fields_cmp_params { | ||||
| 	/*! \brief Pointer to the fields to check */ | ||||
| 	const struct ast_variable *fields; | ||||
|  | ||||
| 	/*! \brief Regular expression for checking object id */ | ||||
| 	regex_t *regex; | ||||
|  | ||||
| 	/*! \brief Optional container to put object into */ | ||||
| 	struct ao2_container *container; | ||||
| }; | ||||
| @@ -101,13 +108,19 @@ static int sorcery_memory_fields_cmp(void *obj, void *arg, int flags) | ||||
| 	RAII_VAR(struct ast_variable *, objset, NULL, ast_variables_destroy); | ||||
| 	RAII_VAR(struct ast_variable *, diff, NULL, ast_variables_destroy); | ||||
|  | ||||
| 	/* If we can't turn the object into an object set OR if differences exist between the fields | ||||
| 	 * passed in and what are present on the object they are not a match. | ||||
| 	 */ | ||||
| 	if (params->fields && | ||||
| 	if (params->regex) { | ||||
| 		/* If a regular expression has been provided see if it matches, otherwise move on */ | ||||
| 		if (!regexec(params->regex, ast_sorcery_object_get_id(obj), 0, NULL, 0)) { | ||||
| 			ao2_link(params->container, obj); | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} else if (params->fields && | ||||
| 	    (!(objset = ast_sorcery_objectset_create(params->sorcery, obj)) || | ||||
| 	     (ast_sorcery_changeset_create(objset, params->fields, &diff)) || | ||||
| 	     diff)) { | ||||
| 		/* If we can't turn the object into an object set OR if differences exist between the fields | ||||
| 		 * passed in and what are present on the object they are not a match. | ||||
| 		 */ | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| @@ -154,6 +167,23 @@ static void sorcery_memory_retrieve_multiple(const struct ast_sorcery *sorcery, | ||||
| 	ao2_callback(data, 0, sorcery_memory_fields_cmp, ¶ms); | ||||
| } | ||||
|  | ||||
| static void sorcery_memory_retrieve_regex(const struct ast_sorcery *sorcery, void *data, const char *type, struct ao2_container *objects, const char *regex) | ||||
| { | ||||
| 	regex_t expression; | ||||
| 	struct sorcery_memory_fields_cmp_params params = { | ||||
| 		.sorcery = sorcery, | ||||
| 		.container = objects, | ||||
| 		.regex = &expression, | ||||
| 	}; | ||||
|  | ||||
| 	if (regcomp(&expression, regex, REG_EXTENDED | REG_NOSUB)) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	ao2_callback(data, 0, sorcery_memory_fields_cmp, ¶ms); | ||||
| 	regfree(&expression); | ||||
| } | ||||
|  | ||||
| static int sorcery_memory_update(void *data, void *object) | ||||
| { | ||||
| 	RAII_VAR(void *, existing, NULL, ao2_cleanup); | ||||
|   | ||||
| @@ -1419,6 +1419,74 @@ AST_TEST_DEFINE(object_retrieve_multiple_field) | ||||
| 	return AST_TEST_PASS; | ||||
| } | ||||
|  | ||||
| AST_TEST_DEFINE(object_retrieve_regex) | ||||
| { | ||||
| 	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); | ||||
| 	RAII_VAR(struct test_sorcery_object *, obj, NULL, ao2_cleanup); | ||||
| 	RAII_VAR(struct ao2_container *, objects, NULL, ao2_cleanup); | ||||
|  | ||||
| 	switch (cmd) { | ||||
| 	case TEST_INIT: | ||||
| 		info->name = "object_retrieve_regex"; | ||||
| 		info->category = "/main/sorcery/"; | ||||
| 		info->summary = "sorcery multiple object retrieval using regex unit test"; | ||||
| 		info->description = | ||||
| 			"Test multiple object retrieval in sorcery using regular expression for matching"; | ||||
| 		return AST_TEST_NOT_RUN; | ||||
| 	case TEST_EXECUTE: | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	if (!(sorcery = alloc_and_initialize_sorcery())) { | ||||
| 		ast_test_status_update(test, "Failed to open sorcery structure\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-98joe"))) { | ||||
| 		ast_test_status_update(test, "Failed to allocate a known object type\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	if (ast_sorcery_create(sorcery, obj)) { | ||||
| 		ast_test_status_update(test, "Failed to create object using in-memory wizard\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	ao2_cleanup(obj); | ||||
|  | ||||
| 	if (!(obj = ast_sorcery_alloc(sorcery, "test", "blah-93joe"))) { | ||||
| 		ast_test_status_update(test, "Failed to allocate second instance of a known object type\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	if (ast_sorcery_create(sorcery, obj)) { | ||||
| 		ast_test_status_update(test, "Failed to create second object using in-memory wizard\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	ao2_cleanup(obj); | ||||
|  | ||||
| 	if (!(obj = ast_sorcery_alloc(sorcery, "test", "neener-93joe"))) { | ||||
| 		ast_test_status_update(test, "Failed to allocate third instance of a known object type\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	if (ast_sorcery_create(sorcery, obj)) { | ||||
| 		ast_test_status_update(test, "Failed to create third object using in-memory wizard\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	if (!(objects = ast_sorcery_retrieve_by_regex(sorcery, "test", "^blah-"))) { | ||||
| 		ast_test_status_update(test, "Failed to retrieve a container of objects\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} else if (ao2_container_count(objects) != 2) { | ||||
| 		ast_test_status_update(test, "Received a container with incorrect number of objects in it\n"); | ||||
| 		return AST_TEST_FAIL; | ||||
| 	} | ||||
|  | ||||
| 	return AST_TEST_PASS; | ||||
| } | ||||
|  | ||||
| AST_TEST_DEFINE(object_update) | ||||
| { | ||||
| 	RAII_VAR(struct ast_sorcery *, sorcery, NULL, ast_sorcery_unref); | ||||
| @@ -2099,6 +2167,7 @@ static int unload_module(void) | ||||
| 	AST_TEST_UNREGISTER(object_retrieve_field); | ||||
| 	AST_TEST_UNREGISTER(object_retrieve_multiple_all); | ||||
| 	AST_TEST_UNREGISTER(object_retrieve_multiple_field); | ||||
| 	AST_TEST_UNREGISTER(object_retrieve_regex); | ||||
| 	AST_TEST_UNREGISTER(object_update); | ||||
| 	AST_TEST_UNREGISTER(object_update_uncreated); | ||||
| 	AST_TEST_UNREGISTER(object_delete); | ||||
| @@ -2140,6 +2209,7 @@ static int load_module(void) | ||||
| 	AST_TEST_REGISTER(object_retrieve_field); | ||||
| 	AST_TEST_REGISTER(object_retrieve_multiple_all); | ||||
| 	AST_TEST_REGISTER(object_retrieve_multiple_field); | ||||
| 	AST_TEST_REGISTER(object_retrieve_regex); | ||||
| 	AST_TEST_REGISTER(object_update); | ||||
| 	AST_TEST_REGISTER(object_update_uncreated); | ||||
| 	AST_TEST_REGISTER(object_delete); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user