← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~blr/turnip/api-refs into lp:turnip

 

Bayard 'kit' Randel has proposed merging lp:~blr/turnip/api-refs into lp:turnip.

Requested reviews:
  Canonical Launchpad Branches (canonical-launchpad-branches)

For more details, see:
https://code.launchpad.net/~blr/turnip/api-refs/+merge/251394

Provides an API endpoint for git references, supporting GET for collections and distinct refs.
-- 
Your team Launchpad code reviewers is subscribed to branch lp:turnip.
=== modified file 'turnip/api/store.py'
--- turnip/api/store.py	2015-02-26 21:34:02 +0000
+++ turnip/api/store.py	2015-03-01 22:30:26 +0000
@@ -6,6 +6,15 @@
 import pygit2
 
 
+def get_ref_type_name(ref_type_code):
+    """Returns human readable ref type from ref type int."""
+    types = {1: 'commit',
+             2: 'tree',
+             3: 'blob',
+             4: 'tag'}
+    return types.get(ref_type_code)
+
+
 class Store(object):
     """Provides methods for manipulating repos on disk with pygit2."""
 
@@ -29,3 +38,38 @@
         except (IOError, OSError):
             print('Unable to delete repository from {}.'.format(repo))
             raise
+
+    @staticmethod
+    def get_refs(repo_path):
+        """Return all refs for a git repository."""
+        try:
+            repo = pygit2.Repository(repo_path)
+        except pygit2.GitError:
+            print('Unable to locate repository in {}.'.format(repo))
+            raise
+        ref_list = []
+        for ref in repo.listall_references():
+            git_object = repo.lookup_reference(ref).get_object()
+            ref_list.append(
+                {"ref": ref,
+                 "object": {'sha': str(git_object.oid),
+                            'type': get_ref_type_name(git_object.type)}})
+        return ref_list
+
+    @staticmethod
+    def get_ref(repo_path, ref):
+        """Return a specific ref for a git repository."""
+        try:
+            repo = pygit2.Repository(repo_path)
+        except pygit2.GitError:
+            print('Unable to locate repository in {}.'.format(repo))
+            raise
+        try:
+            git_object = repo.lookup_reference('refs/' + ref).get_object()
+        except pygit2.GitError:
+            print('Ref {} not found.'.format(ref))
+            raise
+        ref = {"ref": ref,
+               "object": {'sha': str(git_object.oid),
+                          'type': get_ref_type_name(git_object.type)}}
+        return ref

=== modified file 'turnip/api/tests/test_api.py'
--- turnip/api/tests/test_api.py	2015-02-25 07:28:44 +0000
+++ turnip/api/tests/test_api.py	2015-03-01 22:30:26 +0000
@@ -4,6 +4,7 @@
 
 import json
 import os
+import re
 import unittest
 import uuid
 
@@ -15,6 +16,7 @@
 from webtest import TestApp
 
 from turnip import api
+import test_helpers
 
 
 class ApiTestCase(TestCase):
@@ -27,8 +29,15 @@
         self.repo_path = str(uuid.uuid1())
         self.repo_store = os.path.join(repo_store, self.repo_path)
 
-    def test_repo_post(self):
-        resp = self.app.post('/repo', json.dumps({'repo_path': self.repo_path}))
+    def get_ref(self, ref):
+        test_helpers.init_repo(self.repo_store, commits=True, tags=True)
+        resp = self.app.get('/repo/{}/refs/{}'.format(
+            self.repo_path, ref))
+        return json.loads(resp.json_body)
+
+    def test_repo_create(self):
+        resp = self.app.post('/repo', json.dumps(
+            {'repo_path': self.repo_path}))
         self.assertEqual(resp.status_code, 200)
 
     def test_repo_delete(self):
@@ -37,6 +46,32 @@
         self.assertEqual(resp.status_code, 200)
         self.assertFalse(os.path.exists(self.repo_store))
 
+    def test_repo_get_refs(self):
+        """Ensure expected ref objects are returned and shas match."""
+        ref = 'refs/heads/master'
+        repo = test_helpers.init_repo(self.repo_store, commits=True, tags=True)
+        resp = self.app.get('/repo/{}/refs'.format(self.repo_path))
+        body = json.loads(resp.json_body)
+
+        self.assertTrue(any(obj['ref'] == ref for obj in body))
+        self.assertTrue(
+            any(re.match('refs/tags.*', obj['ref']) for obj in body))
+
+        oid = str(repo.head.get_object().oid)  # git object sha
+        resp_sha = ([obj for obj in body if obj['ref'] ==
+                     ref][0]['object'].get('sha'))
+        self.assertEqual(oid, resp_sha)
+
+    def test_repo_get_ref(self):
+        ref = 'heads/master'
+        resp = self.get_ref(ref)
+        self.assertEqual(ref, resp['ref'])
+
+    def test_repo_get_tag(self):
+        tag = 'tags/test-tag'
+        resp = self.get_ref(tag)
+        self.assertEqual(tag, resp['ref'])
+
 
 if __name__ == '__main__':
     unittest.main()

=== added file 'turnip/api/tests/test_helpers.py'
--- turnip/api/tests/test_helpers.py	1970-01-01 00:00:00 +0000
+++ turnip/api/tests/test_helpers.py	2015-03-01 22:30:26 +0000
@@ -0,0 +1,37 @@
+# Copyright 2015 Canonical Ltd.  All rights reserved.
+
+from pygit2 import (
+    init_repository,
+    GIT_OBJ_COMMIT,
+    Signature,
+    )
+
+AUTHOR = Signature('Test Author', 'author@xxxxxxx')
+COMMITTER = Signature('Test Commiter', 'committer@xxxxxxx')
+
+
+def create_commit(repo, parents=[]):
+    tree = repo.TreeBuilder().write()
+    commit = repo.create_commit(
+        'refs/heads/master',
+        AUTHOR, COMMITTER, 'test commit.',
+        tree,
+        parents  # parent
+    )
+    return commit
+
+
+def create_tag(repo):
+    oid = repo.head.get_object().oid
+    tag = repo.create_tag(
+        'test-tag', oid, GIT_OBJ_COMMIT, COMMITTER, 'test tag')
+    return tag
+
+
+def init_repo(repo_path, commits=None, tags=None):
+    repo = init_repository(repo_path, True)
+    if commits:
+        create_commit(repo)
+    if tags:
+        create_tag(repo)
+    return repo

=== modified file 'turnip/api/views.py'
--- turnip/api/views.py	2015-02-26 21:34:02 +0000
+++ turnip/api/views.py	2015-03-01 22:30:26 +0000
@@ -1,5 +1,6 @@
 # Copyright 2015 Canonical Ltd.  All rights reserved.
 
+import json
 import os
 
 from cornice.resource import resource
@@ -10,7 +11,7 @@
 from turnip.api.store import Store
 
 
-@resource(collection_path='repo', path='/repo/{name}')
+@resource(collection_path='/repo', path='/repo/{name}')
 class RepoAPI(object):
     """Provides HTTP API for repository actions."""
 
@@ -44,3 +45,35 @@
             Store.delete(repo)
         except Exception:
             return exc.HTTPNotFound()  # 404
+
+
+@resource(collection_path='/repo/{name}/refs',
+          path='/repo/{name}/refs/{ref:.*}')
+class RefAPI(object):
+    """Provides HTTP API for git references."""
+
+    def __init__(self, request):
+        config = TurnipConfig()
+        self.request = request
+        self.repo_store = config.get('repo_store')
+
+    def collection_get(self):
+        name = self.request.matchdict['name']
+        repo = os.path.join(self.repo_store, name)
+        try:
+            refs = Store.get_refs(repo)
+        except Exception as ex:
+            print(ex)
+            return exc.HTTPNotFound()  # 404
+        return json.dumps(refs)
+
+    def get(self):
+        name = self.request.matchdict['name']
+        ref = self.request.matchdict['ref']
+        repo = os.path.join(self.repo_store, name)
+        try:
+            ref = Store.get_ref(repo, ref)
+        except Exception as ex:
+            print(ex)
+            return exc.HTTPNotFound()
+        return json.dumps(ref)


Follow ups