Repository: osmlab/maproulette-python-client

Issue:
[Feature Request] Add endpoint for searching for challenges based on keywords
**Is your feature request related to a problem? Please describe.**
Users should be able to search for challenges based on keywords or "tags" as they are referred to in the API docs. Namely, [this](https://maproulette2.geo.apple.com/docs/swagger-ui/index.html?url=/assets/swagger.json#/Challenge/getItemsBasedOnTags) endpoint should be supported. 

**Describe the solution you'd like**
Support the `/challenges/tags` endpoint.

**Describe alternatives you've considered**
N/A

**Additional context**
Although the UI refers to these challenge tags as "keywords", the API refers to them as tags. 

Discussion:

Patch:
diff --git a/examples/challenge_examples.py b/examples/challenge_examples.py
index 0a809fe..54f8d04 100644
--- a/examples/challenge_examples.py
+++ b/examples/challenge_examples.py
@@ -19,6 +19,10 @@ project_id = '2491'
 print(json.dumps(api.get_challenge_by_name(project_id=project_id,
                                            challenge_name=challenge_name), indent=4, sort_keys=True))
 
+# We can also use challenges' set tags to retrieve them:
+challenge_tags = 'River'
+print(json.dumps(api.get_challenges_by_tags(challenge_tags=challenge_tags), indent=4, sort_keys=True))
+
 # We can access challenge statistics as well:
 print(json.dumps(api.get_challenge_statistics_by_id(challenge_id)))
 
diff --git a/maproulette/api/challenge.py b/maproulette/api/challenge.py
index 6751847..1ef1f1f 100644
--- a/maproulette/api/challenge.py
+++ b/maproulette/api/challenge.py
@@ -54,6 +54,25 @@ class Challenge(MapRouletteServer):
         response = self.get(endpoint=f"/project/{project_id}/challenge/{challenge_name}")
         return response
 
+    def get_challenges_by_tags(self, challenge_tags, limit=10, page=0):
+        """Method to retrieve challenge information via tags applied to the challenge
+
+        :param challenge_tags: a comma-separated list of tags to search challenges for
+        :param limit: the limit to the number of results returned in the response. Default is 10
+        :param page: used in conjunction with the limit parameter to page through X number of responses. Default is 0.
+        :returns: the API response from the GET request
+        """
+        query_params = {
+            "tags": str(challenge_tags),
+            "limit ": str(limit),
+            "page": str(page)
+        }
+        response = self.get(
+            endpoint=f"/challenges/tags",
+            params=query_params
+        )
+        return response
+
     def get_virtual_challenge_by_id(self, challenge_id):
         """Method to retrieve an existing virtual challenge
 


Test patch:
diff --git a/tests/test_challenge_api.py b/tests/test_challenge_api.py
index dfc0e38..20d533e 100644
--- a/tests/test_challenge_api.py
+++ b/tests/test_challenge_api.py
@@ -66,6 +66,13 @@ class TestChallengeAPI(unittest.TestCase):
         response = api_instance.get_challenge_by_name(test_project_id, test_challenge_name)
         self.assertEqual(response['status'], '200')
 
+    @patch('maproulette.api.maproulette_server.requests.Session.get')
+    def test_get_challenges_by_tags(self, mock_request, api_instance=api):
+        test_challenge_tag = 'River'
+        mock_request.return_value.status_code = '200'
+        response = api_instance.get_challenges_by_tags(test_challenge_tag)
+        self.assertEqual(response['status'], '200')
+
     @patch('maproulette.api.maproulette_server.requests.Session.get')
     def test_get_virtual_challenge_by_id(self, mock_request, api_instance=api):
         test_virtual_challenge_id = '12345'