launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #17996
[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