-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sort out Repo API for individual commits & their diffs
X-Ref: #56
- Loading branch information
Showing
3 changed files
with
208 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
186 changes: 186 additions & 0 deletions
186
tests/fixtures/07b9bf1540305153ceeb4519a50b588c35a35464.diff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
diff --git a/api.py b/api.py | ||
new file mode 100644 | ||
index 0000000..ae9d907 | ||
--- /dev/null | ||
+++ b/api.py | ||
@@ -0,0 +1,179 @@ | ||
+from flask import Flask, url_for | ||
+from werkzeug.exceptions import NotFound | ||
+app = Flask(__name__) | ||
+from pygit2 import Repository, GIT_OBJ_COMMIT, GIT_OBJ_TREE, GIT_OBJ_BLOB, GIT_REF_SYMBOLIC | ||
+from datetime import datetime, tzinfo, timedelta | ||
+import json | ||
+from base64 import b64encode | ||
+ | ||
+class FixedOffset(tzinfo): | ||
+ ZERO = timedelta(0) | ||
+ def __init__(self, offset): | ||
+ self._offset = timedelta(minutes = offset) | ||
+ | ||
+ def utcoffset(self, dt): | ||
+ return self._offset | ||
+ | ||
+ def dst(self, dt): | ||
+ return self.ZERO | ||
+ | ||
+REPO_BASE = '/Users/rajiv/Code/' | ||
+ | ||
+def _get_repo(repo_key): | ||
+ path = REPO_BASE + repo_key | ||
+ try: | ||
+ return Repository(path) | ||
+ except KeyError: | ||
+ raise NotFound("repository not found") | ||
+ | ||
+def _convert_signature(sig): | ||
+ return { | ||
+ "name" : sig.name, | ||
+ "email" : sig.email, | ||
+ "date" : datetime.fromtimestamp(sig.time, FixedOffset(sig.offset)) | ||
+ } | ||
+ | ||
+def _convert_commit(repo_key, commit): | ||
+ return { | ||
+ "url": url_for('get_commit', _external=True, repo_key=repo_key, sha=commit.hex), | ||
+ "sha": commit.hex, | ||
+ "author": _convert_signature(commit.author), | ||
+ "committer": _convert_signature(commit.committer), | ||
+ "message": commit.message, | ||
+ "tree" : { | ||
+ "sha": commit.tree.hex, | ||
+ "url": url_for('get_tree', _external=True, repo_key=repo_key, sha=commit.tree.hex), | ||
+ }, | ||
+ "parents" : [{ | ||
+ "sha": c.hex, | ||
+ "url": url_for('get_commit', _external=True, repo_key=repo_key, sha=c.hex) | ||
+ } for c in commit.parents] | ||
+ } | ||
+ | ||
+def _convert_tree(repo_key, tree): | ||
+ entry_list = [] | ||
+ for entry in tree: | ||
+ obj = entry.to_object() | ||
+ entry_data = { | ||
+ "path": entry.name, | ||
+ "sha": entry.hex, | ||
+ } | ||
+ if obj.type == GIT_OBJ_BLOB: | ||
+ entry_data['type'] = "blob" | ||
+ entry_data['size'] = obj.size | ||
+ entry_data['url'] = url_for('get_blob', _external=True, repo_key=repo_key, sha=entry.hex) | ||
+ elif obj.type == GIT_OBJ_TREE: | ||
+ entry_data['type'] = "tree" | ||
+ entry_data['url'] = url_for('get_tree', _external=True, repo_key=repo_key, sha=entry.hex) | ||
+ entry_list.append(entry_data) | ||
+ | ||
+ return { | ||
+ "url": url_for('get_tree', _external=True, repo_key=repo_key, sha=tree.hex), | ||
+ "sha": tree.hex, | ||
+ "tree": entry_list, | ||
+ #"mode": todo, | ||
+ } | ||
+ | ||
+def _linkobj_for_gitobj(repo_key, obj, include_type=False): | ||
+ data = {} | ||
+ data['sha'] = obj.hex | ||
+ obj_type = None | ||
+ if obj.type == GIT_OBJ_COMMIT: | ||
+ obj_type = 'commit' | ||
+ elif obj.type == GIT_OBJ_TREE: | ||
+ obj_type = 'tree' | ||
+ elif obj.type == GIT_OBJ_BLOB: | ||
+ obj_type = 'blob' | ||
+ if obj_type is not None: | ||
+ data['url'] = url_for('get_' + obj_type, _external=True, repo_key=repo_key, sha=obj.hex) | ||
+ if include_type: | ||
+ data['type'] = obj_type | ||
+ return data | ||
+ | ||
+def _encode_blob_data(data): | ||
+ try: | ||
+ return 'utf-8', data.decode('utf-8') | ||
+ except UnicodeDecodeError: | ||
+ return 'base64', b64encode(data) | ||
+ | ||
+def _convert_blob(repo_key, blob): | ||
+ encoding, data = _encode_blob_data(blob.data) | ||
+ return { | ||
+ "url": url_for('get_blob', _external=True, repo_key=repo_key, sha=blob.hex), | ||
+ "sha": blob.hex, | ||
+ "size": blob.size, | ||
+ "encoding": encoding, | ||
+ "data": data, | ||
+ } | ||
+ | ||
+def _convert_ref(repo_key, ref, obj): | ||
+ return { | ||
+ "url": url_for('get_ref_list', _external=True, repo_key=repo_key, ref_path=ref.name[5:]), #[5:] to cut off the redundant refs/ | ||
+ "ref": ref.name, | ||
+ "object": _linkobj_for_gitobj(repo_key, obj, include_type=True), | ||
+ } | ||
+ | ||
+def dthandler(obj): | ||
+ if hasattr(obj, 'isoformat'): | ||
+ return obj.isoformat() | ||
+ | ||
+def _json_dump(obj): | ||
+ return json.dumps(obj, default=dthandler) | ||
+ | ||
+@app.route('/repos/<repo_key>/git/commits/<sha>') | ||
+def get_commit(repo_key, sha): | ||
+ repo = _get_repo(repo_key) | ||
+ try: | ||
+ commit = repo[unicode(sha)] | ||
+ except KeyError: | ||
+ raise NotFound("commit not found") | ||
+ if commit.type != GIT_OBJ_COMMIT: | ||
+ raise NotFound("sha not a commit") | ||
+ return _json_dump(_convert_commit(repo_key, commit)) | ||
+ | ||
+ | ||
+@app.route('/repos/<repo_key>/git/trees/<sha>') | ||
+def get_tree(repo_key, sha): | ||
+ repo = _get_repo(repo_key) | ||
+ try: | ||
+ tree = repo[unicode(sha)] | ||
+ except KeyError: | ||
+ raise NotFound("tree not found") | ||
+ if tree.type != GIT_OBJ_TREE: | ||
+ raise NotFound("sha not a tree") | ||
+ return _json_dump(_convert_tree(repo_key, tree)) | ||
+ | ||
+@app.route('/repos/<repo_key>/git/blobs/<sha>') | ||
+def get_blob(repo_key, sha): | ||
+ repo = _get_repo(repo_key) | ||
+ try: | ||
+ blob = repo[unicode(sha)] | ||
+ except KeyError: | ||
+ raise NotFound("blob not found") | ||
+ if blob.type != GIT_OBJ_BLOB: | ||
+ raise NotFound("sha not a blob") | ||
+ return _json_dump(_convert_blob(repo_key, blob)) | ||
+ | ||
+ | ||
+ | ||
+@app.route('/repos/<repo_key>/git/refs') | ||
+@app.route('/repos/<repo_key>/git/refs/<path:ref_path>') | ||
+def get_ref_list(repo_key, ref_path=None): | ||
+ if ref_path is not None: | ||
+ ref_path = "refs/" + ref_path | ||
+ else: | ||
+ ref_path = "" | ||
+ repo = _get_repo(repo_key) | ||
+ ref_data = [ | ||
+ _convert_ref(repo_key, reference, repo[reference.oid]) for reference in filter(lambda x: x.type != GIT_REF_SYMBOLIC, [repo.lookup_reference(r) for r in filter(lambda x: x.startswith(ref_path), repo.listall_references())]) | ||
+ ] | ||
+ if len(ref_data) == 1: | ||
+ ref_data = ref_data[0] | ||
+ return _json_dump(ref_data) | ||
+ | ||
+ | ||
+if __name__ == '__main__': | ||
+ app.debug = True | ||
+ app.run(host="0.0.0.0") | ||
+ | ||
+application = app | ||
\ No newline at end of file |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters