Repository: mapbox/mapbox-sdk-py
Pull request #198 (2017-07-08 20:20:49)

Tokens api support
Have added support for listing of tokens and tests for listing, creation of tokens and temporary tokens. Will add other endpoints in the same PR after review for these,


Patch:
diff --git a/mapbox/__init__.py b/mapbox/__init__.py
index e73f3ff..1d86d23 100644
--- a/mapbox/__init__.py
+++ b/mapbox/__init__.py
@@ -8,4 +8,5 @@ from .services.geocoding import Geocoder, InvalidCountryCodeError, InvalidPlaceT
 from .services.mapmatching import MapMatcher
 from .services.surface import Surface
 from .services.static import Static
+from .services.tokens import Tokens
 from .services.uploads import Uploader
diff --git a/mapbox/services/tokens.py b/mapbox/services/tokens.py
index 81427ed..12719d9 100644
--- a/mapbox/services/tokens.py
+++ b/mapbox/services/tokens.py
@@ -1,7 +1,8 @@
-import arrow
+from datetime import datetime, timedelta
 
 from uritemplate import URITemplate
 
+from mapbox.errors import ValidationError
 from mapbox.services.base import Service
 
 
@@ -12,7 +13,9 @@ class Tokens(Service):
     def baseuri(self):
         return 'https://{0}/tokens/v2'.format(self.host)
 
-    def create(self, username, scopes=None, note=None, expires=0):
+    def create(self, username=None, scopes=None, note=None):
+        if username is None:
+            username = self.username
         if not scopes:
             raise ValueError("One or more token scopes are required")
         if not note:
@@ -22,9 +25,80 @@ class Tokens(Service):
 
         payload = {'scopes': scopes, 'note': note}
 
-        if expires > 0:
-            payload['expires'] = arrow.now().replace(seconds=+expires).isoformat()
+        res = self.session.post(uri, json=payload)
+        self.handle_http_error(res)
+        return res
+
+    def list_tokens(self, username=None, limit=None):
+        if username is None:
+            username = self.username
+        uri = URITemplate(self.baseuri + '/{username}').expand(username=username)
+
+        params = {}
+        if limit:
+            params['limit'] = int(limit)
+
+        res = self.session.get(uri, params=params)
+        self.handle_http_error(res)
+        return res
+
+    def create_temp_token(self, username=None, scopes=None, expires=3600):
+        if username is None:
+            username = self.username
+        if not scopes:
+            raise ValueError("One or more token scopes are required")
+
+        uri = URITemplate(self.baseuri + '/{username}').expand(username=username)
+
+        payload = {'scopes': scopes}
+
+        if expires <= 0 or expires > 3600:
+            raise ValidationError("Expiry should be within 1 hour from now")
+        payload['expires'] = (datetime.utcnow() + timedelta(seconds=expires)).isoformat()
 
         res = self.session.post(uri, json=payload)
         self.handle_http_error(res)
         return res
+
+    def update_auth(self, authorization_id, username=None, scopes=None, note=None):
+        if username is None:
+            username = self.username
+        if not scopes and not note:
+            raise ValueError("Provide either scopes or a note to update token")
+
+        uri = URITemplate(self.baseuri + '/{username}/{authorization_id}').expand(username=username, authorization_id=authorization_id)
+
+        payload = {}
+        if scopes:
+            payload['scopes'] = scopes
+        if note:
+            payload['note'] = note
+
+        res = self.session.patch(uri, json=payload)
+        self.handle_http_error(res)
+        return res
+
+    def delete_auth(self, authorization_id, username=None):
+        if username is None:
+            username = self.username
+        uri = URITemplate(self.baseuri + '/{username}/{authorization_id}').expand(username=username, authorization_id=authorization_id)
+
+        res = self.session.delete(uri)
+        self.handle_http_error(res)
+        return res
+
+    def check_validity(self):
+        uri = URITemplate(self.baseuri)
+
+        res = self.session.get(uri)
+        self.handle_http_error(res)
+        return res
+
+    def list_scopes(self, username=None):
+        if username is None:
+            username = self.username
+        uri = URITemplate('https://{host}/scopes/v1' + '/{username}').expand(host=self.host, username=username)
+
+        res = self.session.get(uri)
+        self.handle_http_error(res)
+        return res


Test patch:
diff --git a/tests/test_tokens.py b/tests/test_tokens.py
new file mode 100644
index 0000000..6b57759
--- /dev/null
+++ b/tests/test_tokens.py
@@ -0,0 +1,103 @@
+import responses
+
+from mapbox.services.tokens import Tokens
+
+
+@responses.activate
+def test_token_create():
+    """Token creation works"""
+    responses.add(
+        responses.POST,
+        'https://api.mapbox.com/tokens/v2/testuser?access_token=pk.test',
+        match_querystring=True,
+        body='{"scopes": ["styles:read", "fonts:read"]}',
+        status=200,
+        content_type='application/json')
+
+    response = Tokens(access_token='pk.test').create('testuser', ["styles:read", "fonts:read"])
+    assert response.status_code == 200
+
+
+@responses.activate
+def test_token_list():
+    """Token listing works"""
+    responses.add(
+        responses.GET,
+        'https://api.mapbox.com/tokens/v2/testuser?access_token=pk.test',
+        match_querystring=True,
+        status=200,
+        content_type='application/json')
+
+    response = Tokens(access_token='pk.test').list_tokens('testuser')
+    assert response.status_code == 200
+
+
+@responses.activate
+def test_temp_token_create():
+    """Temporary token creation works"""
+    responses.add(
+        responses.POST,
+        'https://api.mapbox.com/tokens/v2/testuser?access_token=sk.test',
+        match_querystring=True,
+        body='{"scopes": ["styles:read", "fonts:read"]}',
+        status=200,
+        content_type='application/json')
+
+    response = Tokens(access_token='sk.test').create_temp_token('testuser', ["styles:read", "fonts:read"])
+    assert response.status_code == 200
+
+
+@responses.activate
+def test_update_token():
+    """Token updation works"""
+    responses.add(
+        responses.PATCH,
+        'https://api.mapbox.com/tokens/v2/testuser/auth_id?access_token=pk.test',
+        match_querystring=True,
+        body='{"scopes": ["styles:read", "fonts:read"]}',
+        status=200,
+        content_type='application/json')
+
+    response = Tokens(access_token='pk.test').update_auth('auth_id', 'testuser', ["styles:read", "fonts:read"])
+    assert response.status_code == 200
+
+
+@responses.activate
+def test_delete_auth():
+    """Token authorization deletion works"""
+    responses.add(
+        responses.DELETE,
+        'https://api.mapbox.com/tokens/v2/testuser/auth_id?access_token=pk.test',
+        match_querystring=True,
+        status=204)
+
+    response = Tokens(access_token='pk.test').delete_auth('auth_id', 'testuser')
+    assert response.status_code == 204
+
+
+@responses.activate
+def test_check_validity():
+    """Token checking validation works"""
+    responses.add(
+        responses.GET,
+        'https://api.mapbox.com/tokens/v2?access_token=pk.test',
+        match_querystring=True,
+        status=200,
+        content_type='application/json')
+
+    response = Tokens(access_token='pk.test').check_validity()
+    assert response.status_code == 200
+
+
+@responses.activate
+def test_list_scopes():
+    """Listing of scopes for a token works"""
+    responses.add(
+        responses.GET,
+        'https://api.mapbox.com/scopes/v1/testuser?access_token=pk.test',
+        match_querystring=True,
+        status=200,
+        content_type='application/json')
+
+    response = Tokens(access_token='pk.test').list_scopes('testuser')
+    assert response.status_code == 200