blob: 18ffb42c8b0b7137b4f4ce66355398e86554bb45 [file] [log] [blame]
# Copyright 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from google.appengine.api import users
from google.appengine.ext import db
from codereview import git_models
from codereview import library
from codereview import models
from change_pb2 import ChangeService
from add_patchset_pb2 import *
from complete_patchset_pb2 import *
from upload_patchset_file_pb2 import *
from submit_change_pb2 import *
from util import *
class ChangeServiceImp(ChangeService, InternalAPI):
@automatic_retry
def SubmitChange(self, rpc_controller, req, done):
rsp = SubmitChangeResponse()
user = users.User(req.owner)
branch = db.get(db.Key(req.dest_branch_key))
if not branch:
rsp.status_code = SubmitChangeResponse.UNKNOWN_BRANCH
done(rsp)
rev = commit_to_revision(branch.project, req.commit)
if rev.patchset:
rsp.status_code = SubmitChangeResponse.PATCHSET_EXISTS
done(rsp)
subject = u(req.commit.subject)[0:100]
reviewers = []
cc = []
if req.bundle_key:
rb = git_models.ReceivedBundle.get(req.bundle_key)
if rb:
reviewers = [x for x in rb.reviewers if x != user]
cc = [x for x in rb.cc if x != user]
def trans():
change = models.Change(
subject = subject,
description = u(req.commit.message),
owner = user,
dest_project = branch.project,
dest_branch = branch,
n_patchsets = 1)
if cc:
change.cc = cc;
change.put()
if reviewers:
(added_review_status, deleted_review_status, new_review_status) = \
library.update_reviewers(change, [], reviewers)
for rs in added_review_status:
rs.put()
patchset = models.PatchSet(
change = change,
owner = user,
parent = change,
revision = rev,
id = 1)
patchset.put()
return (change, patchset)
change, patchset = db.run_in_transaction(trans)
library.send_new_change_emails(change, change.owner, reviewers, cc)
if rev.link_patchset(patchset):
rsp.status_code = SubmitChangeResponse.CREATED
rsp.change_id = change.key().id()
rsp.patchset_id = patchset.id
rsp.patchset_key = str(patchset.key())
else:
db.delete([change, patchset])
rsp.status_code = SubmitChangeResponse.PATCHSET_EXISTS
done(rsp)
@automatic_retry
def AddPatchSet(self, rpc_controller, req, done):
rsp = AddPatchSetResponse()
user = users.User(req.owner)
branch = db.get(db.Key(req.dest_branch_key))
if not branch:
rsp.status_code = AddPatchSetResponse.UNKNOWN_BRANCH
done(rsp)
rev = commit_to_revision(branch.project, req.commit)
if rev.patchset:
rsp.status_code = AddPatchSetResponse.PATCHSET_EXISTS
done(rsp)
subject = u(req.commit.subject)[0:100]
def trans():
change = models.Change.get_by_id(req.change_id)
if not change:
return None
change.subject = subject
change.description = u(req.commit.message)
change.n_patchsets += 1
id = change.n_patchsets
change.put()
patchset = models.PatchSet(
change = change,
owner = user,
parent = change,
revision = rev,
id = id)
patchset.put()
return patchset
patchset = db.run_in_transaction(trans)
if not patchset:
rsp.status_code = AddPatchSetResponse.UNKNOWN_CHANGE
done(rsp)
if rev.link_patchset(patchset):
rsp.status_code = AddPatchSetResponse.CREATED
rsp.patchset_id = patchset.id
rsp.patchset_key = str(patchset.key())
else:
db.delete(patchset)
rsp.status_code = AddPatchSetResponse.PATCHSET_EXISTS
done(rsp)
@automatic_retry
def UploadPatchsetFile(self, rpc_controller, req, done):
rsp = UploadPatchsetFileResponse()
patchset = db.get(db.Key(req.patchset_key))
if not patchset:
rsp.status_code = UploadPatchsetFileResponse.UNKNOWN_PATCHSET
done(rsp)
return
if patchset.complete or patchset.change.closed:
rsp.status_code = UploadPatchsetFileResponse.CLOSED
done(rsp)
return
if UploadPatchsetFileRequest.ADD == req.status:
status = 'A'
elif UploadPatchsetFileRequest.MODIFY == req.status:
status = 'M'
elif UploadPatchsetFileRequest.DELETE == req.status:
status = 'D'
else:
status = '?'
try:
if req.base_id:
old_data = models.DeltaContent.create_content(
id = req.base_id,
text_z = req.base_z)
new_data = models.DeltaContent.create_content(
id = req.final_id,
text_z = req.patch_z,
base = old_data)
if new_data.text_z == req.patch_z:
diff_data = new_data
else:
diff_data = models.DeltaContent.create_patch(
id = req.patch_id,
text_z = req.patch_z)
else:
old_data = None
new_data = None
diff_data = models.DeltaContent.create_patch(
id = req.patch_id,
text_z = req.patch_z)
except models.DeltaPatchingException:
logging.error("Patch error on change %s, patch set %s, file %s"
% (patchset.change.key().id(),
str(patchset.id),
u(req.file_name))
)
rsp.status_code = UploadPatchsetFileResponse.PATCHING_ERROR
done(rsp)
return
file_name = u(req.file_name)
other_versions = []
other_patches = []
if patchset.id > 1:
for ps in models.gql(models.PatchSet,
'WHERE change = :1'
' ORDER BY id',
patchset.change).fetch(1000):
if ps.id == patchset.id:
continue
prior = models.Patch.get_patch_by_filename(ps, file_name)
if prior and not prior.patch_equals(diff_data):
other_versions.append('%d %d %s'
% (ps.key().id(), ps.id, prior.id))
other_patches.append(prior)
patch = models.Patch.get_or_insert_patch(
patchset = patchset,
filename = file_name,
status = status,
multi_way_diff = req.multi_way_diff,
n_comments = 0,
old_data = old_data,
new_data = new_data,
diff_data = diff_data,
other_versions = other_versions)
my_ovstr = '%d %d %s' % (patchset.key().id(),
patchset.id,
patch.id)
def trans():
for prior in other_patches:
p = db.get(prior.key())
p.other_versions.append(my_ovstr)
p.put()
db.run_in_transaction(trans)
if old_data:
models.CachedDeltaContent.get(old_data.key())
models.CachedDeltaContent.get(new_data.key())
if diff_data != new_data:
models.CachedDeltaContent.get(diff_data.key())
else:
models.CachedDeltaContent.get(diff_data.key())
rsp.status_code = UploadPatchsetFileResponse.CREATED
done(rsp)
@automatic_retry
def CompletePatchset(serlf, rpc_controller, req, done):
rsp = CompletePatchsetResponse()
patchset = db.get(db.Key(req.patchset_key))
if not patchset.complete:
patchset.complete = True
patchset.put()
if len(req.compressed_filenames) > 0:
models.PatchSetFilenames.store_compressed(
patchset,
req.compressed_filenames)
done(rsp)