blob: 6532a0fe1fe8ff3266b868d196961a42b7f466ba [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// 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.
package com.google.gerrit.plugins.codeowners.backend.findowners;
import com.google.common.base.Throwables;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigUpdate;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnersBackend;
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.update.RetryHelper;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Optional;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
@Singleton
public class FindOwnersBackend implements CodeOwnersBackend {
/** The ID of this code owner backend. */
public static final String ID = "find-owners";
private final CodeOwnerConfigFile.Factory codeOwnerConfigFileFactory;
private final GitRepositoryManager repoManager;
private final PersonIdent serverIdent;
private final MetaDataUpdate.InternalFactory metaDataUpdateInternalFactory;
private final RetryHelper retryHelper;
@Inject
FindOwnersBackend(
CodeOwnerConfigFile.Factory codeOwnerConfigFileFactory,
GitRepositoryManager repoManager,
@GerritPersonIdent PersonIdent serverIdent,
MetaDataUpdate.InternalFactory metaDataUpdateInternalFactory,
RetryHelper retryHelper) {
this.codeOwnerConfigFileFactory = codeOwnerConfigFileFactory;
this.repoManager = repoManager;
this.serverIdent = serverIdent;
this.metaDataUpdateInternalFactory = metaDataUpdateInternalFactory;
this.retryHelper = retryHelper;
}
@Override
public Optional<CodeOwnerConfig> getCodeOwnerConfig(CodeOwnerConfig.Key codeOwnerConfigKey) {
try {
return codeOwnerConfigFileFactory.load(codeOwnerConfigKey).getLoadedCodeOwnerConfig();
} catch (IOException | ConfigInvalidException e) {
throw new StorageException(
String.format("failed to load code owner config %s", codeOwnerConfigKey), e);
}
}
@Override
public Optional<CodeOwnerConfig> upsertCodeOwnerConfig(
CodeOwnerConfig.Key codeOwnerConfigKey,
CodeOwnerConfigUpdate codeOwnerConfigUpdate,
@Nullable IdentifiedUser currentUser)
throws StorageException {
try {
return retryHelper
.pluginUpdate(
"upsertCodeOwnerConfigInSourceBranch",
() ->
upsertCodeOwnerConfigInSourceBranch(
currentUser, codeOwnerConfigKey, codeOwnerConfigUpdate))
.call();
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new StorageException(e);
}
}
private Optional<CodeOwnerConfig> upsertCodeOwnerConfigInSourceBranch(
@Nullable IdentifiedUser currentUser,
CodeOwnerConfig.Key codeOwnerConfigKey,
CodeOwnerConfigUpdate codeOwnerConfigUpdate) {
try (Repository repository = repoManager.openRepository(codeOwnerConfigKey.project())) {
CodeOwnerConfigFile codeOwnerConfigFile =
codeOwnerConfigFileFactory
.load(repository, codeOwnerConfigKey)
.setCodeOwnerConfigUpdate(codeOwnerConfigUpdate);
try (MetaDataUpdate metaDataUpdate =
createMetaDataUpdate(codeOwnerConfigKey.project(), repository, currentUser)) {
codeOwnerConfigFile.commit(metaDataUpdate);
}
return codeOwnerConfigFile.getLoadedCodeOwnerConfig();
} catch (IOException | ConfigInvalidException e) {
throw new StorageException(
String.format("failed to upsert code owner config %s", codeOwnerConfigKey), e);
}
}
private MetaDataUpdate createMetaDataUpdate(
Project.NameKey project, Repository repository, @Nullable IdentifiedUser currentUser) {
MetaDataUpdate metaDataUpdate = metaDataUpdateInternalFactory.create(project, repository, null);
try {
metaDataUpdate.getCommitBuilder().setCommitter(serverIdent);
if (currentUser != null) {
// Using MetaDataUpdate#setAuthor copies the timezone and timestamp from the committer
// identity, so that it's ensured that the author and committer identities have the same
// timezone and timestamp.
metaDataUpdate.setAuthor(currentUser);
} else {
// In this case the author identity is the same as the committer identity, hence it already
// has the correct timezone and timestamp and we can set it on the commit builder directly.
metaDataUpdate.getCommitBuilder().setAuthor(serverIdent);
}
return metaDataUpdate;
} catch (Throwable t) {
metaDataUpdate.close();
Throwables.throwIfUnchecked(t);
throw new StorageException("Failed to create MetaDataUpdate", t);
}
}
}