Skip to content

Commit

Permalink
Merge pull request #182 from AKSW/feature/qeiCommitIDInRequest
Browse files Browse the repository at this point in the history
Implement overlapping edit resolution methods (QEI)
  • Loading branch information
splattater authored Feb 25, 2019
2 parents 21926fc + 1d17973 commit 99a28b1
Show file tree
Hide file tree
Showing 4 changed files with 520 additions and 42 deletions.
15 changes: 15 additions & 0 deletions quit/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,21 @@ def getFileReferenceAndContext(self, blob, commit):
return quitWorkingData
return self._blobs.get(blob)

def applyQueryOnCommit(self, parsedQuery, parent_commit_ref, target_ref, query=None,
default_graph=[], named_graph=[]):
"""Apply an update query on the graph and the git repository."""
graph, commitid = self.instance(parent_commit_ref)
resultingChanges, exception = graph.update(parsedQuery)
if exception:
# TODO need to revert or invalidate the graph at this point.
pass
oid = self.commit(graph, resultingChanges, 'New Commit from QuitStore', parent_commit_ref,
target_ref, query=query, default_graph=default_graph,
named_graph=named_graph)
if exception:
raise exception
return oid

def commit(self, graph, delta, message, parent_commit_ref, target_ref, query=None,
default_graph=[], named_graph=[], **kwargs):
"""Commit changes after applying deltas to the blobs.
Expand Down
16 changes: 11 additions & 5 deletions quit/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import re
import logging

from _pygit2 import GitError
from _pygit2 import GitError, Oid
from os.path import expanduser, join
from quit.exceptions import RepositoryNotFound, RevisionNotFound, NodeNotFound, RemoteNotFound
from quit.exceptions import QuitGitRefNotFound, QuitGitRepoError, QuitGitPushError
Expand Down Expand Up @@ -117,7 +117,13 @@ def lookup(self, name):
except KeyError:
pass
except ValueError:
return self._repository.get(name).id
pass
try:
revison = self._repository.get(name)
if revison:
return revison.id
except Exception as e:
logger.exception(e)
raise RevisionNotFound(name)

def revision(self, id='HEAD'):
Expand Down Expand Up @@ -278,9 +284,9 @@ def pull(self, remote_name=None, refspec=None, **kwargs):
local_branch = groups.group("dst")
logger.debug("pull: parsed refspec is: {}, {}, {}".format(plus, remote_branch,
local_branch))
remote_master_id = self.fetch(remote_name=remote_name, remote_branch=remote_branch)
if remote_master_id is not None:
self.merge(target=local_branch, branch=remote_master_id, **kwargs)
remote_reference = self.fetch(remote_name=remote_name, remote_branch=remote_branch)
if remote_reference is not None:
self.merge(target=local_branch, branch=remote_reference, **kwargs)

def push(self, remote_name=None, refspec=None):
"""Push changes on a local repository to a remote repository.
Expand Down
125 changes: 89 additions & 36 deletions quit/web/modules/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
from quit.helpers import parse_sparql_request, parse_query_type
from quit.web.app import render_template, feature_required
from quit.exceptions import UnSupportedQuery, SparqlProtocolError, NonAbsoluteBaseError
from quit.exceptions import FromNamedError
from quit.exceptions import FromNamedError, QuitMergeConflict, RevisionNotFound
import datetime
import uuid
import base64

logger = logging.getLogger('quit.modules.endpoint')

Expand Down Expand Up @@ -89,56 +92,106 @@ def sparql(branch_or_ref):
except SparqlProtocolError:
return make_response('Sparql Protocol Error', 400)

try:
graph, commitid = quit.instance(branch_or_ref)
except Exception as e:
logger.exception(e)
return make_response('No branch or reference given.', 400)

if queryType in ['InsertData', 'DeleteData', 'Modify', 'DeleteWhere', 'Load']:
res, exception = graph.update(parsedQuery)
if branch_or_ref:
commit_id = quit.repository.revision(branch_or_ref).id
else:
commit_id = None

parent_commit_id = request.values.get('parent_commit_id', None) or None
if parent_commit_id and parent_commit_id != commit_id:
resolution_method = request.values.get('resolution_method', None) or None
if resolution_method == "reject":
logger.debug("rejecting update because {} is at {} but {} was expected".format(
branch_or_ref, commit_id, parent_commit_id))
return make_response('reject', 409) # alternative 412
elif resolution_method in ("merge", "branch"):
logger.debug(("writing update to a branch of {} because it is at {} but {} was "
"expected").format(branch_or_ref, commit_id, parent_commit_id))
try:
quit.repository.lookup(parent_commit_id)
except RevisionNotFound:
return make_response("The provided parent commit (parent_commit_id={}) "
"could not be found.".format(parent_commit_id), 400)

time = datetime.datetime.now().strftime('%Y-%m-%d-%H%M%S')
shortUUID = (base64.urlsafe_b64encode(uuid.uuid1().bytes).decode("utf-8")
).rstrip('=\n').replace('/', '_')
target_branch = "tmp/{}_{}".format(time, shortUUID)
target_ref = "refs/heads/" + target_branch
logger.debug("target ref is: {}".format(target_ref))
oid = quit.applyQueryOnCommit(parsedQuery, parent_commit_id, target_ref,
query=query, default_graph=default_graph,
named_graph=named_graph)

if resolution_method == "merge":
logger.debug(("going to merge update into {} because it is at {} but {} was "
"expected").format(branch_or_ref, commit_id, parent_commit_id))
try:
quit.repository.merge(target=branch_or_ref, branch=target_ref)
oid = quit.repository.revision(branch_or_ref).id
# delete temporary branch
tmp_branch = quit.repository._repository.branches.get(target_branch)
tmp_branch.delete()
response = make_response('success', 200)
target_branch = branch_or_ref
except QuitMergeConflict as e:
response = make_response('merge failed', 400)
else:
response = make_response('branched', 200)
response.headers["X-CurrentBranch"] = target_branch
response.headers["X-CurrentCommit"] = oid
return response

# Add info about temporary branch
else:
graph, commitid = quit.instance(parent_commit_id)

try:
target_head = request.values.get('target_head', branch_or_ref) or default_branch
target_ref = 'refs/heads/{}'.format(target_head)

oid = quit.commit(graph, res, 'New Commit from QuitStore', branch_or_ref,
target_ref, query=query, default_graph=default_graph,
named_graph=named_graph)
if exception is not None:
logger.exception(exception)
return 'Update query not executed (completely), (detected UnSupportedQuery)', 400
response = make_response('', 200)
response.headers["X-CurrentBranch"] = target_ref
if oid is not None:
response.headers["X-CurrentCommit"] = oid
else:
response.headers["X-CurrentCommit"] = commitid
return response
try:
oid = quit.applyQueryOnCommit(parsedQuery, branch_or_ref, target_ref,
query=query, default_graph=default_graph,
named_graph=named_graph)
response = make_response('', 200)
response.headers["X-CurrentBranch"] = target_head
if oid is not None:
response.headers["X-CurrentCommit"] = oid
else:
response.headers["X-CurrentCommit"] = commitid
return response
except Exception as e:
# query ok, but unsupported query type or other problem during commit
logger.exception(e)
return make_response('Error after executing the update query.', 400)
elif queryType in ['SelectQuery', 'DescribeQuery', 'AskQuery', 'ConstructQuery']:
try:
graph, commitid = quit.instance(branch_or_ref)
except Exception as e:
# query ok, but unsupported query type or other problem during commit
logger.exception(e)
return make_response('Error after executing the update query.', 400)
elif queryType in ['SelectQuery', 'DescribeQuery', 'AskQuery', 'ConstructQuery']:
return make_response('No branch or reference given.', 400)

try:
res = graph.query(parsedQuery)
except FromNamedError:
return make_response('FROM NAMED not supported, yet', 400)
except UnSupportedQuery:
return make_response('Unsupported Query', 400)
else:
logger.debug("Unsupported Type: {}".format(queryType))
return make_response("Unsupported Query Type: {}".format(queryType), 400)

mimetype = _getBestMatchingMimeType(request, queryType)
mimetype = _getBestMatchingMimeType(request, queryType)

if not mimetype:
return make_response("Mimetype: {} not acceptable".format(mimetype), 406)
if not mimetype:
return make_response("Mimetype: {} not acceptable".format(mimetype), 406)

response = create_result_response(res, mimetype, serializations[mimetype])
if commitid:
response.headers["X-CurrentCommit"] = commitid
return response
response = create_result_response(res, mimetype, serializations[mimetype])
if branch_or_ref:
response.headers["X-CurrentBranch"] = branch_or_ref
if commitid:
response.headers["X-CurrentCommit"] = commitid
return response
else:
logger.debug("Unsupported Type: {}".format(queryType))
return make_response("Unsupported Query Type: {}".format(queryType), 400)


@endpoint.route("/provenance", methods=['POST', 'GET'])
Expand Down
Loading

0 comments on commit 99a28b1

Please sign in to comment.