Merge "Remove the bug-report link from code-owners."
diff --git a/java/com/google/gerrit/plugins/codeowners/acceptance/AbstractCodeOwnersTest.java b/java/com/google/gerrit/plugins/codeowners/acceptance/AbstractCodeOwnersTest.java
index 32c82a1..fda3e52 100644
--- a/java/com/google/gerrit/plugins/codeowners/acceptance/AbstractCodeOwnersTest.java
+++ b/java/com/google/gerrit/plugins/codeowners/acceptance/AbstractCodeOwnersTest.java
@@ -38,6 +38,9 @@
import com.google.gerrit.plugins.codeowners.acceptance.testsuite.CodeOwnerConfigOperations;
import com.google.gerrit.plugins.codeowners.acceptance.testsuite.TestCodeOwnerConfigCreation.Builder;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerBackend;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImportMode;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigReference;
import com.google.gerrit.plugins.codeowners.backend.config.BackendConfig;
import com.google.gerrit.plugins.codeowners.backend.config.CodeOwnersPluginConfiguration;
import com.google.gerrit.plugins.codeowners.backend.config.StatusConfig;
@@ -341,4 +344,13 @@
}
throw new IllegalStateException("unknown code owner backend: " + backend.getClass().getName());
}
+
+ protected CodeOwnerConfigReference createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode importMode, CodeOwnerConfig.Key codeOwnerConfigKey) {
+ return CodeOwnerConfigReference.builder(
+ importMode, codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getFilePath())
+ .setProject(codeOwnerConfigKey.project())
+ .setBranch(codeOwnerConfigKey.branchNameKey().branch())
+ .build();
+ }
}
diff --git a/java/com/google/gerrit/plugins/codeowners/api/BUILD b/java/com/google/gerrit/plugins/codeowners/api/BUILD
index 9ab1591..f8d6d60 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/BUILD
+++ b/java/com/google/gerrit/plugins/codeowners/api/BUILD
@@ -6,6 +6,7 @@
srcs = glob(["*.java"]),
visibility = ["//visibility:public"],
deps = PLUGIN_DEPS_NEVERLINK + [
+ "//lib/errorprone:annotations",
"//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/backend",
"//plugins/code-owners/java/com/google/gerrit/plugins/codeowners/common",
"//plugins/code-owners/proto:owners_metadata_java_proto",
diff --git a/java/com/google/gerrit/plugins/codeowners/api/CodeOwnerConfigFileInfo.java b/java/com/google/gerrit/plugins/codeowners/api/CodeOwnerConfigFileInfo.java
new file mode 100644
index 0000000..41fa62b
--- /dev/null
+++ b/java/com/google/gerrit/plugins/codeowners/api/CodeOwnerConfigFileInfo.java
@@ -0,0 +1,65 @@
+// Copyright (C) 2023 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.api;
+
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImportMode;
+import java.util.List;
+
+/**
+ * Representation of a code owner config file in the REST API.
+ *
+ * <p>This class determines the JSON format of code owner config files in the REST API.
+ *
+ * <p>The JSON only contains the location of the code owner config file (project, branch, path) and
+ * information about the imported code owner config files. It's not a representation of the code
+ * owner config file content (the content of a code owner config file is represented by {@link
+ * CodeOwnerConfigInfo}).
+ */
+public class CodeOwnerConfigFileInfo {
+ /**
+ * The name of the project from which the code owner config was loaded, or for unresolved imports,
+ * from which the code owner config was supposed to be loaded.
+ */
+ public String project;
+
+ /**
+ * The name of the branch from which the code owner config was loaded, or for unresolved imports,
+ * from which the code owner config was supposed to be loaded.
+ */
+ public String branch;
+
+ /** The path of the code owner config file. */
+ public String path;
+
+ /** Imported code owner config files. */
+ public List<CodeOwnerConfigFileInfo> imports;
+
+ /** Imported code owner config files that couldn't be resolved. */
+ public List<CodeOwnerConfigFileInfo> unresolvedImports;
+
+ /**
+ * Message explaining why this code owner config couldn't be resolved.
+ *
+ * <p>Only set for unresolved imports.
+ */
+ public String unresolvedErrorMessage;
+
+ /**
+ * The import mode.
+ *
+ * <p>Only set for imports.
+ */
+ public CodeOwnerConfigImportMode importMode;
+}
diff --git a/java/com/google/gerrit/plugins/codeowners/api/CodeOwners.java b/java/com/google/gerrit/plugins/codeowners/api/CodeOwners.java
index a2079b8..390e34c 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/CodeOwners.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/CodeOwners.java
@@ -17,6 +17,7 @@
import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableSet;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.gerrit.extensions.client.ListAccountsOption;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
@@ -78,6 +79,7 @@
*
* <p>Appends to the options which have been set so far.
*/
+ @CanIgnoreReturnValue
public QueryRequest withOptions(
ListAccountsOption option, ListAccountsOption... furtherOptions) {
this.options.add(requireNonNull(option, "option"));
@@ -90,6 +92,7 @@
*
* @param limit the limit
*/
+ @CanIgnoreReturnValue
public QueryRequest withLimit(int limit) {
this.limit = limit;
return this;
@@ -100,6 +103,7 @@
*
* @param seed seed that should be used to shuffle code owners that have the same score
*/
+ @CanIgnoreReturnValue
public QueryRequest withSeed(long seed) {
this.seed = seed;
return this;
@@ -112,6 +116,7 @@
* @param resolveAllUsers whether code ownerships that are assigned to all users should be
* resolved to random users
*/
+ @CanIgnoreReturnValue
public QueryRequest setResolveAllUsers(boolean resolveAllUsers) {
this.resolveAllUsers = resolveAllUsers;
return this;
@@ -123,6 +128,7 @@
* @param highestScoreOnly whether only the code owners with the highest score should be
* returned
*/
+ @CanIgnoreReturnValue
public QueryRequest withHighestScoreOnly(boolean highestScoreOnly) {
this.highestScoreOnly = highestScoreOnly;
return this;
@@ -135,6 +141,7 @@
*
* @param debug whether debug logs should be included into the response
*/
+ @CanIgnoreReturnValue
public QueryRequest withDebug(boolean debug) {
this.debug = debug;
return this;
@@ -147,6 +154,7 @@
*
* @param revision the revision from which the code owner configs should be read
*/
+ @CanIgnoreReturnValue
public QueryRequest forRevision(String revision) {
this.revision = revision;
return this;
diff --git a/java/com/google/gerrit/plugins/codeowners/api/CodeOwnersInfo.java b/java/com/google/gerrit/plugins/codeowners/api/CodeOwnersInfo.java
index d699029..763dff2 100644
--- a/java/com/google/gerrit/plugins/codeowners/api/CodeOwnersInfo.java
+++ b/java/com/google/gerrit/plugins/codeowners/api/CodeOwnersInfo.java
@@ -34,6 +34,9 @@
*/
public Boolean ownedByAllUsers;
+ /** The code owner config files that have been inspected to gather the code owners. */
+ public List<CodeOwnerConfigFileInfo> codeOwnerConfigs;
+
/**
* Debug logs that may help to understand why a user is or isn't suggested as a code owner.
*
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackend.java b/java/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackend.java
index a0df99d..0be6cce 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackend.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/AbstractFileBasedCodeOwnerBackend.java
@@ -15,6 +15,8 @@
package com.google.gerrit.plugins.codeowners.backend;
import static com.google.gerrit.plugins.codeowners.backend.CodeOwnersInternalServerErrorException.newInternalServerError;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.VERSIONED_META_DATA_CHANGE;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
@@ -30,6 +32,7 @@
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.gerrit.server.update.context.RefUpdateContext;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
@@ -251,7 +254,10 @@
codeOwnerConfigKey)
.setCodeOwnerConfigUpdate(codeOwnerConfigUpdate);
- try (MetaDataUpdate metaDataUpdate =
+ try (
+ RefUpdateContext pluginCtx = RefUpdateContext.open(PLUGIN);
+ RefUpdateContext ctx = RefUpdateContext.open(VERSIONED_META_DATA_CHANGE);
+ MetaDataUpdate metaDataUpdate =
createMetaDataUpdate(codeOwnerConfigKey.project(), repository, currentUser)) {
codeOwnerConfigFile.commit(metaDataUpdate);
}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFile.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFile.java
index 90fd878..0669b68 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFile.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFile.java
@@ -15,6 +15,7 @@
package com.google.gerrit.plugins.codeowners.backend;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
@@ -25,6 +26,7 @@
import com.google.gerrit.plugins.codeowners.util.JgitPath;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
import com.google.gerrit.server.git.meta.VersionedMetaData;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import java.io.IOException;
import java.util.Optional;
@@ -260,8 +262,11 @@
update.getRepository().exactRef(getRefName()) != null,
"branch %s does not exist",
getRefName());
-
- return super.commit(update);
+ // The commit goes to an ordinary branch (e.g. refs/heads/main). PLUGIN context is enough for
+ // such cases.
+ try(RefUpdateContext ctx = RefUpdateContext.open(PLUGIN)) {
+ return super.commit(update);
+ }
}
@Override
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFileUpdateScanner.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFileUpdateScanner.java
index d0bd003..04b0827 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFileUpdateScanner.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigFileUpdateScanner.java
@@ -15,6 +15,7 @@
package com.google.gerrit.plugins.codeowners.backend;
import static com.google.gerrit.plugins.codeowners.backend.CodeOwnersInternalServerErrorException.newInternalServerError;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;
@@ -25,6 +26,7 @@
import com.google.gerrit.server.GerritPersonIdent;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -101,7 +103,9 @@
"updating code owner files in branch %s of project %s",
branchNameKey.branch(), branchNameKey.project());
- try (Repository repository = repoManager.openRepository(branchNameKey.project());
+ try (
+ RefUpdateContext ctx = RefUpdateContext.open(PLUGIN);
+ Repository repository = repoManager.openRepository(branchNameKey.project());
RevWalk rw = new RevWalk(repository);
ObjectInserter oi = repository.newObjectInserter();
CodeOwnerConfigTreeWalk treeWalk =
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigImport.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigImport.java
new file mode 100644
index 0000000..a75c15b
--- /dev/null
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigImport.java
@@ -0,0 +1,86 @@
+// 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;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import java.util.Optional;
+
+/**
+ * Information about an import of a {@link CodeOwnerConfig}.
+ *
+ * <p>Contains the keys of the importing and the imported code owner config, as well as the
+ * reference that the importing code owner config uses to reference the imported code owner config
+ * (contains the import mode).
+ *
+ * <p>It's possible that this class represents non-resolveable imports (e.g. an import of a
+ * non-existing code owner config). In this case an error message is contained that explains why the
+ * import couldn't be resolved.
+ */
+@AutoValue
+public abstract class CodeOwnerConfigImport {
+ /** Key of the importing code owner config. */
+ public abstract CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig();
+
+ /** Key of the imported code owner config. */
+ public abstract CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig();
+
+ /** The code owner config reference that references the imported code owner config. */
+ public abstract CodeOwnerConfigReference codeOwnerConfigReference();
+
+ /**
+ * If the import couldn't be resolved, a message explaining why the code owner config reference
+ * couldn't be resolved.
+ */
+ public abstract Optional<String> errorMessage();
+
+ @Override
+ public final String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("keyOfImportingCodeOwnerConfig", keyOfImportingCodeOwnerConfig())
+ .add("keyOfImportedCodeOwnerConfig", keyOfImportedCodeOwnerConfig())
+ .add("codeOwnerConfigReference", codeOwnerConfigReference())
+ .add("errorMessage", errorMessage())
+ .toString();
+ }
+
+ /** Creates a {@link CodeOwnerConfigImport} instance for an unresolved import. */
+ @VisibleForTesting
+ public static CodeOwnerConfigImport createUnresolvedImport(
+ CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig,
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig,
+ CodeOwnerConfigReference codeOwnerConfigReference,
+ String errorMessage) {
+ return new AutoValue_CodeOwnerConfigImport(
+ keyOfImportingCodeOwnerConfig,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ Optional.of(errorMessage));
+ }
+
+ /** Creates a {@link CodeOwnerConfigImport} instance for a resolved import. */
+ @VisibleForTesting
+ public static CodeOwnerConfigImport createResolvedImport(
+ CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig,
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig,
+ CodeOwnerConfigReference codeOwnerConfigReference) {
+ return new AutoValue_CodeOwnerConfigImport(
+ keyOfImportingCodeOwnerConfig,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ Optional.empty());
+ }
+}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
index 07894ce..7db6b68 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolver.java
@@ -50,7 +50,6 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
@@ -234,6 +233,7 @@
return resolve(
pathCodeOwnersResult.get().getPathCodeOwners(),
pathCodeOwnersResult.get().getAnnotations(),
+ pathCodeOwnersResult.get().resolvedImports(),
pathCodeOwnersResult.get().unresolvedImports(),
pathCodeOwnersResult.messages());
}
@@ -260,6 +260,7 @@
return resolve(
codeOwnerReferences,
/* annotationsByCodeOwnerReference= */ ImmutableMultimap.of(),
+ /* resolvedImports= */ ImmutableList.of(),
/* unresolvedImports= */ ImmutableList.of(),
/* pathCodeOwnersMessages= */ ImmutableList.of());
}
@@ -279,9 +280,11 @@
private CodeOwnerResolverResult resolve(
Set<CodeOwnerReference> codeOwnerReferences,
ImmutableMultimap<CodeOwnerReference, CodeOwnerAnnotation> annotationsByCodeOwnerReference,
- List<UnresolvedImport> unresolvedImports,
+ ImmutableList<CodeOwnerConfigImport> resolvedImports,
+ ImmutableList<CodeOwnerConfigImport> unresolvedImports,
ImmutableList<String> pathCodeOwnersMessages) {
requireNonNull(codeOwnerReferences, "codeOwnerReferences");
+ requireNonNull(resolvedImports, "resolvedImports");
requireNonNull(unresolvedImports, "unresolvedImports");
requireNonNull(pathCodeOwnersMessages, "pathCodeOwnersMessages");
@@ -313,7 +316,8 @@
annotationsByCodeOwner.build(),
ownedByAllUsers.get(),
hasUnresolvedCodeOwners.get(),
- !unresolvedImports.isEmpty(),
+ resolvedImports,
+ unresolvedImports,
messageBuilder.build());
logger.atFine().log("resolve result = %s", codeOwnerResolverResult);
return codeOwnerResolverResult;
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverResult.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverResult.java
index 7dd9e96..4db3a4c 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverResult.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverResult.java
@@ -52,8 +52,16 @@
/** Whether there are code owner references which couldn't be resolved. */
public abstract boolean hasUnresolvedCodeOwners();
+ /** Imports which were successfully resolved. */
+ public abstract ImmutableList<CodeOwnerConfigImport> resolvedImports();
+
+ /** Imports which couldn't be resolved. */
+ public abstract ImmutableList<CodeOwnerConfigImport> unresolvedImports();
+
/** Whether there are imports which couldn't be resolved. */
- public abstract boolean hasUnresolvedImports();
+ public boolean hasUnresolvedImports() {
+ return !unresolvedImports().isEmpty();
+ }
/** Gets messages that were collected while resolving the code owners. */
public abstract ImmutableList<String> messages();
@@ -76,7 +84,8 @@
.add("annotations", annotations())
.add("ownedByAllUsers", ownedByAllUsers())
.add("hasUnresolvedCodeOwners", hasUnresolvedCodeOwners())
- .add("hasUnresolvedImports", hasUnresolvedImports())
+ .add("resolvedImports", resolvedImports())
+ .add("unresolvedImports", unresolvedImports())
.add("messages", messages())
.toString();
}
@@ -87,25 +96,16 @@
ImmutableMultimap<CodeOwner, CodeOwnerAnnotation> annotations,
boolean ownedByAllUsers,
boolean hasUnresolvedCodeOwners,
- boolean hasUnresolvedImports,
+ ImmutableList<CodeOwnerConfigImport> resolvedImports,
+ ImmutableList<CodeOwnerConfigImport> unresolvedImports,
List<String> messages) {
return new AutoValue_CodeOwnerResolverResult(
codeOwners,
annotations,
ownedByAllUsers,
hasUnresolvedCodeOwners,
- hasUnresolvedImports,
+ resolvedImports,
+ unresolvedImports,
ImmutableList.copyOf(messages));
}
-
- /** Creates a empty {@link CodeOwnerResolverResult} instance. */
- public static CodeOwnerResolverResult createEmpty() {
- return new AutoValue_CodeOwnerResolverResult(
- /* codeOwners= */ ImmutableSet.of(),
- /* annotations= */ ImmutableMultimap.of(),
- /* ownedByAllUsers= */ false,
- /* hasUnresolvedCodeOwners= */ false,
- /* hasUnresolvedImports= */ false,
- /* messages= */ ImmutableList.of());
- }
}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnersOnAddReviewer.java b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnersOnAddReviewer.java
index 5052edb..78f0056 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnersOnAddReviewer.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/CodeOwnersOnAddReviewer.java
@@ -14,6 +14,8 @@
package com.google.gerrit.plugins.codeowners.backend;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.CHANGE_MODIFICATION;
+import static com.google.gerrit.server.update.context.RefUpdateContext.RefUpdateType.PLUGIN;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableList;
@@ -36,6 +38,7 @@
import com.google.gerrit.server.update.BatchUpdateOp;
import com.google.gerrit.server.update.ChangeContext;
import com.google.gerrit.server.update.RetryHelper;
+import com.google.gerrit.server.update.context.RefUpdateContext;
import com.google.gerrit.server.util.AccountTemplateUtil;
import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext;
@@ -157,7 +160,9 @@
"addCodeOwnersMessageOnAddReviewer",
updateFactory -> {
try (BatchUpdate batchUpdate =
- updateFactory.create(projectName, currentUser, when)) {
+ updateFactory.create(projectName, currentUser, when);
+ RefUpdateContext pluginCtx = RefUpdateContext.open(PLUGIN);
+ RefUpdateContext changeCtx = RefUpdateContext.open(CHANGE_MODIFICATION)) {
batchUpdate.addOp(changeId, new Op(reviewers, maxPathsInChangeMessages));
batchUpdate.execute();
}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java b/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java
index a61c913..5e7a11f 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwners.java
@@ -252,17 +252,18 @@
}
// Resolve global imports.
- ImmutableList.Builder<UnresolvedImport> unresolvedImports = ImmutableList.builder();
- ImmutableSet<CodeOwnerConfigImport> globalImports = getGlobalImports(0, codeOwnerConfig);
- OptionalResultWithMessages<List<UnresolvedImport>> unresolvedGlobalImports;
+ ImmutableList.Builder<CodeOwnerConfigImport> resolvedImports = ImmutableList.builder();
+ ImmutableList.Builder<CodeOwnerConfigImport> unresolvedImports = ImmutableList.builder();
+ ImmutableSet<CodeOwnerImport> globalImports = getGlobalImports(0, codeOwnerConfig);
+ OptionalResultWithMessages<CodeOwnerConfigImports> globalImportedCodeOwnerConfigs;
if (!globalCodeOwnersIgnored) {
- unresolvedGlobalImports =
+ globalImportedCodeOwnerConfigs =
resolveImports(codeOwnerConfig.key(), globalImports, resolvedCodeOwnerConfigBuilder);
} else {
// skip global import with mode GLOBAL_CODE_OWNER_SETS_ONLY,
// since we already know that global code owners will be ignored, we do not need to resolve
// these imports
- unresolvedGlobalImports =
+ globalImportedCodeOwnerConfigs =
resolveImports(
codeOwnerConfig.key(),
globalImports.stream()
@@ -273,8 +274,9 @@
.collect(toImmutableSet()),
resolvedCodeOwnerConfigBuilder);
}
- messages.addAll(unresolvedGlobalImports.messages());
- unresolvedImports.addAll(unresolvedGlobalImports.get());
+ messages.addAll(globalImportedCodeOwnerConfigs.messages());
+ resolvedImports.addAll(globalImportedCodeOwnerConfigs.get().resolved());
+ unresolvedImports.addAll(globalImportedCodeOwnerConfigs.get().unresolved());
// Remove all global code owner sets if any per-file code owner set has the
// ignoreGlobalAndParentCodeOwners flag set to true (as in this case they are ignored and
@@ -317,18 +319,22 @@
}
// Resolve per-file imports.
- ImmutableSet<CodeOwnerConfigImport> perFileImports =
+ ImmutableSet<CodeOwnerImport> perFileImports =
getPerFileImports(
0, codeOwnerConfig.key(), resolvedCodeOwnerConfigBuilder.codeOwnerSets());
- OptionalResultWithMessages<List<UnresolvedImport>> unresolvedPerFileImports =
+ OptionalResultWithMessages<CodeOwnerConfigImports> perFileImportedCodeOwnerConfigs =
resolveImports(codeOwnerConfig.key(), perFileImports, resolvedCodeOwnerConfigBuilder);
- messages.addAll(unresolvedPerFileImports.messages());
- unresolvedImports.addAll(unresolvedPerFileImports.get());
+ messages.addAll(perFileImportedCodeOwnerConfigs.messages());
+ resolvedImports.addAll(perFileImportedCodeOwnerConfigs.get().resolved());
+ unresolvedImports.addAll(perFileImportedCodeOwnerConfigs.get().unresolved());
this.pathCodeOwnersResult =
OptionalResultWithMessages.create(
PathCodeOwnersResult.create(
- path, resolvedCodeOwnerConfigBuilder.build(), unresolvedImports.build()),
+ path,
+ resolvedCodeOwnerConfigBuilder.build(),
+ resolvedImports.build(),
+ unresolvedImports.build()),
messages);
logger.atFine().log("path code owners result = %s", pathCodeOwnersResult);
return this.pathCodeOwnersResult;
@@ -343,11 +349,12 @@
* @param resolvedCodeOwnerConfigBuilder the builder for the resolved code owner config
* @return list of unresolved imports, empty list if all imports were successfully resolved
*/
- private OptionalResultWithMessages<List<UnresolvedImport>> resolveImports(
+ private OptionalResultWithMessages<CodeOwnerConfigImports> resolveImports(
CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig,
- Set<CodeOwnerConfigImport> codeOwnerConfigImports,
+ Set<CodeOwnerImport> codeOwnerConfigImports,
CodeOwnerConfig.Builder resolvedCodeOwnerConfigBuilder) {
- ImmutableList.Builder<UnresolvedImport> unresolvedImports = ImmutableList.builder();
+ ImmutableList.Builder<CodeOwnerConfigImport> resolvedImports = ImmutableList.builder();
+ ImmutableList.Builder<CodeOwnerConfigImport> unresolvedImports = ImmutableList.builder();
StringBuilder messageBuilder = new StringBuilder();
try (Timer0.Context ctx = codeOwnerMetrics.resolveCodeOwnerConfigImports.start()) {
logger.atFine().log("resolve imports of codeOwnerConfig %s", keyOfImportingCodeOwnerConfig);
@@ -361,7 +368,7 @@
Map<BranchNameKey, ObjectId> revisionMap = new HashMap<>();
revisionMap.put(codeOwnerConfig.key().branchNameKey(), codeOwnerConfig.revision());
- Queue<CodeOwnerConfigImport> codeOwnerConfigsToImport = new ArrayDeque<>();
+ Queue<CodeOwnerImport> codeOwnerConfigsToImport = new ArrayDeque<>();
codeOwnerConfigsToImport.addAll(codeOwnerConfigImports);
if (!codeOwnerConfigsToImport.isEmpty()) {
messageBuilder.append(
@@ -370,7 +377,7 @@
keyOfImportingCodeOwnerConfig.format(codeOwners)));
}
while (!codeOwnerConfigsToImport.isEmpty()) {
- CodeOwnerConfigImport codeOwnerConfigImport = codeOwnerConfigsToImport.poll();
+ CodeOwnerImport codeOwnerConfigImport = codeOwnerConfigsToImport.poll();
messageBuilder.append(codeOwnerConfigImport.format());
CodeOwnerConfigReference codeOwnerConfigReference =
@@ -387,7 +394,7 @@
projectCache.get(keyOfImportedCodeOwnerConfig.project());
if (!projectState.isPresent()) {
unresolvedImports.add(
- UnresolvedImport.create(
+ CodeOwnerConfigImport.createUnresolvedImport(
codeOwnerConfigImport.importingCodeOwnerConfig(),
keyOfImportedCodeOwnerConfig,
codeOwnerConfigReference,
@@ -399,7 +406,7 @@
}
if (!projectState.get().statePermitsRead()) {
unresolvedImports.add(
- UnresolvedImport.create(
+ CodeOwnerConfigImport.createUnresolvedImport(
codeOwnerConfigImport.importingCodeOwnerConfig(),
keyOfImportedCodeOwnerConfig,
codeOwnerConfigReference,
@@ -425,7 +432,7 @@
if (!mayBeImportedCodeOwnerConfig.isPresent()) {
unresolvedImports.add(
- UnresolvedImport.create(
+ CodeOwnerConfigImport.createUnresolvedImport(
codeOwnerConfigImport.importingCodeOwnerConfig(),
keyOfImportedCodeOwnerConfig,
codeOwnerConfigReference,
@@ -439,6 +446,13 @@
}
CodeOwnerConfig importedCodeOwnerConfig = mayBeImportedCodeOwnerConfig.get();
+
+ resolvedImports.add(
+ CodeOwnerConfigImport.createResolvedImport(
+ codeOwnerConfigImport.importingCodeOwnerConfig(),
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+
CodeOwnerConfigImportMode importMode = codeOwnerConfigReference.importMode();
logger.atFine().log("import mode = %s", importMode.name());
@@ -475,7 +489,7 @@
if (importMode.resolveImportsOfImport()
&& seenCodeOwnerConfigs.add(keyOfImportedCodeOwnerConfig)) {
logger.atFine().log("resolve imports of imported code owner config");
- Set<CodeOwnerConfigImport> transitiveImports = new HashSet<>();
+ Set<CodeOwnerImport> transitiveImports = new HashSet<>();
transitiveImports.addAll(
getGlobalImports(codeOwnerConfigImport.importLevel() + 1, importedCodeOwnerConfig));
transitiveImports.addAll(
@@ -495,7 +509,7 @@
transitiveImports.stream()
.map(
codeOwnerCfgImport ->
- CodeOwnerConfigImport.create(
+ CodeOwnerImport.create(
codeOwnerCfgImport.importLevel(),
codeOwnerCfgImport.importingCodeOwnerConfig(),
CodeOwnerConfigReference.copyWithNewImportMode(
@@ -516,31 +530,31 @@
message = message.substring(0, message.length() - 1);
}
return OptionalResultWithMessages.create(
- unresolvedImports.build(),
+ CodeOwnerConfigImports.create(resolvedImports.build(), unresolvedImports.build()),
!message.isEmpty() ? ImmutableList.of(message) : ImmutableList.of());
}
- private ImmutableSet<CodeOwnerConfigImport> getGlobalImports(
+ private ImmutableSet<CodeOwnerImport> getGlobalImports(
int importLevel, CodeOwnerConfig codeOwnerConfig) {
return codeOwnerConfig.imports().stream()
.map(
codeOwnerConfigReference ->
- CodeOwnerConfigImport.create(
+ CodeOwnerImport.create(
importLevel, codeOwnerConfig.key(), codeOwnerConfigReference))
.collect(toImmutableSet());
}
- private ImmutableSet<CodeOwnerConfigImport> getPerFileImports(
+ private ImmutableSet<CodeOwnerImport> getPerFileImports(
int importLevel,
CodeOwnerConfig.Key importingCodeOwnerConfig,
Set<CodeOwnerSet> codeOwnerSets) {
- ImmutableSet.Builder<CodeOwnerConfigImport> codeOwnerConfigImports = ImmutableSet.builder();
+ ImmutableSet.Builder<CodeOwnerImport> codeOwnerConfigImports = ImmutableSet.builder();
for (CodeOwnerSet codeOwnerSet : codeOwnerSets) {
codeOwnerSet.imports().stream()
.forEach(
codeOwnerConfigReference ->
codeOwnerConfigImports.add(
- CodeOwnerConfigImport.create(
+ CodeOwnerImport.create(
importLevel,
importingCodeOwnerConfig,
codeOwnerConfigReference,
@@ -623,7 +637,7 @@
}
@AutoValue
- abstract static class CodeOwnerConfigImport {
+ abstract static class CodeOwnerImport {
/**
* The import level.
*
@@ -683,7 +697,7 @@
return levels > 0 ? String.format("%" + (levels * 2) + "s", "") : "";
}
- public static CodeOwnerConfigImport create(
+ public static CodeOwnerImport create(
int importLevel,
CodeOwnerConfig.Key importingCodeOwnerConfig,
CodeOwnerConfigReference codeOwnerConfigReference) {
@@ -691,7 +705,7 @@
importLevel, importingCodeOwnerConfig, codeOwnerConfigReference, Optional.empty());
}
- public static CodeOwnerConfigImport create(
+ public static CodeOwnerImport create(
int importLevel,
CodeOwnerConfig.Key importingCodeOwnerConfig,
CodeOwnerConfigReference codeOwnerConfigReference,
@@ -703,13 +717,28 @@
Optional.of(codeOwnerSet));
}
- public static CodeOwnerConfigImport create(
+ public static CodeOwnerImport create(
int importLevel,
CodeOwnerConfig.Key importingCodeOwnerConfig,
CodeOwnerConfigReference codeOwnerConfigReference,
Optional<CodeOwnerSet> codeOwnerSet) {
- return new AutoValue_PathCodeOwners_CodeOwnerConfigImport(
+ return new AutoValue_PathCodeOwners_CodeOwnerImport(
importLevel, importingCodeOwnerConfig, codeOwnerConfigReference, codeOwnerSet);
}
}
+
+ @AutoValue
+ abstract static class CodeOwnerConfigImports {
+ /** Imported code owner configs the could be resolved. */
+ abstract ImmutableList<CodeOwnerConfigImport> resolved();
+
+ /** Imported code owner configs the could not be resolved. */
+ abstract ImmutableList<CodeOwnerConfigImport> unresolved();
+
+ static CodeOwnerConfigImports create(
+ ImmutableList<CodeOwnerConfigImport> resolved,
+ ImmutableList<CodeOwnerConfigImport> unresolved) {
+ return new AutoValue_PathCodeOwners_CodeOwnerConfigImports(resolved, unresolved);
+ }
+ }
}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResult.java b/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResult.java
index 93a36be..5b262bf 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResult.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResult.java
@@ -36,8 +36,11 @@
/** Gets the resolved code owner config. */
abstract CodeOwnerConfig codeOwnerConfig();
+ /** Gets a list of resolved imports. */
+ public abstract ImmutableList<CodeOwnerConfigImport> resolvedImports();
+
/** Gets a list of unresolved imports. */
- public abstract ImmutableList<UnresolvedImport> unresolvedImports();
+ public abstract ImmutableList<CodeOwnerConfigImport> unresolvedImports();
/** Whether there are unresolved imports. */
public boolean hasUnresolvedImports() {
@@ -104,14 +107,21 @@
return MoreObjects.toStringHelper(this)
.add("path", path())
.add("codeOwnerConfig", codeOwnerConfig())
+ .add("resolvedImports", resolvedImports())
.add("unresolvedImports", unresolvedImports())
.toString();
}
/** Creates a {@link PathCodeOwnersResult} instance. */
public static PathCodeOwnersResult create(
- Path path, CodeOwnerConfig codeOwnerConfig, List<UnresolvedImport> unresolvedImports) {
+ Path path,
+ CodeOwnerConfig codeOwnerConfig,
+ List<CodeOwnerConfigImport> resolvedImports,
+ List<CodeOwnerConfigImport> unresolvedImports) {
return new AutoValue_PathCodeOwnersResult(
- path, codeOwnerConfig, ImmutableList.copyOf(unresolvedImports));
+ path,
+ codeOwnerConfig,
+ ImmutableList.copyOf(resolvedImports),
+ ImmutableList.copyOf(unresolvedImports));
}
}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/UnresolvedImport.java b/java/com/google/gerrit/plugins/codeowners/backend/UnresolvedImport.java
deleted file mode 100644
index bea1e08..0000000
--- a/java/com/google/gerrit/plugins/codeowners/backend/UnresolvedImport.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// 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;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.MoreObjects;
-
-/** Information about an unresolved import. */
-@AutoValue
-public abstract class UnresolvedImport {
- /** Key of the importing code owner config. */
- public abstract CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig();
-
- /** Key of the imported code owner config. */
- public abstract CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig();
-
- /** The code owner config reference that was attempted to be resolved. */
- public abstract CodeOwnerConfigReference codeOwnerConfigReference();
-
- /** Message explaining why the code owner config reference couldn't be resolved. */
- public abstract String message();
-
- @Override
- public final String toString() {
- return MoreObjects.toStringHelper(this)
- .add("keyOfImportingCodeOwnerConfig", keyOfImportingCodeOwnerConfig())
- .add("keyOfImportedCodeOwnerConfig", keyOfImportedCodeOwnerConfig())
- .add("codeOwnerConfigReference", codeOwnerConfigReference())
- .add("message", message())
- .toString();
- }
-
- /** Creates a {@link UnresolvedImport} instance. */
- static UnresolvedImport create(
- CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig,
- CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig,
- CodeOwnerConfigReference codeOwnerConfigReference,
- String message) {
- return new AutoValue_UnresolvedImport(
- keyOfImportingCodeOwnerConfig,
- keyOfImportedCodeOwnerConfig,
- codeOwnerConfigReference,
- message);
- }
-}
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/UnresolvedImportFormatter.java b/java/com/google/gerrit/plugins/codeowners/backend/UnresolvedImportFormatter.java
index 52958d9..7946468 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/UnresolvedImportFormatter.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/UnresolvedImportFormatter.java
@@ -20,7 +20,7 @@
import com.google.inject.Inject;
import java.nio.file.Path;
-/** Class to format an {@link UnresolvedImport} as a user-readable string. */
+/** Class to format an {@link CodeOwnerConfigImport} as a user-readable string. */
public class UnresolvedImportFormatter {
private final CodeOwnersPluginConfiguration codeOwnersPluginConfiguration;
private final ProjectCache projectCache;
@@ -37,7 +37,7 @@
}
/** Returns a user-readable string representation of the given unresolved import. */
- public String format(UnresolvedImport unresolvedImport) {
+ public String format(CodeOwnerConfigImport unresolvedImport) {
return String.format(
"The import of %s:%s:%s in %s:%s:%s cannot be resolved: %s",
unresolvedImport.keyOfImportedCodeOwnerConfig().project(),
@@ -46,10 +46,16 @@
unresolvedImport.keyOfImportingCodeOwnerConfig().project(),
unresolvedImport.keyOfImportingCodeOwnerConfig().shortBranchName(),
getFilePath(unresolvedImport.keyOfImportingCodeOwnerConfig()),
- unresolvedImport.message());
+ unresolvedImport
+ .errorMessage()
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ String.format(
+ "unresolved import %s must have an error message", unresolvedImport))));
}
- private Path getFilePath(CodeOwnerConfig.Key codeOwnerConfigKey) {
+ public Path getFilePath(CodeOwnerConfig.Key codeOwnerConfigKey) {
return getBackend(codeOwnerConfigKey).getFilePath(codeOwnerConfigKey);
}
@@ -60,6 +66,10 @@
* returned.
*/
private CodeOwnerBackend getBackend(CodeOwnerConfig.Key codeOwnerConfigKey) {
+ // For unresolved imports the project may not exist. Trying to get the project config for
+ // non-existing projects fails, hence check whether the project exists before trying to access
+ // the project config and fall back to the default code owner backend if the project doesn't
+ // exist.
if (projectCache.get(codeOwnerConfigKey.project()).isPresent()) {
return codeOwnersPluginConfiguration
.getProjectConfig(codeOwnerConfigKey.project())
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/AbstractGetCodeOwnersForPath.java b/java/com/google/gerrit/plugins/codeowners/restapi/AbstractGetCodeOwnersForPath.java
index 677e67c..522da89 100644
--- a/java/com/google/gerrit/plugins/codeowners/restapi/AbstractGetCodeOwnersForPath.java
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/AbstractGetCodeOwnersForPath.java
@@ -38,6 +38,7 @@
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.plugins.codeowners.api.CodeOwnerConfigFileInfo;
import com.google.gerrit.plugins.codeowners.api.CodeOwnersInfo;
import com.google.gerrit.plugins.codeowners.backend.CodeOwner;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerAnnotation;
@@ -92,6 +93,7 @@
private final CodeOwnerConfigHierarchy codeOwnerConfigHierarchy;
private final Provider<CodeOwnerResolver> codeOwnerResolver;
private final CodeOwnerJson.Factory codeOwnerJsonFactory;
+ private final CodeOwnerConfigFileJson codeOwnerConfigFileJson;
private final EnumSet<ListAccountsOption> options;
private final Set<String> hexOptions;
@@ -167,7 +169,8 @@
CodeOwnersPluginConfiguration codeOwnersPluginConfiguration,
CodeOwnerConfigHierarchy codeOwnerConfigHierarchy,
Provider<CodeOwnerResolver> codeOwnerResolver,
- CodeOwnerJson.Factory codeOwnerJsonFactory) {
+ CodeOwnerJson.Factory codeOwnerJsonFactory,
+ CodeOwnerConfigFileJson codeOwnerConfigFileJson) {
this.accountVisibility = accountVisibility;
this.accounts = accounts;
this.accountControlFactory = accountControlFactory;
@@ -178,6 +181,7 @@
this.codeOwnerConfigHierarchy = codeOwnerConfigHierarchy;
this.codeOwnerResolver = codeOwnerResolver;
this.codeOwnerJsonFactory = codeOwnerJsonFactory;
+ this.codeOwnerConfigFileJson = codeOwnerConfigFileJson;
this.options = EnumSet.noneOf(ListAccountsOption.class);
this.hexOptions = new HashSet<>();
}
@@ -213,6 +217,8 @@
ListMultimap<CodeOwner, CodeOwnerAnnotation> annotations = LinkedListMultimap.create();
AtomicBoolean ownedByAllUsers = new AtomicBoolean(false);
ImmutableList.Builder<String> debugLogsBuilder = ImmutableList.builder();
+ ImmutableList.Builder<CodeOwnerConfigFileInfo> codeOwnerConfigFileInfosBuilder =
+ ImmutableList.builder();
codeOwnerConfigHierarchy.visit(
rsrc.getBranch(),
rsrc.getRevision(),
@@ -221,6 +227,12 @@
CodeOwnerResolverResult pathCodeOwners =
codeOwnerResolver.get().resolvePathCodeOwners(codeOwnerConfig, rsrc.getPath());
+ codeOwnerConfigFileInfosBuilder.add(
+ codeOwnerConfigFileJson.format(
+ codeOwnerConfig.key(),
+ pathCodeOwners.resolvedImports(),
+ pathCodeOwners.unresolvedImports()));
+
debugLogsBuilder.addAll(pathCodeOwners.messages());
codeOwners.addAll(pathCodeOwners.codeOwners());
annotations.putAll(pathCodeOwners.annotations());
@@ -308,7 +320,7 @@
ImmutableMap<CodeOwner, Double> scoredCodeOwners =
codeOwnerScorings.getScorings(filteredCodeOwners);
- ImmutableList<CodeOwner> sortedAndLimitedCodeOwners = sortAndLimit(rsrc, scoredCodeOwners);
+ ImmutableList<CodeOwner> sortedAndLimitedCodeOwners = sortAndLimit(scoredCodeOwners);
if (highestScoreOnly) {
Optional<Double> highestScore =
@@ -325,7 +337,7 @@
codeOwnersInfo.codeOwners =
codeOwnerJsonFactory.create(getFillOptions()).format(sortedAndLimitedCodeOwners);
codeOwnersInfo.ownedByAllUsers = ownedByAllUsers.get() ? true : null;
-
+ codeOwnersInfo.codeOwnerConfigs = codeOwnerConfigFileInfosBuilder.build();
ImmutableList<String> debugLogs = debugLogsBuilder.build();
codeOwnersInfo.debugLogs = debug ? debugLogs : null;
logger.atFine().log("debug logs: %s", debugLogs);
@@ -480,9 +492,8 @@
return fillOptions;
}
- private ImmutableList<CodeOwner> sortAndLimit(
- R rsrc, ImmutableMap<CodeOwner, Double> scoredCodeOwners) {
- return sortCodeOwners(rsrc, seed, scoredCodeOwners).limit(limit).collect(toImmutableList());
+ private ImmutableList<CodeOwner> sortAndLimit(ImmutableMap<CodeOwner, Double> scoredCodeOwners) {
+ return sortCodeOwners(seed, scoredCodeOwners).limit(limit).collect(toImmutableList());
}
/**
@@ -492,13 +503,12 @@
*
* <p>The order of code owners with the same score is random.
*
- * @param rsrc resource on which this REST endpoint is invoked
* @param seed seed that should be used to randomize the order
* @param scoredCodeOwners the code owners with their scores
* @return the sorted code owners
*/
private Stream<CodeOwner> sortCodeOwners(
- R rsrc, Optional<Long> seed, ImmutableMap<CodeOwner, Double> scoredCodeOwners) {
+ Optional<Long> seed, ImmutableMap<CodeOwner, Double> scoredCodeOwners) {
return randomizeOrder(seed, scoredCodeOwners.keySet())
.sorted(Comparator.comparingDouble(scoredCodeOwners::get).reversed());
}
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/CodeOwnerConfigFileJson.java b/java/com/google/gerrit/plugins/codeowners/restapi/CodeOwnerConfigFileJson.java
new file mode 100644
index 0000000..fe17a3f
--- /dev/null
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/CodeOwnerConfigFileJson.java
@@ -0,0 +1,119 @@
+// Copyright (C) 2023 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.restapi;
+
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableList;
+import com.google.gerrit.plugins.codeowners.api.CodeOwnerConfigFileInfo;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImport;
+import com.google.gerrit.plugins.codeowners.backend.UnresolvedImportFormatter;
+import com.google.inject.Inject;
+import java.util.List;
+
+/** Collection of routines to populate {@link CodeOwnerConfigFileInfo}. */
+public class CodeOwnerConfigFileJson {
+ private final UnresolvedImportFormatter unresolvedImportFormatter;
+
+ @Inject
+ CodeOwnerConfigFileJson(UnresolvedImportFormatter unresolvedImportFormatter) {
+ this.unresolvedImportFormatter = unresolvedImportFormatter;
+ }
+
+ /**
+ * Formats the provided code owner config file information as a {@link CodeOwnerConfigFileInfo}.
+ *
+ * @param codeOwnerConfigKey the key of the code owner config file as {@link
+ * CodeOwnerConfigFileInfo}
+ * @param resolvedImports code owner config files which have been successfully imported directly
+ * or indirectly
+ * @param unresolvedImports code owner config files which are imported directly or indirectly but
+ * couldn't be resolved
+ * @return the provided {@link com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig.Key}
+ * as {@link CodeOwnerConfigFileInfo}
+ */
+ public CodeOwnerConfigFileInfo format(
+ CodeOwnerConfig.Key codeOwnerConfigKey,
+ List<CodeOwnerConfigImport> resolvedImports,
+ List<CodeOwnerConfigImport> unresolvedImports) {
+ requireNonNull(codeOwnerConfigKey, "codeOwnerConfigKey");
+ requireNonNull(resolvedImports, "resolvedImports");
+ requireNonNull(unresolvedImports, "unresolvedImports");
+
+ CodeOwnerConfigFileInfo info = new CodeOwnerConfigFileInfo();
+
+ info.project = codeOwnerConfigKey.project().get();
+ info.branch = codeOwnerConfigKey.branchNameKey().branch();
+ info.path = unresolvedImportFormatter.getFilePath(codeOwnerConfigKey).toString();
+
+ ImmutableList<CodeOwnerConfigFileInfo> unresolvedImportInfos =
+ unresolvedImports.stream()
+ .filter(
+ unresolvedImport ->
+ unresolvedImport.keyOfImportingCodeOwnerConfig().equals(codeOwnerConfigKey))
+ .map(
+ unresolvedImport -> {
+ CodeOwnerConfigFileInfo unresolvedCodeOwnerConfigFileInfo =
+ format(
+ unresolvedImport.keyOfImportedCodeOwnerConfig(),
+ /* resolvedImports= */ ImmutableList.of(),
+ /* unresolvedImports= */ ImmutableList.of());
+ unresolvedCodeOwnerConfigFileInfo.importMode =
+ unresolvedImport.codeOwnerConfigReference().importMode();
+ unresolvedCodeOwnerConfigFileInfo.unresolvedErrorMessage =
+ unresolvedImport
+ .errorMessage()
+ .orElseThrow(
+ () ->
+ new IllegalStateException(
+ String.format(
+ "unresolved import %s must have an error message",
+ unresolvedImport)));
+ return unresolvedCodeOwnerConfigFileInfo;
+ })
+ .collect(toImmutableList());
+ info.unresolvedImports = !unresolvedImportInfos.isEmpty() ? unresolvedImportInfos : null;
+
+ ImmutableList<CodeOwnerConfigFileInfo> importInfos =
+ resolvedImports.stream()
+ .filter(
+ resolvedImport ->
+ resolvedImport.keyOfImportingCodeOwnerConfig().equals(codeOwnerConfigKey))
+ .map(
+ resolvedImport -> {
+ CodeOwnerConfigFileInfo resolvedCodeOwnerConfigFileInfo =
+ format(
+ resolvedImport.keyOfImportedCodeOwnerConfig(),
+ removeImportEntriesFor(resolvedImports, codeOwnerConfigKey),
+ removeImportEntriesFor(unresolvedImports, codeOwnerConfigKey));
+ resolvedCodeOwnerConfigFileInfo.importMode =
+ resolvedImport.codeOwnerConfigReference().importMode();
+ return resolvedCodeOwnerConfigFileInfo;
+ })
+ .collect(toImmutableList());
+ info.imports = !importInfos.isEmpty() ? importInfos : null;
+
+ return info;
+ }
+
+ private ImmutableList<CodeOwnerConfigImport> removeImportEntriesFor(
+ List<CodeOwnerConfigImport> imports, CodeOwnerConfig.Key codeOwnerConfigKey) {
+ return imports.stream()
+ .filter(i -> !i.keyOfImportingCodeOwnerConfig().equals(codeOwnerConfigKey))
+ .collect(toImmutableList());
+ }
+}
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnersForPathInBranch.java b/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnersForPathInBranch.java
index e0a9145..85410e9 100644
--- a/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnersForPathInBranch.java
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnersForPathInBranch.java
@@ -82,6 +82,7 @@
CodeOwnerConfigHierarchy codeOwnerConfigHierarchy,
Provider<CodeOwnerResolver> codeOwnerResolver,
CodeOwnerJson.Factory codeOwnerJsonFactory,
+ CodeOwnerConfigFileJson codeOwnerConfigFileJson,
GitRepositoryManager repoManager) {
super(
accountVisibility,
@@ -93,7 +94,8 @@
codeOwnersPluginConfiguration,
codeOwnerConfigHierarchy,
codeOwnerResolver,
- codeOwnerJsonFactory);
+ codeOwnerJsonFactory,
+ codeOwnerConfigFileJson);
this.repoManager = repoManager;
}
diff --git a/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnersForPathInChange.java b/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnersForPathInChange.java
index 3ed9a51..b7149c5 100644
--- a/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnersForPathInChange.java
+++ b/java/com/google/gerrit/plugins/codeowners/restapi/GetCodeOwnersForPathInChange.java
@@ -73,7 +73,8 @@
CodeOwnerConfigHierarchy codeOwnerConfigHierarchy,
Provider<CodeOwnerResolver> codeOwnerResolver,
ServiceUserClassifier serviceUserClassifier,
- CodeOwnerJson.Factory codeOwnerJsonFactory) {
+ CodeOwnerJson.Factory codeOwnerJsonFactory,
+ CodeOwnerConfigFileJson codeOwnerConfigFileJson) {
super(
accountVisibility,
accounts,
@@ -84,7 +85,8 @@
codeOwnersPluginConfiguration,
codeOwnerConfigHierarchy,
codeOwnerResolver,
- codeOwnerJsonFactory);
+ codeOwnerJsonFactory,
+ codeOwnerConfigFileJson);
this.serviceUserClassifier = serviceUserClassifier;
}
diff --git a/java/com/google/gerrit/plugins/codeowners/testing/CodeOwnerConfigFileInfoSubject.java b/java/com/google/gerrit/plugins/codeowners/testing/CodeOwnerConfigFileInfoSubject.java
new file mode 100644
index 0000000..4d09a10
--- /dev/null
+++ b/java/com/google/gerrit/plugins/codeowners/testing/CodeOwnerConfigFileInfoSubject.java
@@ -0,0 +1,188 @@
+// Copyright (C) 2023 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.testing;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.truth.ListSubject.elements;
+
+import com.google.common.truth.FailureMetadata;
+import com.google.common.truth.StringSubject;
+import com.google.common.truth.Subject;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import com.google.gerrit.plugins.codeowners.api.CodeOwnerConfigFileInfo;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerBackend;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImportMode;
+import com.google.gerrit.truth.ListSubject;
+
+/** {@link Subject} for doing assertions on {@link CodeOwnerConfigFileInfo}s. */
+public class CodeOwnerConfigFileInfoSubject extends Subject {
+ /**
+ * Starts fluent chain to do assertions on a {@link CodeOwnerConfigFileInfo}.
+ *
+ * @param codeOwnerConfigFileInfo the code owner config file info on which assertions should be
+ * done
+ * @return the created {@link CodeOwnerConfigFileInfoSubject}
+ */
+ public static CodeOwnerConfigFileInfoSubject assertThat(
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo) {
+ return assertAbout(codeOwnerConfigFileInfos()).that(codeOwnerConfigFileInfo);
+ }
+
+ public static Factory<CodeOwnerConfigFileInfoSubject, CodeOwnerConfigFileInfo>
+ codeOwnerConfigFileInfos() {
+ return CodeOwnerConfigFileInfoSubject::new;
+ }
+
+ private final CodeOwnerConfigFileInfo codeOwnerConfigFileInfo;
+
+ private CodeOwnerConfigFileInfoSubject(
+ FailureMetadata metadata, CodeOwnerConfigFileInfo codeOwnerConfigFileInfo) {
+ super(metadata, codeOwnerConfigFileInfo);
+ this.codeOwnerConfigFileInfo = codeOwnerConfigFileInfo;
+ }
+
+ /** Returns a subject for the project of the code owner config file info. */
+ public StringSubject hasProjectThat() {
+ return check("project()").that(codeOwnerConfigFileInfo().project);
+ }
+
+ /** Returns a subject for the branch of the code owner config file info. */
+ public StringSubject hasBranchThat() {
+ return check("branch()").that(codeOwnerConfigFileInfo().branch);
+ }
+
+ /** Returns a subject for the path of the code owner config file info. */
+ public StringSubject hasPathThat() {
+ return check("path()").that(codeOwnerConfigFileInfo().path);
+ }
+
+ /**
+ * Returns a {@link ListSubject} for the (resolved) imports of the code owner config file info.
+ */
+ public ListSubject<CodeOwnerConfigFileInfoSubject, CodeOwnerConfigFileInfo> hasImportsThat() {
+ return check("imports()")
+ .about(elements())
+ .thatCustom(codeOwnerConfigFileInfo().imports, codeOwnerConfigFileInfos());
+ }
+
+ /**
+ * Returns a {@link ListSubject} for the unresolved imports of the code owner config file info.
+ */
+ public ListSubject<CodeOwnerConfigFileInfoSubject, CodeOwnerConfigFileInfo>
+ hasUnresolvedImportsThat() {
+ return check("unresolvedimports()")
+ .about(elements())
+ .thatCustom(codeOwnerConfigFileInfo().unresolvedImports, codeOwnerConfigFileInfos());
+ }
+
+ /** Returns a subject for the import mode of the code owner config file info. */
+ public Subject hasImportModeThat() {
+ return check("importMode()").that(codeOwnerConfigFileInfo().importMode);
+ }
+
+ /** Returns a subject for the unresolved error message of the code owner config file info. */
+ public Subject hasUnresolvedErrorMessageThat() {
+ return check("unresolvedErrorMessage()").that(codeOwnerConfigFileInfo().unresolvedErrorMessage);
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertKey(
+ CodeOwnerBackend codeOwnerBackend, CodeOwnerConfig.Key codeOwnerConfigKey) {
+ hasProjectThat().isEqualTo(codeOwnerConfigKey.project().get());
+ hasBranchThat().isEqualTo(codeOwnerConfigKey.branchNameKey().branch());
+ hasPathThat().isEqualTo(codeOwnerBackend.getFilePath(codeOwnerConfigKey).toString());
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertNoResolvedImports() {
+ hasImportsThat().isNull();
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertResolvedImport(
+ CodeOwnerBackend codeOwnerBackend,
+ CodeOwnerConfig.Key codeOwnerConfigKey,
+ CodeOwnerConfigImportMode importMode) {
+ hasImportsThat().hasSize(1);
+ CodeOwnerConfigFileInfoSubject subjectForResolvedImport = hasImportsThat().element(0);
+ subjectForResolvedImport
+ .assertKey(codeOwnerBackend, codeOwnerConfigKey)
+ .assertImportMode(importMode)
+ .assertNoUnresolvedErrorMessage();
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertNoUnresolvedImports() {
+ hasUnresolvedImportsThat().isNull();
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertUnresolvedImport(
+ CodeOwnerBackend codeOwnerBackend,
+ CodeOwnerConfig.Key codeOwnerConfigKey,
+ CodeOwnerConfigImportMode importMode,
+ String unresolvedErrorMessage) {
+ hasUnresolvedImportsThat().hasSize(1);
+ CodeOwnerConfigFileInfoSubject subjectForUnresolvedImport =
+ hasUnresolvedImportsThat().element(0);
+ subjectForUnresolvedImport
+ .assertKey(codeOwnerBackend, codeOwnerConfigKey)
+ .assertImportMode(importMode)
+ .assertUnresolvedErrorMessage(unresolvedErrorMessage);
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertNoImports() {
+ assertNoResolvedImports();
+ assertNoUnresolvedImports();
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertImportMode(CodeOwnerConfigImportMode importMode) {
+ hasImportModeThat().isEqualTo(importMode);
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertNoImportMode() {
+ hasImportModeThat().isNull();
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertUnresolvedErrorMessage(
+ String unresolvedErrorMessage) {
+ hasUnresolvedErrorMessageThat().isEqualTo(unresolvedErrorMessage);
+ return this;
+ }
+
+ @CanIgnoreReturnValue
+ public CodeOwnerConfigFileInfoSubject assertNoUnresolvedErrorMessage() {
+ hasUnresolvedErrorMessageThat().isNull();
+ return this;
+ }
+
+ private CodeOwnerConfigFileInfo codeOwnerConfigFileInfo() {
+ isNotNull();
+ return codeOwnerConfigFileInfo;
+ }
+}
diff --git a/java/com/google/gerrit/plugins/codeowners/testing/CodeOwnersInfoSubject.java b/java/com/google/gerrit/plugins/codeowners/testing/CodeOwnersInfoSubject.java
index 11433e2..9e5fb44 100644
--- a/java/com/google/gerrit/plugins/codeowners/testing/CodeOwnersInfoSubject.java
+++ b/java/com/google/gerrit/plugins/codeowners/testing/CodeOwnersInfoSubject.java
@@ -15,6 +15,7 @@
package com.google.gerrit.plugins.codeowners.testing;
import static com.google.common.truth.Truth.assertAbout;
+import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerConfigFileInfoSubject.codeOwnerConfigFileInfos;
import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerInfoSubject.codeOwnerInfos;
import static com.google.gerrit.truth.ListSubject.elements;
@@ -22,6 +23,7 @@
import com.google.common.truth.FailureMetadata;
import com.google.common.truth.IterableSubject;
import com.google.common.truth.Subject;
+import com.google.gerrit.plugins.codeowners.api.CodeOwnerConfigFileInfo;
import com.google.gerrit.plugins.codeowners.api.CodeOwnerInfo;
import com.google.gerrit.plugins.codeowners.api.CodeOwnersInfo;
import com.google.gerrit.truth.ListSubject;
@@ -65,6 +67,13 @@
}
}
+ public ListSubject<CodeOwnerConfigFileInfoSubject, CodeOwnerConfigFileInfo>
+ hasCodeOwnerConfigsThat() {
+ return check("codeOwnerConfigs()")
+ .about(elements())
+ .thatCustom(codeOwnersInfo().codeOwnerConfigs, codeOwnerConfigFileInfos());
+ }
+
public IterableSubject hasDebugLogsThat() {
return check("debugLogs").that(codeOwnersInfo().debugLogs);
}
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/AbstractGetCodeOwnersForPathIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/AbstractGetCodeOwnersForPathIT.java
index 74c9eb2..cd6f9a2 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/AbstractGetCodeOwnersForPathIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/AbstractGetCodeOwnersForPathIT.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerConfigFileInfoSubject.assertThat;
import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerInfoSubject.hasAccountId;
import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerInfoSubject.hasAccountName;
import static com.google.gerrit.plugins.codeowners.testing.CodeOwnersInfoSubject.assertThat;
@@ -25,6 +26,7 @@
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.config.GerritConfig;
import com.google.gerrit.acceptance.testsuite.account.AccountOperations;
@@ -44,13 +46,16 @@
import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersIT;
import com.google.gerrit.plugins.codeowners.acceptance.testsuite.TestCodeOwnerConfigCreation;
import com.google.gerrit.plugins.codeowners.acceptance.testsuite.TestPathExpressions;
+import com.google.gerrit.plugins.codeowners.api.CodeOwnerConfigFileInfo;
import com.google.gerrit.plugins.codeowners.api.CodeOwners;
import com.google.gerrit.plugins.codeowners.api.CodeOwnersInfo;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerBackend;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImportMode;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigReference;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerResolver;
import com.google.gerrit.plugins.codeowners.backend.CodeOwnerSet;
+import com.google.gerrit.plugins.codeowners.backend.config.BackendConfig;
import com.google.gerrit.plugins.codeowners.restapi.CheckCodeOwnerCapability;
import com.google.gerrit.plugins.codeowners.restapi.GetCodeOwnersForPathInBranch;
import com.google.inject.Inject;
@@ -77,11 +82,14 @@
@Inject private GroupOperations groupOperations;
@Inject private ProjectOperations projectOperations;
+ private CodeOwnerBackend backend;
+
protected TestPathExpressions testPathExpressions;
@Before
public void setup() throws Exception {
testPathExpressions = plugin.getSysInjector().getInstance(TestPathExpressions.class);
+ backend = plugin.getSysInjector().getInstance(BackendConfig.class).getDefaultBackend();
}
/** Must return the {@link CodeOwners} API against which the tests should be run. */
@@ -98,7 +106,10 @@
@Test
public void getCodeOwnersWhenNoCodeOwnerConfigsExist() throws Exception {
- assertThat(queryCodeOwners("/foo/bar/baz.md")).hasCodeOwnersThat().isEmpty();
+ CodeOwnersInfo codeOwnersInfo = queryCodeOwners("/foo/bar/baz.md");
+ assertThat(codeOwnersInfo).hasCodeOwnersThat().isEmpty();
+ assertThat(codeOwnersInfo).hasOwnedByAllUsersThat().isNull();
+ assertThat(codeOwnersInfo).hasCodeOwnerConfigsThat().isEmpty();
}
@Test
@@ -115,6 +126,7 @@
CodeOwnersInfo codeOwnersInfo = queryCodeOwners("/foo/bar/baz.md");
assertThat(codeOwnersInfo).hasCodeOwnersThat().isEmpty();
assertThat(codeOwnersInfo).hasOwnedByAllUsersThat().isNull();
+ assertThat(codeOwnersInfo).hasCodeOwnerConfigsThat().isEmpty();
}
@Test
@@ -136,29 +148,32 @@
private void testGetCodeOwners(boolean useAbsolutePath) throws Exception {
TestAccount user2 = accountCreator.user2();
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/")
- .addCodeOwnerEmail(admin.email())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/foo/")
- .addCodeOwnerEmail(user.email())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/")
+ .addCodeOwnerEmail(user.email())
+ .create();
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/foo/bar/")
- .addCodeOwnerEmail(user2.email())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey3 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addCodeOwnerEmail(user2.email())
+ .create();
CodeOwnersInfo codeOwnersInfo =
queryCodeOwners(useAbsolutePath ? "/foo/bar/baz.md" : "foo/bar/baz.md");
@@ -172,6 +187,27 @@
.comparingElementsUsing(hasAccountName())
.containsExactly(null, null, null);
assertThat(codeOwnersInfo).hasOwnedByAllUsersThat().isNull();
+
+ assertThat(codeOwnersInfo.codeOwnerConfigs).hasSize(3);
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo3 = codeOwnersInfo.codeOwnerConfigs.get(0);
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo2 = codeOwnersInfo.codeOwnerConfigs.get(1);
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo1 = codeOwnersInfo.codeOwnerConfigs.get(2);
+ assertThat(codeOwnerConfigFileInfo3)
+ .assertKey(backend, codeOwnerConfigKey3)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+ assertThat(codeOwnerConfigFileInfo2)
+ .assertKey(backend, codeOwnerConfigKey2)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+ assertThat(codeOwnerConfigFileInfo1)
+ .assertKey(backend, codeOwnerConfigKey1)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+
assertThat(codeOwnersInfo).hasDebugLogsThat().isNull();
}
@@ -184,24 +220,26 @@
// 1. code owner config that makes "user2" a code owner, inheriting code owners from parent code
// owner configs is enabled by default
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/foo/bar/")
- .addCodeOwnerEmail(user2.email())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addCodeOwnerEmail(user2.email())
+ .create();
// 2. code owner config that makes "user" a code owner, code owners from parent code owner
// configs are ignored
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/foo/")
- .ignoreParentCodeOwners()
- .addCodeOwnerEmail(user.email())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/")
+ .ignoreParentCodeOwners()
+ .addCodeOwnerEmail(user.email())
+ .create();
// 3. code owner config that makes "admin" a code owner and assigns code ownership to all users,
// but for this test this code owner config is ignored, since the 2. code owner config ignores
@@ -227,36 +265,71 @@
.containsExactly(user2.id(), user.id())
.inOrder();
assertThat(codeOwnersInfo).hasOwnedByAllUsersThat().isNull();
+
+ assertThat(codeOwnersInfo.codeOwnerConfigs).hasSize(2);
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo1 = codeOwnersInfo.codeOwnerConfigs.get(0);
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo2 = codeOwnersInfo.codeOwnerConfigs.get(1);
+ assertThat(codeOwnerConfigFileInfo1)
+ .assertKey(backend, codeOwnerConfigKey1)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+ assertThat(codeOwnerConfigFileInfo2)
+ .assertKey(backend, codeOwnerConfigKey2)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
}
@Test
public void getPerFileCodeOwners() throws Exception {
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/foo/bar/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression(testPathExpressions.matchFileType("txt"))
- .addCodeOwnerEmail(admin.email())
- .build())
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression(testPathExpressions.matchFileType("md"))
- .addCodeOwnerEmail(user.email())
- .build())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression(testPathExpressions.matchFileType("txt"))
+ .addCodeOwnerEmail(admin.email())
+ .build())
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression(testPathExpressions.matchFileType("md"))
+ .addCodeOwnerEmail(user.email())
+ .build())
+ .create();
- assertThat(queryCodeOwners("/foo/bar/config.txt"))
+ CodeOwnersInfo codeOwnersInfo = queryCodeOwners("/foo/bar/config.txt");
+ assertThat(codeOwnersInfo)
.hasCodeOwnersThat()
.comparingElementsUsing(hasAccountId())
.containsExactly(admin.id());
- assertThat(queryCodeOwners("/foo/bar/baz.md"))
+ assertThat(Iterables.getOnlyElement(codeOwnersInfo.codeOwnerConfigs))
+ .assertKey(backend, codeOwnerConfigKey)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+
+ codeOwnersInfo = queryCodeOwners("/foo/bar/baz.md");
+ assertThat(codeOwnersInfo)
.hasCodeOwnersThat()
.comparingElementsUsing(hasAccountId())
.containsExactly(user.id());
- assertThat(queryCodeOwners("/foo/bar/main.config")).hasCodeOwnersThat().isEmpty();
+ assertThat(Iterables.getOnlyElement(codeOwnersInfo.codeOwnerConfigs))
+ .assertKey(backend, codeOwnerConfigKey)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+
+ codeOwnersInfo = queryCodeOwners("/foo/bar/main.config");
+ assertThat(codeOwnersInfo).hasCodeOwnersThat().isEmpty();
+ assertThat(Iterables.getOnlyElement(codeOwnersInfo.codeOwnerConfigs))
+ .assertKey(backend, codeOwnerConfigKey)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
}
@Test
@@ -548,22 +621,24 @@
TestAccount user2 = accountCreator.user2();
// create some code owner configs
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/")
- .addCodeOwnerEmail(admin.email())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/foo/bar/")
- .addCodeOwnerEmail(user.email())
- .addCodeOwnerEmail(user2.email())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addCodeOwnerEmail(user.email())
+ .addCodeOwnerEmail(user2.email())
+ .create();
// get code owners with different limits
CodeOwnersInfo codeOwnersInfo =
@@ -576,18 +651,51 @@
.element(0)
.hasAccountIdThat()
.isAnyOf(user.id(), user2.id());
+ assertThat(codeOwnersInfo.codeOwnerConfigs).hasSize(2);
+ assertThat(codeOwnersInfo.codeOwnerConfigs.get(0))
+ .assertKey(backend, codeOwnerConfigKey2)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+ assertThat(codeOwnersInfo.codeOwnerConfigs.get(1))
+ .assertKey(backend, codeOwnerConfigKey1)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
codeOwnersInfo = queryCodeOwners(getCodeOwnersApi().query().withLimit(2), "/foo/bar/baz.md");
assertThat(codeOwnersInfo)
.hasCodeOwnersThat()
.comparingElementsUsing(hasAccountId())
.containsExactly(user.id(), user2.id());
+ assertThat(codeOwnersInfo.codeOwnerConfigs).hasSize(2);
+ assertThat(codeOwnersInfo.codeOwnerConfigs.get(0))
+ .assertKey(backend, codeOwnerConfigKey2)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+ assertThat(codeOwnersInfo.codeOwnerConfigs.get(1))
+ .assertKey(backend, codeOwnerConfigKey1)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
codeOwnersInfo = getCodeOwnersApi().query().withLimit(3).get("/foo/bar/baz.md");
assertThat(codeOwnersInfo)
.hasCodeOwnersThat()
.comparingElementsUsing(hasAccountId())
.containsExactly(admin.id(), user.id(), user2.id());
+ assertThat(codeOwnersInfo.codeOwnerConfigs).hasSize(2);
+ assertThat(codeOwnersInfo.codeOwnerConfigs.get(0))
+ .assertKey(backend, codeOwnerConfigKey2)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
+ assertThat(codeOwnersInfo.codeOwnerConfigs.get(1))
+ .assertKey(backend, codeOwnerConfigKey1)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
}
@Test
@@ -632,10 +740,12 @@
public void getGlobalCodeOwners() throws Exception {
TestAccount globalOwner =
accountCreator.create("global_owner", "global.owner@example.com", "Global Owner", null);
- assertThat(queryCodeOwners("/foo/bar/baz.md"))
+ CodeOwnersInfo codeOwnersInfo = queryCodeOwners("/foo/bar/baz.md");
+ assertThat(codeOwnersInfo)
.hasCodeOwnersThat()
.comparingElementsUsing(hasAccountId())
.containsExactly(globalOwner.id());
+ assertThat(codeOwnersInfo).hasCodeOwnerConfigsThat().isEmpty();
}
@Test
@@ -767,18 +877,25 @@
@Test
public void getDefaultCodeOwners() throws Exception {
// Create default code owner config file in refs/meta/config.
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch(RefNames.REFS_CONFIG)
- .folderPath("/")
- .addCodeOwnerEmail(user.email())
- .create();
+ CodeOwnerConfig.Key codeOwnerConfigKey =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch(RefNames.REFS_CONFIG)
+ .folderPath("/")
+ .addCodeOwnerEmail(user.email())
+ .create();
- assertThat(queryCodeOwners("/foo/bar/baz.md"))
+ CodeOwnersInfo codeOwnersInfo = queryCodeOwners("/foo/bar/baz.md");
+ assertThat(codeOwnersInfo)
.hasCodeOwnersThat()
.comparingElementsUsing(hasAccountId())
.containsExactly(user.id());
+ assertThat(Iterables.getOnlyElement(codeOwnersInfo.codeOwnerConfigs))
+ .assertKey(backend, codeOwnerConfigKey)
+ .assertNoImports()
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage();
}
@Test
@@ -1643,4 +1760,168 @@
}
assertThat(foundDifferentOrder).isTrue();
}
+
+ @Test
+ public void getCodeOwnersWithUnresolvedImport() throws Exception {
+ skipTestIfImportsNotSupportedByCodeOwnersBackend();
+
+ Project.NameKey nonExistingProject = Project.nameKey("non-existing");
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ CodeOwnerConfig.Key.create(nonExistingProject, "master", "/", "OWNERS");
+ CodeOwnerConfigReference nonResolvableCodeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
+ CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addImport(nonResolvableCodeOwnerConfigReference)
+ .addCodeOwnerEmail(admin.email())
+ .addCodeOwnerEmail(user.email())
+ .create();
+
+ String path = "/foo/bar/baz.md";
+ CodeOwnersInfo codeOwnersInfo = queryCodeOwners(getCodeOwnersApi().query(), path);
+ assertThat(codeOwnersInfo)
+ .hasCodeOwnersThat()
+ .comparingElementsUsing(hasAccountId())
+ .containsExactly(user.id(), admin.id());
+ assertThat(Iterables.getOnlyElement(codeOwnersInfo.codeOwnerConfigs))
+ .assertKey(backend, keyOfImportingCodeOwnerConfig)
+ .assertNoResolvedImports()
+ .assertUnresolvedImport(
+ backend,
+ keyOfImportedCodeOwnerConfig,
+ nonResolvableCodeOwnerConfigReference.importMode(),
+ String.format("project %s not found", nonExistingProject));
+ }
+
+ @Test
+ public void getCodeOwnersWithResolvedImport() throws Exception {
+ skipTestIfImportsNotSupportedByCodeOwnersBackend();
+
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/baz/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
+ CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addImport(codeOwnerConfigReference)
+ .addCodeOwnerEmail(user.email())
+ .create();
+
+ String path = "/foo/bar/baz.md";
+ CodeOwnersInfo codeOwnersInfo = queryCodeOwners(getCodeOwnersApi().query(), path);
+ assertThat(codeOwnersInfo)
+ .hasCodeOwnersThat()
+ .comparingElementsUsing(hasAccountId())
+ .containsExactly(user.id(), admin.id());
+ assertThat(Iterables.getOnlyElement(codeOwnersInfo.codeOwnerConfigs))
+ .assertKey(backend, keyOfImportingCodeOwnerConfig)
+ .assertNoUnresolvedImports()
+ .assertResolvedImport(
+ backend, keyOfImportedCodeOwnerConfig, codeOwnerConfigReference.importMode());
+ }
+
+ @Test
+ public void getCodeOwnersWithNestedImport() throws Exception {
+ skipTestIfImportsNotSupportedByCodeOwnersBackend();
+
+ TestAccount user2 = accountCreator.user2();
+
+ Project.NameKey nonExistingProject = Project.nameKey("non-existing");
+ CodeOwnerConfig.Key keyOfUnresolvableCodeOwnerConfig =
+ CodeOwnerConfig.Key.create(nonExistingProject, "master", "/", "OWNERS");
+ CodeOwnerConfigReference unresolvableCodeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY,
+ keyOfUnresolvableCodeOwnerConfig);
+
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .addImport(unresolvableCodeOwnerConfigReference)
+ .addCodeOwnerEmail(admin.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference2 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig2);
+
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/baz/")
+ .addImport(codeOwnerConfigReference2)
+ .addCodeOwnerEmail(user2.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig1);
+
+ CodeOwnerConfig.Key keyOfImportingCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addImport(codeOwnerConfigReference1)
+ .addCodeOwnerEmail(user.email())
+ .create();
+
+ String path = "/foo/bar/baz.md";
+ CodeOwnersInfo codeOwnersInfo = queryCodeOwners(getCodeOwnersApi().query(), path);
+ assertThat(codeOwnersInfo)
+ .hasCodeOwnersThat()
+ .comparingElementsUsing(hasAccountId())
+ .containsExactly(user.id(), user2.id(), admin.id());
+
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo =
+ Iterables.getOnlyElement(codeOwnersInfo.codeOwnerConfigs);
+ assertThat(codeOwnerConfigFileInfo)
+ .assertKey(backend, keyOfImportingCodeOwnerConfig)
+ .assertNoImportMode()
+ .assertNoUnresolvedErrorMessage()
+ .assertNoUnresolvedImports()
+ .assertResolvedImport(
+ backend, keyOfImportedCodeOwnerConfig1, codeOwnerConfigReference1.importMode());
+
+ codeOwnerConfigFileInfo = Iterables.getOnlyElement(codeOwnerConfigFileInfo.imports);
+ assertThat(codeOwnerConfigFileInfo)
+ .assertKey(backend, keyOfImportedCodeOwnerConfig1)
+ .assertImportMode(codeOwnerConfigReference1.importMode())
+ .assertNoUnresolvedErrorMessage()
+ .assertNoUnresolvedImports()
+ .assertResolvedImport(
+ backend, keyOfImportedCodeOwnerConfig2, codeOwnerConfigReference2.importMode());
+
+ codeOwnerConfigFileInfo = Iterables.getOnlyElement(codeOwnerConfigFileInfo.imports);
+ assertThat(codeOwnerConfigFileInfo)
+ .assertKey(backend, keyOfImportedCodeOwnerConfig2)
+ .assertImportMode(codeOwnerConfigReference2.importMode())
+ .assertNoUnresolvedErrorMessage()
+ .assertNoResolvedImports()
+ .assertUnresolvedImport(
+ backend,
+ keyOfUnresolvableCodeOwnerConfig,
+ unresolvableCodeOwnerConfigReference.importMode(),
+ String.format("project %s not found", nonExistingProject));
+ }
}
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
index 83eb13f..66928ef 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CheckCodeOwnerIT.java
@@ -14,6 +14,7 @@
package com.google.gerrit.plugins.codeowners.acceptance.api;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
@@ -100,7 +101,7 @@
@Test
public void checkCodeOwnerForSymbolicRefPointingToAnUnbornBranch() throws Exception {
try (Repository repo = repoManager.openRepository(project)) {
- repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing");
+ testRefAction(() -> repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing"));
}
RestResponse response =
adminRestSession.get(
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerSubmitRuleIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerSubmitRuleIT.java
index f3e7d1b..2d2c8c7 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerSubmitRuleIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/CodeOwnerSubmitRuleIT.java
@@ -14,6 +14,7 @@
package com.google.gerrit.plugins.codeowners.acceptance.api;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowLabel;
@@ -716,7 +717,7 @@
try (Repository repo = repoManager.openRepository(project)) {
RefUpdate ru = repo.updateRef(RefNames.refsCacheAutomerge(mergeCommit.name()));
ru.setForceUpdate(true);
- assertThat(ru.delete()).isEqualTo(RefUpdate.Result.FORCED);
+ assertThat(testRefAction(() -> ru.delete())).isEqualTo(RefUpdate.Result.FORCED);
assertThat(repo.exactRef(RefNames.refsCacheAutomerge(mergeCommit.name()))).isNull();
}
}
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigForPathInBranchIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigForPathInBranchIT.java
index 5ddd85f..843b656 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigForPathInBranchIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnerConfigForPathInBranchIT.java
@@ -14,6 +14,7 @@
package com.google.gerrit.plugins.codeowners.acceptance.api;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerConfigInfoSubject.assertThatOptional;
import static com.google.gerrit.testing.GerritJUnit.assertThrows;
@@ -54,7 +55,7 @@
@GerritConfig(name = "plugin.code-owners.enableExperimentalRestEndpoints", value = "true")
public void getCodeOwnerConfigFromSymbolicRefPointingToAnUnbornBranch() throws Exception {
try (Repository repo = repoManager.openRepository(project)) {
- repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing");
+ testRefAction(() -> repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing"));
}
RestResponse response =
adminRestSession.get(
diff --git a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnersForPathInBranchIT.java b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnersForPathInBranchIT.java
index a78d420..34d2271 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnersForPathInBranchIT.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/acceptance/api/GetCodeOwnersForPathInBranchIT.java
@@ -14,6 +14,7 @@
package com.google.gerrit.plugins.codeowners.acceptance.api;
+import static com.google.gerrit.testing.TestActionRefUpdateContext.testRefAction;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.plugins.codeowners.testing.CodeOwnerInfoSubject.hasAccountId;
import static com.google.gerrit.plugins.codeowners.testing.CodeOwnersInfoSubject.assertThat;
@@ -73,7 +74,7 @@
@Test
public void getCodeOwnersFromSymbolicRefPointingToAnUnbornBranch() throws Exception {
try (Repository repo = repoManager.openRepository(project)) {
- repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing");
+ testRefAction(() -> repo.updateRef(Constants.HEAD, true).link("refs/heads/non-existing"));
}
RestResponse response =
adminRestSession.get(
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigImportTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigImportTest.java
new file mode 100644
index 0000000..b6efbc6
--- /dev/null
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerConfigImportTest.java
@@ -0,0 +1,41 @@
+// Copyright (C) 2021 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;
+
+import org.junit.Test;
+
+/** Tests for {@link CodeOwnerConfigImport}. */
+public class CodeOwnerConfigImportTest extends AbstractAutoValueTest {
+ @Test
+ public void toStringIncludesAllData_resolvedImport() throws Exception {
+ CodeOwnerConfigImport resolvedImport =
+ CodeOwnerConfigImport.createResolvedImport(
+ CodeOwnerConfig.Key.create(project, "master", "/"),
+ CodeOwnerConfig.Key.create(project, "master", "/bar/"),
+ CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"));
+ assertThatToStringIncludesAllData(resolvedImport, CodeOwnerConfigImport.class);
+ }
+
+ @Test
+ public void toStringIncludesAllData_unresolvedImport() throws Exception {
+ CodeOwnerConfigImport unresolvedImport =
+ CodeOwnerConfigImport.createUnresolvedImport(
+ CodeOwnerConfig.Key.create(project, "master", "/"),
+ CodeOwnerConfig.Key.create(project, "master", "/bar/"),
+ CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"),
+ "test message");
+ assertThatToStringIncludesAllData(unresolvedImport, CodeOwnerConfigImport.class);
+ }
+}
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverResultTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverResultTest.java
index 18ea017..deab817 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverResultTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/CodeOwnerResolverResultTest.java
@@ -29,7 +29,17 @@
/* annotations= */ ImmutableMultimap.of(),
/* ownedByAllUsers= */ false,
/* hasUnresolvedCodeOwners= */ false,
- /* hasUnresolvedImports= */ false,
+ ImmutableList.of(
+ CodeOwnerConfigImport.createResolvedImport(
+ CodeOwnerConfig.Key.create(project, "master", "/"),
+ CodeOwnerConfig.Key.create(project, "master", "/bar/"),
+ CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"))),
+ ImmutableList.of(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ CodeOwnerConfig.Key.create(project, "master", "/"),
+ CodeOwnerConfig.Key.create(project, "master", "/bar/"),
+ CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"),
+ "test message")),
ImmutableList.of("test message"));
assertThatToStringIncludesAllData(codeOwnerResolverResult, CodeOwnerResolverResult.class);
}
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResultTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResultTest.java
index d270fdc..d9b3a71 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResultTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersResultTest.java
@@ -27,19 +27,27 @@
@Test
public void toStringIncludesAllData() throws Exception {
CodeOwnerConfig.Key codeOwnerConfigKey = CodeOwnerConfig.Key.create(project, "master", "/");
- CodeOwnerConfigReference codeOwnerConfigReference =
+ CodeOwnerConfigReference resolvableCodeOwnerConfigReference =
CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS");
+ CodeOwnerConfigReference unresolvableCodeOwnerConfigReference =
+ CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/baz/OWNERS");
PathCodeOwnersResult pathCodeOwnersResult =
PathCodeOwnersResult.create(
Paths.get("/foo/bar/baz.md"),
CodeOwnerConfig.builder(codeOwnerConfigKey, TEST_REVISION)
- .addImport(codeOwnerConfigReference)
+ .addImport(resolvableCodeOwnerConfigReference)
+ .addImport(unresolvableCodeOwnerConfigReference)
.build(),
ImmutableList.of(
- UnresolvedImport.create(
+ CodeOwnerConfigImport.createResolvedImport(
codeOwnerConfigKey,
CodeOwnerConfig.Key.create(project, "master", "/bar/"),
- codeOwnerConfigReference,
+ resolvableCodeOwnerConfigReference)),
+ ImmutableList.of(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ codeOwnerConfigKey,
+ CodeOwnerConfig.Key.create(project, "master", "/baz/"),
+ unresolvableCodeOwnerConfigReference,
"test message")));
assertThatToStringIncludesAllData(pathCodeOwnersResult, PathCodeOwnersResult.class);
}
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersTest.java
index 9563ade..04df442 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/PathCodeOwnersTest.java
@@ -239,7 +239,8 @@
pathCodeOwnersFactory.createWithoutCache(
emptyCodeOwnerConfig, Paths.get("/foo/bar/baz.md"));
assertThat(pathCodeOwners.resolveCodeOwnerConfig().get().getPathCodeOwners()).isEmpty();
- assertThat(pathCodeOwners.resolveCodeOwnerConfig().get().hasUnresolvedImports()).isFalse();
+ assertThat(pathCodeOwners.resolveCodeOwnerConfig().get().resolvedImports()).isEmpty();
+ assertThat(pathCodeOwners.resolveCodeOwnerConfig().get().unresolvedImports()).isEmpty();
}
@Test
@@ -471,6 +472,11 @@
@Test
public void nonResolveableImportIsIgnored() throws Exception {
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ CodeOwnerConfig.Key.create(project, "master", "/non-existing/", "OWNERS");
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with non-resolveable import
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
@@ -478,9 +484,7 @@
.project(project)
.branch("master")
.folderPath("/")
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.ALL, "/non-existing/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.addCodeOwnerSet(CodeOwnerSet.builder().addCodeOwnerEmail(admin.email()).build())
.create();
@@ -494,24 +498,40 @@
// Expectation: we get the global code owner from the importing code owner config, the
// non-resolveable import is silently ignored
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports()).isEmpty();
+ assertThat(pathCodeOwnersResult.unresolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ String.format(
+ "code owner config does not exist (revision = %s)",
+ projectOperations
+ .project(keyOfImportedCodeOwnerConfig.project())
+ .getHead(keyOfImportedCodeOwnerConfig.branchNameKey().branch())
+ .name())));
}
@Test
public void importOfNonCodeOwnerConfigFileIsIgnored() throws Exception {
// create a file that looks like a code owner config file, but which has a name that is not
// allowed as code owner config file
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/")
- .fileName("FOO")
- .addCodeOwnerEmail(user.email())
- .create();
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/")
+ .fileName("FOO")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
// create config with import of non code owner config file
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
@@ -520,7 +540,7 @@
.project(project)
.branch("master")
.folderPath("/")
- .addImport(CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/FOO"))
+ .addImport(codeOwnerConfigReference)
.addCodeOwnerSet(CodeOwnerSet.builder().addCodeOwnerEmail(admin.email()).build())
.create();
@@ -538,7 +558,19 @@
assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwnersResult.hasUnresolvedImports()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports()).isEmpty();
+ assertThat(pathCodeOwnersResult.unresolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ String.format(
+ "code owner config does not exist (revision = %s)",
+ projectOperations
+ .project(keyOfImportedCodeOwnerConfig.project())
+ .getHead(keyOfImportedCodeOwnerConfig.branchNameKey().branch())
+ .name())));
}
@Test
@@ -548,14 +580,17 @@
// parameter fileExtension) or file extensions are enabled for code owner config files (config
// paramater enableCodeOwnerConfigFilesWithFileExtensions). Both is not the case here, hence any
// import of this file in another code owner config file should get ignored.
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/")
- .fileName("OWNERS.foo")
- .addCodeOwnerEmail(user.email())
- .create();
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/")
+ .fileName("OWNERS.foo")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
// create the importing config
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
@@ -564,8 +599,7 @@
.project(project)
.branch("master")
.folderPath("/")
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/OWNERS.FOO"))
+ .addImport(codeOwnerConfigReference)
.addCodeOwnerSet(CodeOwnerSet.builder().addCodeOwnerEmail(admin.email()).build())
.create();
@@ -584,7 +618,19 @@
assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwnersResult.hasUnresolvedImports()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports()).isEmpty();
+ assertThat(pathCodeOwnersResult.unresolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ String.format(
+ "code owner config does not exist (revision = %s)",
+ projectOperations
+ .project(keyOfImportedCodeOwnerConfig.project())
+ .getHead(keyOfImportedCodeOwnerConfig.branchNameKey().branch())
+ .name())));
}
@Test
@@ -595,14 +641,17 @@
// Create a code owner config file with a file extension. This file is considered as a code
// owner config file since file extensions for code owner config files are enabled (paramater
// enableCodeOwnerConfigFilesWithFileExtensions).
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/")
- .fileName("OWNERS.FOO")
- .addCodeOwnerEmail(user.email())
- .create();
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/")
+ .fileName("OWNERS.FOO")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
// create the importing config
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
@@ -611,8 +660,7 @@
.project(project)
.branch("master")
.folderPath("/")
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/OWNERS.FOO"))
+ .addImport(codeOwnerConfigReference)
.addCodeOwnerSet(CodeOwnerSet.builder().addCodeOwnerEmail(admin.email()).build())
.create();
@@ -630,7 +678,13 @@
assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwnersResult.hasUnresolvedImports()).isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -644,6 +698,19 @@
}
private void testImportGlobalCodeOwners(CodeOwnerConfigImportMode importMode) throws Exception {
+ // create imported config with global code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(importMode, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
@@ -652,18 +719,9 @@
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(CodeOwnerConfigReference.create(importMode, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with global code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
@@ -674,17 +732,40 @@
// Expectation: we get the global code owners from the importing and the imported code owner
// config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void importPerFileCodeOwners_importModeAll() throws Exception {
+ // create imported config with matching per-file code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("*.md")
+ .addCodeOwnerEmail(user.email())
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with matching per-file code owner and import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
@@ -695,44 +776,53 @@
.addPathExpression("*.md")
.addCodeOwnerEmail(admin.email())
.build())
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with matching per-file code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression("*.md")
- .addCodeOwnerEmail(user.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the matching per-file code owners from the importing and the imported
// code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void nonMatchingPerFileCodeOwnersAreNotImported_importModeAll() throws Exception {
+ // create imported config with non-matching per-file code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("*.txt")
+ .addCodeOwnerEmail(user.email())
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with matching per-file code owner and import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
@@ -743,27 +833,13 @@
.addPathExpression("*.md")
.addCodeOwnerEmail(admin.email())
.build())
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with non-matching per-file code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression("*.txt")
- .addCodeOwnerEmail(user.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
@@ -771,17 +847,41 @@
// Expectation: we only get the matching per-file code owners from the importing code owner
// config, the per-file code owners from the imported code owner config are not relevant since
// they do not match
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void perFileCodeOwnersAreNotImported_importModeGlobalCodeOwnerSetsOnly() throws Exception {
+ // create imported config with matching per-file code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("*.md")
+ .addCodeOwnerEmail(user.email())
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
// create importing config with matching per-file code owner and import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
@@ -792,28 +892,13 @@
.addPathExpression("*.md")
.addCodeOwnerEmail(admin.email())
.build())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with matching per-file code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression("*.md")
- .addCodeOwnerEmail(user.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
@@ -821,48 +906,57 @@
// Expectation: we only get the matching per-file code owners from the importing code owner
// config, the matching per-file code owners from the imported code owner config are not
// relevant with import mode GLOBAL_CODE_OWNER_SETS_ONLY
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void
importIgnoreGlobalAndParentCodeOwnersFlagFromMatchingPerFileCodeOwnerSet_importModeAll()
throws Exception {
+ // create imported config with matching per-file code owner that has the
+ // ignoreGlobalAndParentCodeOwners flag set to true
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .setIgnoreGlobalAndParentCodeOwners()
+ .addPathExpression("*.md")
+ .addCodeOwnerEmail(user.email())
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with matching per-file code owner that has the
- // ignoreGlobalAndParentCodeOwners flag set to true
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .setIgnoreGlobalAndParentCodeOwners()
- .addPathExpression("*.md")
- .addCodeOwnerEmail(user.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
@@ -872,50 +966,58 @@
// the matching per-file code owner set in the imported code owner config has the
// ignoreGlobalAndParentCodeOwners flag set to true which causes global code owners to be
// ignored, in addition this flag causes parent code owners to be ignored
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().ignoreParentCodeOwners())
- .isTrue();
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.ignoreParentCodeOwners()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void
ignoreGlobalAndParentCodeOwnersFlagIsNotImportedFromNonMatchingPerFileCodeOwnerSet_importModeAll()
throws Exception {
+ // create imported config with non-matching per-file code owner that has the
+ // ignoreGlobalAndParentCodeOwners flag set to true
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .setIgnoreGlobalAndParentCodeOwners()
+ .addPathExpression("*.txt")
+ .addCodeOwnerEmail(user.email())
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with non-matching per-file code owner that has the
- // ignoreGlobalAndParentCodeOwners flag set to true
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .setIgnoreGlobalAndParentCodeOwners()
- .addPathExpression("*.txt")
- .addCodeOwnerEmail(user.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
@@ -924,50 +1026,58 @@
// per-file code owners from the imported code owner config and its
// ignoreGlobalAndParentCodeOwners flag are not relevant since the per-file code owner set does
// not match
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().ignoreParentCodeOwners())
- .isFalse();
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.ignoreParentCodeOwners()).isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void ignoreGlobalAndParentCodeOwnersFlagIsNotImported_importModeGlobalCodeOwnerSetsOnly()
throws Exception {
+ // create imported config with matching per-file code owner that has the
+ // ignoreGlobalAndParentCodeOwners flag set to true
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .setIgnoreGlobalAndParentCodeOwners()
+ .addPathExpression("*.md")
+ .addCodeOwnerEmail(user.email())
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with matching per-file code owner that has the
- // ignoreGlobalAndParentCodeOwners flag set to true
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .setIgnoreGlobalAndParentCodeOwners()
- .addPathExpression("*.md")
- .addCodeOwnerEmail(user.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
@@ -976,89 +1086,114 @@
// matching per-file code owners from the imported code owner config and its
// ignoreGlobalAndParentCodeOwners flag are not relevant with import mode
// GLOBAL_CODE_OWNER_SETS_ONLY
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().ignoreParentCodeOwners())
- .isFalse();
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.ignoreParentCodeOwners()).isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void importIgnoreParentCodeOwnersFlag_importModeAll() throws Exception {
+ // create imported config with the ignoreParentCodeOnwers flag set to true
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .ignoreParentCodeOwners()
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with the ignoreParentCodeOnwers flag set to true
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .ignoreParentCodeOwners()
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: ignoreParentCodeOwners is true because the ignoreParentCodeOwners flag in the
// imported code owner config is set to true
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().ignoreParentCodeOwners())
- .isTrue();
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.ignoreParentCodeOwners()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void ignoreParentCodeOwnersFlagNotImported_importModeGlobalCodeOwnerSetsOnly()
throws Exception {
+ // create imported config with the ignoreParentCodeOnwers flag set to true
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .ignoreParentCodeOwners()
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
// create importing config
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with the ignoreParentCodeOnwers flag set to true
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .ignoreParentCodeOwners()
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: ignoreParentCodeOwners is false because the ignoreParentCodeOwners flag in the
// imported code owner config is not relevant with import mode GLOBAL_CODE_OWNER_SETS_ONLY
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().ignoreParentCodeOwners())
- .isFalse();
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.ignoreParentCodeOwners()).isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -1077,51 +1212,69 @@
throws Exception {
TestAccount user2 = accountCreator.user2();
+ // create config with global code owner that is imported by the imported config
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/baz/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user2.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference2 =
+ createCodeOwnerConfigReference(importMode, keyOfImportedCodeOwnerConfig2);
+
+ // create imported config with global code owner and import
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .addImport(codeOwnerConfigReference2)
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(importMode, keyOfImportedCodeOwnerConfig1);
+
// create importing config with global code owner and import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(CodeOwnerConfigReference.create(importMode, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference1)
.create();
- // create imported config with global code owner and import
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .addImport(CodeOwnerConfigReference.create(importMode, "/baz/OWNERS"))
- .create();
-
- // create config with global code owner that is imported by the imported config
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/baz/")
- .addCodeOwnerEmail(user2.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing code owner config, the imported code
// owner config and the code owner config that is imported by the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email(), user2.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig1,
+ keyOfImportedCodeOwnerConfig2,
+ codeOwnerConfigReference2));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -1130,47 +1283,54 @@
throws Exception {
TestAccount user2 = accountCreator.user2();
+ // create config with per file code owner that is imported by the imported config
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/baz/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("foo.md")
+ .addCodeOwnerEmail(user2.email())
+ .build())
+ .create();
+
+ // create imported config with global code owner and import with import mode ALL
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .addImport(
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig2))
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig1);
+
// create importing config with global code owner and import with import mode
// GLOBAL_CODE_OWNER_SETS_ONLY
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference1)
.create();
- // create imported config with global code owner and import with import mode ALL
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .addImport(CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/baz/OWNERS"))
- .create();
-
- // create config with per file code owner that is imported by the imported config
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/baz/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression("foo.md")
- .addCodeOwnerEmail(user2.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
@@ -1178,11 +1338,23 @@
// Expectation: we get the global owners from the importing code owner config and the imported
// code owner config but not the per file code owner from the code owner config that is imported
// by the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig1,
+ keyOfImportedCodeOwnerConfig2,
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY,
+ keyOfImportedCodeOwnerConfig2)));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -1197,6 +1369,21 @@
private void testImportCodeOwnerConfigWithNameExtension(String nameOfImportedCodeOwnerConfig)
throws Exception {
+ // create imported config with global code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .fileName(nameOfImportedCodeOwnerConfig)
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
@@ -1205,22 +1392,9 @@
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY,
- "/bar/" + nameOfImportedCodeOwnerConfig))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with global code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .fileName(nameOfImportedCodeOwnerConfig)
- .addCodeOwnerEmail(user.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
@@ -1231,47 +1405,74 @@
// Expectation: we get the global code owners from the importing and the imported code owner
// config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void cyclicImports() throws Exception {
+ CodeOwnerConfigReference codeOwnerConfigReference2 =
+ CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/OWNERS");
+
+ // create imported config with global code owner and that imports the importing config
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .addImport(codeOwnerConfigReference2)
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
+ .fileName("OWNERS")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference1)
.create();
- // create imported config with global code owner and that imports the importing config
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .addImport(CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/OWNERS"))
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing and the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig,
+ importingCodeOwnerConfigKey,
+ codeOwnerConfigReference2));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -1327,72 +1528,91 @@
@Test
public void importWithRelativePath() throws Exception {
+ // create imported config with global code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/baz/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import with relative path
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/foo/bar/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "../baz/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with global code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/foo/baz/")
- .addCodeOwnerEmail(user.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo/bar/baz.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing and the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void importFromNonExistingProjectIsIgnored() throws Exception {
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ CodeOwnerConfig.Key.create(Project.nameKey("non-existing"), "master", "/bar/", "OWNERS");
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import from non-existing project
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS")
- .setProject(Project.nameKey("non-existing"))
- .build())
+ .addImport(codeOwnerConfigReference)
.create();
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo/bar/baz.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports()).isEmpty();
+ assertThat(pathCodeOwnersResult.unresolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ String.format("project %s not found", keyOfImportedCodeOwnerConfig.project())));
}
@Test
@@ -1402,32 +1622,33 @@
ConfigInput configInput = new ConfigInput();
configInput.state = ProjectState.HIDDEN;
gApi.projects().name(hiddenProject.get()).config(configInput);
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(hiddenProject)
- .branch("master")
- .folderPath("/")
- .addCodeOwnerEmail(user.email())
- .create();
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(hiddenProject)
+ .branch("master")
+ .folderPath("/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
// create importing config with global code owner and import from the hidden project
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(CodeOwnerConfigImportMode.ALL, "/OWNERS")
- .setProject(hiddenProject)
- .build())
+ .addImport(codeOwnerConfigReference)
.create();
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo/bar/baz.md"));
assertThat(pathCodeOwners).isPresent();
@@ -1435,85 +1656,111 @@
// Expectation: we get the global owners from the importing code owner config, the global code
// owners from the imported code owner config are ignored since the project that contains the
// code owner config is hidden
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports()).isEmpty();
+ assertThat(pathCodeOwnersResult.unresolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ String.format(
+ "state of project %s doesn't permit read",
+ keyOfImportedCodeOwnerConfig.project())));
}
@Test
public void importFromNonExistingBranchIsIgnored() throws Exception {
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ CodeOwnerConfig.Key.create(project, "non-existing", "/bar/", "OWNERS");
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import from non-existing branch
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS")
- .setProject(project)
- .setBranch("non-existing")
- .build())
+ .addImport(codeOwnerConfigReference)
.create();
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo/bar/baz.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing code owner config
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports()).isEmpty();
+ assertThat(pathCodeOwnersResult.unresolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ "code owner config does not exist (revision = current)"));
}
@Test
public void importFromOtherProject() throws Exception {
Project.NameKey otherProject = projectOperations.newProject().create();
+ // create imported config with global code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(otherProject)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import with relative path
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS")
- .setProject(otherProject)
- .build())
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with global code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(otherProject)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo/bar/baz.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing and the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -1527,43 +1774,52 @@
// Create other branches in other project.
createBranch(BranchNameKey.create(otherProject, branchName));
+ // create imported config with global code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(otherProject)
+ .branch(branchName)
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ CodeOwnerConfigReference.builder(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS")
+ .setProject(otherProject)
+ .build();
+
// create importing config with global code owner and import with relative path
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch(branchName)
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS")
- .setProject(otherProject)
- .build())
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with global code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(otherProject)
- .branch(branchName)
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead(branchName),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing and the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -1572,44 +1828,50 @@
String otherBranch = "foo";
createBranch(BranchNameKey.create(project, otherBranch));
+ // create imported config with global code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch(otherBranch)
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import with relative path
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS")
- .setProject(project)
- .setBranch(otherBranch)
- .build())
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with global code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch(otherBranch)
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo/bar/baz.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing and the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -1620,48 +1882,60 @@
String otherBranch = "foo";
createBranch(BranchNameKey.create(otherProject, otherBranch));
+ // create imported config with global code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(otherProject)
+ .branch(otherBranch)
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config with global code owner and import with relative path
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS")
- .setProject(otherProject)
- .setBranch(otherBranch)
- .build())
+ .addImport(codeOwnerConfigReference)
.create();
- // create imported config with global code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(otherProject)
- .branch(otherBranch)
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo/bar/baz.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing and the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void nonResolveablePerFileImportIsIgnored() throws Exception {
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ CodeOwnerConfig.Key.create(project, "master", "/non-existing/", "OWNERS");
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
// create importing config with non-resolveable per file import
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
@@ -1673,10 +1947,7 @@
CodeOwnerSet.builder()
.addCodeOwnerEmail(admin.email())
.addPathExpression("foo.md")
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY,
- "/non-existing/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.build())
.create();
@@ -1690,16 +1961,50 @@
// Expectation: we get the per file code owner from the importing code owner config, the
// non-resolveable per file import is silently ignored
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports()).isTrue();
+ assertThat(pathCodeOwnersResult.resolvedImports()).isEmpty();
+ assertThat(pathCodeOwnersResult.unresolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ String.format(
+ "code owner config does not exist (revision = %s)",
+ projectOperations
+ .project(keyOfImportedCodeOwnerConfig.project())
+ .getHead(keyOfImportedCodeOwnerConfig.branchNameKey().branch())
+ .name())));
}
@Test
public void perFileImport() throws Exception {
TestAccount user2 = accountCreator.user2();
+ // create imported config with ignoreParentCodeOwners = true, a global code owner and a per file
+ // code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .ignoreParentCodeOwners()
+ .addCodeOwnerEmail(user.email())
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("foo.md")
+ .addCodeOwnerEmail(user2.email())
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
// create importing config with per code owner and per file import
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
@@ -1711,28 +2016,10 @@
CodeOwnerSet.builder()
.addPathExpression("foo.md")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.build())
.create();
- // create imported config with ignoreParentCodeOwners = true, a global code owner and a per file
- // code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .ignoreParentCodeOwners()
- .addCodeOwnerEmail(user.email())
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression("foo.md")
- .addCodeOwnerEmail(user2.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
@@ -1744,23 +2031,58 @@
// Expectation: we get the per file code owners from the importing and the global code owner
// from the imported code owner config, but not the per file code owner from the imported code
// owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
// Expectation: the ignoreParentCodeOwners flag from the imported code owner config is ignored
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().ignoreParentCodeOwners())
- .isFalse();
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.ignoreParentCodeOwners()).isFalse();
+
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void importsOfPerFileImportedCodeOwnerConfigAreResolved() throws Exception {
TestAccount user2 = accountCreator.user2();
+ // create config with global code owner that is imported by the imported config
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/baz/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user2.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference2 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig2);
+
+ // create imported config with global code owner and global import
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .addImport(codeOwnerConfigReference2)
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig1);
+
// create importing config with per file code owner and per file import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
@@ -1770,56 +2092,75 @@
CodeOwnerSet.builder()
.addPathExpression("foo.md")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference1)
.build())
.create();
- // create imported config with global code owner and global import
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/baz/OWNERS"))
- .create();
-
- // create config with global code owner that is imported by the imported config
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/baz/")
- .addCodeOwnerEmail(user2.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
// Expectation: we get the global owners from the importing code owner config, the imported code
// owner config and the code owner config that is imported by the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email(), user2.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig1,
+ keyOfImportedCodeOwnerConfig2,
+ codeOwnerConfigReference2));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void onlyGlobalCodeOwnersAreImportedForTransitivePerFileImports() throws Exception {
TestAccount user2 = accountCreator.user2();
+ // create config with per file code owner that is imported by the imported config
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/baz/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("foo.md")
+ .addCodeOwnerEmail(user2.email())
+ .build())
+ .create();
+
+ // create imported config with per global owner and global import with mode ALL
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .addImport(
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig2))
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig1);
+
// create importing config with per file code owner and per file import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
@@ -1829,39 +2170,14 @@
CodeOwnerSet.builder()
.addPathExpression("foo.md")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference1)
.build())
.create();
- // create imported config with per global owner and global import with mode ALL
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .addImport(CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/baz/OWNERS"))
- .create();
-
- // create config with per file code owner that is imported by the imported config
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/baz/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression("foo.md")
- .addCodeOwnerEmail(user2.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
@@ -1869,104 +2185,156 @@
// Expectation: we get the global owners from the importing code owner config and the imported
// code owner config, but not the per file code owner from the code owner config that is
// imported by the imported code owner config
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig1,
+ keyOfImportedCodeOwnerConfig2,
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY,
+ keyOfImportedCodeOwnerConfig2)));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
public void onlyMatchingTransitivePerFileImportsAreImported() throws Exception {
TestAccount user2 = accountCreator.user2();
+ // create config with global code owner that is imported by the imported config for *.md files
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/md/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference2 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig2);
+
+ // create config with global code owner that is imported by the imported config for *.txt files
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig3 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/txt/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user2.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference3 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig3);
+
+ // create imported config with 2 per file imports, one for *.md files and one for *.txt
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("*.md")
+ .addImport(codeOwnerConfigReference2)
+ .build())
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression("*.txt")
+ .addImport(codeOwnerConfigReference3)
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig1);
+
// create importing config with global import
- CodeOwnerConfig.Key rootCodeOwnerConfigKey =
+ CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
.newCodeOwnerConfig()
.project(project)
.branch("master")
.folderPath("/")
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference1)
.create();
- // create imported config with 2 per file imports, one for *.md files and one for *.txt
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression("*.md")
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/md/OWNERS"))
- .build())
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression("*.txt")
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/txt/OWNERS"))
- .build())
- .create();
-
- // create config with global code owner that is imported by the imported config for *.md files
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/md/")
- .addCodeOwnerEmail(user.email())
- .create();
-
- // create config with global code owner that is imported by the imported config for *.txt files
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/txt/")
- .addCodeOwnerEmail(user2.email())
- .create();
-
// Expectation for foo.xyz file: code owners is empty since foo.xyz neither matches *.md nor
// *.txt
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.xyz"));
assertThat(pathCodeOwners).isPresent();
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners()).isEmpty();
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners()).isEmpty();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
// Expectation for foo.md file: code owners contains only user since foo.md only matches *.md
pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.md"));
assertThat(pathCodeOwners).isPresent();
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(user.email());
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig1,
+ keyOfImportedCodeOwnerConfig2,
+ codeOwnerConfigReference2));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
// Expectation for foo.txt file: code owners contains only user2 since foo.txt only matches
// *.txt
pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
- rootCodeOwnerConfigKey,
+ importingCodeOwnerConfigKey,
projectOperations.project(project).getHead("master"),
Paths.get("/foo.txt"));
assertThat(pathCodeOwners).isPresent();
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(user2.email());
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig1,
+ keyOfImportedCodeOwnerConfig3,
+ codeOwnerConfigReference3));
}
@Test
@@ -2090,6 +2458,20 @@
@Test
public void perFileRuleThatIgnoresGlobalCodeOwnersCanImportGlobalCodeOwnersFromOtherFile()
throws Exception {
+ // create imported config with a global code owner
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
// create importing config that:
// * has a global code owner
// * has a per-file import for md files
@@ -2105,21 +2487,10 @@
CodeOwnerSet.builder()
.addPathExpression(testPathExpressions.matchFileType("md"))
.setIgnoreGlobalAndParentCodeOwners()
- .addImport(
- CodeOwnerConfigReference.create(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.build())
.create();
- // create imported config with a global code owner
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
@@ -2131,11 +2502,17 @@
// Expectation: we get the global code owner from the imported code owner config (since it is
// imported by a matching per-file rule), the global code owner from the importing code owner
// config is ignored (since the matching per-file rule ignores parent and global code owners)
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(user.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -2146,6 +2523,24 @@
TestAccount user3 =
accountCreator.create("user3", "user3@example.com", "User3", /* displayName= */ null);
+ // create imported config that has a matching per-file rule
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user2.email())
+ .addCodeOwnerSet(
+ CodeOwnerSet.builder()
+ .addPathExpression(testPathExpressions.matchFileType("md"))
+ .addCodeOwnerEmail(user3.email())
+ .build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, keyOfImportedCodeOwnerConfig);
+
// create importing config that has a global import with mode ALL and a per-file rule for md
// files that ignores global and parent code owners
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
@@ -2155,8 +2550,7 @@
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"))
+ .addImport(codeOwnerConfigReference)
.addCodeOwnerSet(
CodeOwnerSet.builder()
.addPathExpression(testPathExpressions.matchFileType("md"))
@@ -2165,20 +2559,6 @@
.build())
.create();
- // create imported config that has a matching per-file rule
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(project)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user2.email())
- .addCodeOwnerSet(
- CodeOwnerSet.builder()
- .addPathExpression(testPathExpressions.matchFileType("md"))
- .addCodeOwnerEmail(user3.email())
- .build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
@@ -2191,11 +2571,17 @@
// owner config and the code owner from the matching per-file rule in the imported code owner
// config, the global code owners are ignored since there is a matching per-file rule that
// ignores parent and global code owners
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(user.email(), user3.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -2204,6 +2590,35 @@
Project.NameKey otherProject = projectOperations.newProject().create();
+ // create transitively imported config in other project
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(otherProject)
+ .branch("master")
+ .folderPath("/baz/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(CodeOwnerSet.builder().addCodeOwnerEmail(user2.email()).build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference2 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig2);
+
+ // create imported config in other project
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(otherProject)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .addImport(codeOwnerConfigReference2)
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig1);
+
// create importing config
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
@@ -2212,32 +2627,9 @@
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "/bar/OWNERS")
- .setProject(otherProject)
- .build())
+ .addImport(codeOwnerConfigReference1)
.create();
- // create imported config in other project
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(otherProject)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .addImport(CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/baz/OWNERS"))
- .create();
-
- // create transitively imported config in other project
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(otherProject)
- .branch("master")
- .folderPath("/baz/")
- .addCodeOwnerSet(CodeOwnerSet.builder().addCodeOwnerEmail(user2.email()).build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
@@ -2248,11 +2640,21 @@
// Expectation: we get the global owners from the importing code owner config and from the
// directly and transitively imported code owner configs in the other project
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
+ assertThat(pathCodeOwnersResult.getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email(), user2.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig1,
+ keyOfImportedCodeOwnerConfig2,
+ codeOwnerConfigReference2));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
@Test
@@ -2261,6 +2663,35 @@
Project.NameKey otherProject = projectOperations.newProject().create();
+ // create transitively imported config
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(otherProject)
+ .branch("master")
+ .folderPath("/bar/baz/")
+ .fileName("OWNERS")
+ .addCodeOwnerSet(CodeOwnerSet.builder().addCodeOwnerEmail(user2.email()).build())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference2 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig2);
+
+ // create imported config
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(otherProject)
+ .branch("master")
+ .folderPath("/bar/")
+ .fileName("OWNERS")
+ .addCodeOwnerEmail(user.email())
+ .addImport(codeOwnerConfigReference2)
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig1);
+
// create importing config
CodeOwnerConfig.Key importingCodeOwnerConfigKey =
codeOwnerConfigOperations
@@ -2269,32 +2700,9 @@
.branch("master")
.folderPath("/")
.addCodeOwnerEmail(admin.email())
- .addImport(
- CodeOwnerConfigReference.builder(
- CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, "bar/OWNERS")
- .setProject(otherProject)
- .build())
+ .addImport(codeOwnerConfigReference1)
.create();
- // create imported config
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(otherProject)
- .branch("master")
- .folderPath("/bar/")
- .addCodeOwnerEmail(user.email())
- .addImport(CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "baz/OWNERS"))
- .create();
-
- // create transitively imported config
- codeOwnerConfigOperations
- .newCodeOwnerConfig()
- .project(otherProject)
- .branch("master")
- .folderPath("/bar/baz/")
- .addCodeOwnerSet(CodeOwnerSet.builder().addCodeOwnerEmail(user2.email()).build())
- .create();
-
Optional<PathCodeOwners> pathCodeOwners =
pathCodeOwnersFactory.create(
transientCodeOwnerConfigCacheProvider.get(),
@@ -2305,11 +2713,21 @@
// Expectation: we get the global owners from the importing code owner config and from the
// directly and transitively imported code owner configs
+ PathCodeOwnersResult pathCodeOwnersResult = pathCodeOwners.get().resolveCodeOwnerConfig().get();
assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().getPathCodeOwners())
.comparingElementsUsing(hasEmail())
.containsExactly(admin.email(), user.email(), user2.email());
- assertThat(pathCodeOwners.get().resolveCodeOwnerConfig().get().hasUnresolvedImports())
- .isFalse();
+ assertThat(pathCodeOwnersResult.resolvedImports())
+ .containsExactly(
+ CodeOwnerConfigImport.createResolvedImport(
+ importingCodeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig1,
+ codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig1,
+ keyOfImportedCodeOwnerConfig2,
+ codeOwnerConfigReference2));
+ assertThat(pathCodeOwnersResult.unresolvedImports()).isEmpty();
}
private CodeOwnerConfig.Builder createCodeOwnerBuilder() {
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/UnresolvedImportTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/UnresolvedImportTest.java
deleted file mode 100644
index ce0494f..0000000
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/UnresolvedImportTest.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (C) 2021 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;
-
-import org.junit.Test;
-
-/** Tests for {@link UnresolvedImport}. */
-public class UnresolvedImportTest extends AbstractAutoValueTest {
- @Test
- public void toStringIncludesAllData() throws Exception {
- UnresolvedImport unresolvedImport =
- UnresolvedImport.create(
- CodeOwnerConfig.Key.create(project, "master", "/"),
- CodeOwnerConfig.Key.create(project, "master", "/bar/"),
- CodeOwnerConfigReference.create(CodeOwnerConfigImportMode.ALL, "/bar/OWNERS"),
- "test message");
- assertThatToStringIncludesAllData(unresolvedImport, UnresolvedImport.class);
- }
-}
diff --git a/javatests/com/google/gerrit/plugins/codeowners/restapi/CodeOwnerConfigFileJsonIT.java b/javatests/com/google/gerrit/plugins/codeowners/restapi/CodeOwnerConfigFileJsonIT.java
new file mode 100644
index 0000000..ed0b301
--- /dev/null
+++ b/javatests/com/google/gerrit/plugins/codeowners/restapi/CodeOwnerConfigFileJsonIT.java
@@ -0,0 +1,388 @@
+// Copyright (C) 2023 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.restapi;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.testing.GerritJUnit.assertThrows;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.plugins.codeowners.acceptance.AbstractCodeOwnersIT;
+import com.google.gerrit.plugins.codeowners.api.CodeOwnerConfigFileInfo;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfig;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImport;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigImportMode;
+import com.google.gerrit.plugins.codeowners.backend.CodeOwnerConfigReference;
+import org.junit.Before;
+import org.junit.Test;
+
+/** Tests for {@link CodeOwnerConfigFileJson}. */
+public class CodeOwnerConfigFileJsonIT extends AbstractCodeOwnersIT {
+ private CodeOwnerConfigFileJson CodeOwnerConfigFileJson;
+
+ @Before
+ public void setUpCodeOwnersPlugin() throws Exception {
+ CodeOwnerConfigFileJson = plugin.getSysInjector().getInstance(CodeOwnerConfigFileJson.class);
+ }
+
+ @Test
+ public void cannotFormatWithNullCodeOwnerConfigKey() throws Exception {
+ NullPointerException npe =
+ assertThrows(
+ NullPointerException.class,
+ () ->
+ CodeOwnerConfigFileJson.format(
+ /* codeOwnerConfigKey= */ null,
+ /* resolvedImports= */ ImmutableList.of(),
+ /* unresolvedImports= */ ImmutableList.of()));
+ assertThat(npe).hasMessageThat().isEqualTo("codeOwnerConfigKey");
+ }
+
+ @Test
+ public void cannotFormatWithNullResolvedImports() throws Exception {
+ CodeOwnerConfig.Key codeOwnerConfigKey =
+ CodeOwnerConfig.Key.create(Project.nameKey("project"), "master", "/");
+ NullPointerException npe =
+ assertThrows(
+ NullPointerException.class,
+ () ->
+ CodeOwnerConfigFileJson.format(
+ codeOwnerConfigKey,
+ /* resolvedImports= */ null,
+ /* unresolvedImports= */ ImmutableList.of()));
+ assertThat(npe).hasMessageThat().isEqualTo("resolvedImports");
+ }
+
+ @Test
+ public void cannotFormatWithNullUnresolvedImports() throws Exception {
+ CodeOwnerConfig.Key codeOwnerConfigKey =
+ CodeOwnerConfig.Key.create(Project.nameKey("project"), "master", "/");
+ NullPointerException npe =
+ assertThrows(
+ NullPointerException.class,
+ () ->
+ CodeOwnerConfigFileJson.format(
+ codeOwnerConfigKey,
+ /* resolvedImports= */ ImmutableList.of(),
+ /* unresolvedImports= */ null));
+ assertThat(npe).hasMessageThat().isEqualTo("unresolvedImports");
+ }
+
+ @Test
+ public void formatWithoutImports() throws Exception {
+ CodeOwnerConfig.Key codeOwnerConfigKey =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/baz/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo =
+ CodeOwnerConfigFileJson.format(
+ codeOwnerConfigKey,
+ /* resolvedImports= */ ImmutableList.of(),
+ /* unresolvedImports= */ ImmutableList.of());
+ assertThat(codeOwnerConfigFileInfo.project).isEqualTo(codeOwnerConfigKey.project().get());
+ assertThat(codeOwnerConfigFileInfo.branch)
+ .isEqualTo(codeOwnerConfigKey.branchNameKey().branch());
+ assertThat(codeOwnerConfigFileInfo.path)
+ .isEqualTo(codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getFilePath());
+ assertThat(codeOwnerConfigFileInfo.importMode).isNull();
+ assertThat(codeOwnerConfigFileInfo.imports).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedImports).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedErrorMessage).isNull();
+ }
+
+ @Test
+ public void formatWithUnresolvedImports() throws Exception {
+ CodeOwnerConfig.Key codeOwnerConfigKey =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ CodeOwnerConfig.Key.create(project, "stable", "/foo/baz/");
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo =
+ CodeOwnerConfigFileJson.format(
+ codeOwnerConfigKey,
+ /* resolvedImports= */ ImmutableList.of(),
+ /* unresolvedImports= */ ImmutableList.of(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ codeOwnerConfigKey,
+ keyOfImportedCodeOwnerConfig,
+ codeOwnerConfigReference,
+ "error message")));
+ assertThat(codeOwnerConfigFileInfo.project).isEqualTo(codeOwnerConfigKey.project().get());
+ assertThat(codeOwnerConfigFileInfo.branch)
+ .isEqualTo(codeOwnerConfigKey.branchNameKey().branch());
+ assertThat(codeOwnerConfigFileInfo.path)
+ .isEqualTo(codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getFilePath());
+ assertThat(codeOwnerConfigFileInfo.importMode).isNull();
+ assertThat(codeOwnerConfigFileInfo.imports).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedErrorMessage).isNull();
+
+ assertThat(codeOwnerConfigFileInfo.unresolvedImports).hasSize(1);
+ CodeOwnerConfigFileInfo unresolvedImportInfo =
+ Iterables.getOnlyElement(codeOwnerConfigFileInfo.unresolvedImports);
+ assertThat(unresolvedImportInfo.project)
+ .isEqualTo(keyOfImportedCodeOwnerConfig.project().get());
+ assertThat(unresolvedImportInfo.branch)
+ .isEqualTo(keyOfImportedCodeOwnerConfig.branchNameKey().branch());
+ assertThat(unresolvedImportInfo.path)
+ .isEqualTo(
+ codeOwnerConfigOperations.codeOwnerConfig(keyOfImportedCodeOwnerConfig).getFilePath());
+ assertThat(unresolvedImportInfo.importMode).isEqualTo(codeOwnerConfigReference.importMode());
+ assertThat(unresolvedImportInfo.imports).isNull();
+ assertThat(unresolvedImportInfo.unresolvedImports).isNull();
+ assertThat(unresolvedImportInfo.unresolvedErrorMessage).isEqualTo("error message");
+ }
+
+ @Test
+ public void formatWithImports() throws Exception {
+ CodeOwnerConfig.Key codeOwnerConfigKey =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ CodeOwnerConfig.Key.create(project, "stable", "/foo/baz/");
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo =
+ CodeOwnerConfigFileJson.format(
+ codeOwnerConfigKey,
+ /* resolvedImports= */ ImmutableList.of(
+ CodeOwnerConfigImport.createResolvedImport(
+ codeOwnerConfigKey, keyOfImportedCodeOwnerConfig, codeOwnerConfigReference)),
+ /* unresolvedImports= */ ImmutableList.of());
+ assertThat(codeOwnerConfigFileInfo.project).isEqualTo(codeOwnerConfigKey.project().get());
+ assertThat(codeOwnerConfigFileInfo.branch)
+ .isEqualTo(codeOwnerConfigKey.branchNameKey().branch());
+ assertThat(codeOwnerConfigFileInfo.path)
+ .isEqualTo(codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getFilePath());
+ assertThat(codeOwnerConfigFileInfo.importMode).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedImports).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedErrorMessage).isNull();
+
+ assertThat(codeOwnerConfigFileInfo.imports).hasSize(1);
+ CodeOwnerConfigFileInfo resolvedImportInfo =
+ Iterables.getOnlyElement(codeOwnerConfigFileInfo.imports);
+ assertThat(resolvedImportInfo.project).isEqualTo(keyOfImportedCodeOwnerConfig.project().get());
+ assertThat(resolvedImportInfo.branch)
+ .isEqualTo(keyOfImportedCodeOwnerConfig.branchNameKey().branch());
+ assertThat(resolvedImportInfo.path)
+ .isEqualTo(
+ codeOwnerConfigOperations.codeOwnerConfig(keyOfImportedCodeOwnerConfig).getFilePath());
+ assertThat(resolvedImportInfo.importMode).isEqualTo(codeOwnerConfigReference.importMode());
+ assertThat(resolvedImportInfo.imports).isNull();
+ assertThat(resolvedImportInfo.unresolvedImports).isNull();
+ assertThat(resolvedImportInfo.unresolvedErrorMessage).isNull();
+ }
+
+ @Test
+ public void formatWithNestedImports() throws Exception {
+ CodeOwnerConfig.Key codeOwnerConfigKey =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+
+ CodeOwnerConfig.Key keyOfImportedCodeOwnerConfig =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/baz")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY, keyOfImportedCodeOwnerConfig);
+
+ CodeOwnerConfig.Key keyOfNestedImportedCodeOwnerConfig1 =
+ CodeOwnerConfig.Key.create(project, "stable", "/foo/baz1/");
+ CodeOwnerConfigReference nestedCodeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.GLOBAL_CODE_OWNER_SETS_ONLY,
+ keyOfNestedImportedCodeOwnerConfig1);
+
+ CodeOwnerConfig.Key keyOfNestedImportedCodeOwnerConfig2 =
+ CodeOwnerConfig.Key.create(project, "stable", "/foo/baz2/");
+ CodeOwnerConfigReference nestedCodeOwnerConfigReference2 =
+ createCodeOwnerConfigReference(
+ CodeOwnerConfigImportMode.ALL, keyOfNestedImportedCodeOwnerConfig2);
+
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo =
+ CodeOwnerConfigFileJson.format(
+ codeOwnerConfigKey,
+ /* resolvedImports= */ ImmutableList.of(
+ CodeOwnerConfigImport.createResolvedImport(
+ codeOwnerConfigKey, keyOfImportedCodeOwnerConfig, codeOwnerConfigReference),
+ CodeOwnerConfigImport.createResolvedImport(
+ keyOfImportedCodeOwnerConfig,
+ keyOfNestedImportedCodeOwnerConfig1,
+ nestedCodeOwnerConfigReference1)),
+ /* unresolvedImports= */ ImmutableList.of(
+ CodeOwnerConfigImport.createUnresolvedImport(
+ keyOfImportedCodeOwnerConfig,
+ keyOfNestedImportedCodeOwnerConfig2,
+ nestedCodeOwnerConfigReference2,
+ "error message")));
+ assertThat(codeOwnerConfigFileInfo.project).isEqualTo(codeOwnerConfigKey.project().get());
+ assertThat(codeOwnerConfigFileInfo.branch)
+ .isEqualTo(codeOwnerConfigKey.branchNameKey().branch());
+ assertThat(codeOwnerConfigFileInfo.path)
+ .isEqualTo(codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey).getFilePath());
+ assertThat(codeOwnerConfigFileInfo.importMode).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedImports).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedErrorMessage).isNull();
+
+ assertThat(codeOwnerConfigFileInfo.imports).hasSize(1);
+ CodeOwnerConfigFileInfo resolvedImportInfo =
+ Iterables.getOnlyElement(codeOwnerConfigFileInfo.imports);
+ assertThat(resolvedImportInfo.project).isEqualTo(keyOfImportedCodeOwnerConfig.project().get());
+ assertThat(resolvedImportInfo.branch)
+ .isEqualTo(keyOfImportedCodeOwnerConfig.branchNameKey().branch());
+ assertThat(resolvedImportInfo.path)
+ .isEqualTo(
+ codeOwnerConfigOperations.codeOwnerConfig(keyOfImportedCodeOwnerConfig).getFilePath());
+ assertThat(resolvedImportInfo.importMode).isEqualTo(codeOwnerConfigReference.importMode());
+ assertThat(resolvedImportInfo.unresolvedErrorMessage).isNull();
+
+ assertThat(resolvedImportInfo.imports).hasSize(1);
+ CodeOwnerConfigFileInfo nestedResolvedImportInfo =
+ Iterables.getOnlyElement(resolvedImportInfo.imports);
+ assertThat(nestedResolvedImportInfo.project)
+ .isEqualTo(keyOfNestedImportedCodeOwnerConfig1.project().get());
+ assertThat(nestedResolvedImportInfo.branch)
+ .isEqualTo(keyOfNestedImportedCodeOwnerConfig1.branchNameKey().branch());
+ assertThat(nestedResolvedImportInfo.path)
+ .isEqualTo(
+ codeOwnerConfigOperations
+ .codeOwnerConfig(keyOfNestedImportedCodeOwnerConfig1)
+ .getFilePath());
+ assertThat(nestedResolvedImportInfo.importMode)
+ .isEqualTo(nestedCodeOwnerConfigReference1.importMode());
+ assertThat(nestedResolvedImportInfo.imports).isNull();
+ assertThat(nestedResolvedImportInfo.unresolvedImports).isNull();
+ assertThat(nestedResolvedImportInfo.unresolvedErrorMessage).isNull();
+
+ assertThat(resolvedImportInfo.unresolvedImports).hasSize(1);
+ CodeOwnerConfigFileInfo nestedUnresolvedImportInfo1 =
+ Iterables.getOnlyElement(resolvedImportInfo.unresolvedImports);
+ assertThat(nestedUnresolvedImportInfo1.project)
+ .isEqualTo(keyOfNestedImportedCodeOwnerConfig2.project().get());
+ assertThat(nestedUnresolvedImportInfo1.branch)
+ .isEqualTo(keyOfNestedImportedCodeOwnerConfig2.branchNameKey().branch());
+ assertThat(nestedUnresolvedImportInfo1.path)
+ .isEqualTo(
+ codeOwnerConfigOperations
+ .codeOwnerConfig(keyOfNestedImportedCodeOwnerConfig2)
+ .getFilePath());
+ assertThat(nestedUnresolvedImportInfo1.importMode)
+ .isEqualTo(nestedCodeOwnerConfigReference2.importMode());
+ assertThat(nestedUnresolvedImportInfo1.imports).isNull();
+ assertThat(nestedUnresolvedImportInfo1.unresolvedImports).isNull();
+ assertThat(nestedUnresolvedImportInfo1.unresolvedErrorMessage).isEqualTo("error message");
+ }
+
+ @Test
+ public void formatWithCyclicImports() throws Exception {
+ CodeOwnerConfig.Key codeOwnerConfigKey1 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference1 =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, codeOwnerConfigKey1);
+
+ CodeOwnerConfig.Key codeOwnerConfigKey2 =
+ codeOwnerConfigOperations
+ .newCodeOwnerConfig()
+ .project(project)
+ .branch("master")
+ .folderPath("/foo/bar/baz")
+ .addCodeOwnerEmail(admin.email())
+ .create();
+ CodeOwnerConfigReference codeOwnerConfigReference2 =
+ createCodeOwnerConfigReference(CodeOwnerConfigImportMode.ALL, codeOwnerConfigKey2);
+
+ CodeOwnerConfigFileInfo codeOwnerConfigFileInfo =
+ CodeOwnerConfigFileJson.format(
+ codeOwnerConfigKey1,
+ /* resolvedImports= */ ImmutableList.of(
+ CodeOwnerConfigImport.createResolvedImport(
+ codeOwnerConfigKey1, codeOwnerConfigKey2, codeOwnerConfigReference1),
+ CodeOwnerConfigImport.createResolvedImport(
+ codeOwnerConfigKey2, codeOwnerConfigKey1, codeOwnerConfigReference2)),
+ /* unresolvedImports= */ ImmutableList.of());
+
+ assertThat(codeOwnerConfigFileInfo.project).isEqualTo(codeOwnerConfigKey1.project().get());
+ assertThat(codeOwnerConfigFileInfo.branch)
+ .isEqualTo(codeOwnerConfigKey1.branchNameKey().branch());
+ assertThat(codeOwnerConfigFileInfo.path)
+ .isEqualTo(codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey1).getFilePath());
+ assertThat(codeOwnerConfigFileInfo.importMode).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedImports).isNull();
+ assertThat(codeOwnerConfigFileInfo.unresolvedErrorMessage).isNull();
+
+ assertThat(codeOwnerConfigFileInfo.imports).hasSize(1);
+ CodeOwnerConfigFileInfo resolvedImportInfo =
+ Iterables.getOnlyElement(codeOwnerConfigFileInfo.imports);
+ assertThat(resolvedImportInfo.project).isEqualTo(codeOwnerConfigKey2.project().get());
+ assertThat(resolvedImportInfo.branch).isEqualTo(codeOwnerConfigKey2.branchNameKey().branch());
+ assertThat(resolvedImportInfo.path)
+ .isEqualTo(codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey2).getFilePath());
+ assertThat(resolvedImportInfo.importMode).isEqualTo(codeOwnerConfigReference1.importMode());
+ assertThat(resolvedImportInfo.unresolvedErrorMessage).isNull();
+
+ assertThat(resolvedImportInfo.imports).hasSize(1);
+ CodeOwnerConfigFileInfo nestedResolvedImportInfo =
+ Iterables.getOnlyElement(resolvedImportInfo.imports);
+ assertThat(nestedResolvedImportInfo.project).isEqualTo(codeOwnerConfigKey1.project().get());
+ assertThat(nestedResolvedImportInfo.branch)
+ .isEqualTo(codeOwnerConfigKey1.branchNameKey().branch());
+ assertThat(nestedResolvedImportInfo.path)
+ .isEqualTo(codeOwnerConfigOperations.codeOwnerConfig(codeOwnerConfigKey1).getFilePath());
+ assertThat(nestedResolvedImportInfo.importMode)
+ .isEqualTo(codeOwnerConfigReference1.importMode());
+ assertThat(nestedResolvedImportInfo.imports).isNull();
+ assertThat(nestedResolvedImportInfo.unresolvedImports).isNull();
+ assertThat(nestedResolvedImportInfo.unresolvedErrorMessage).isNull();
+ }
+}
diff --git a/resources/Documentation/backend-find-owners.md b/resources/Documentation/backend-find-owners.md
index 846e8d9..1ea48ab 100644
--- a/resources/Documentation/backend-find-owners.md
+++ b/resources/Documentation/backend-find-owners.md
@@ -371,10 +371,12 @@
annotation, this annotation applies to all these users. E.g. if an annotation is
set for the all users wildcard (aka `*`) it applies to all users.
-**NOTE:** Only [email lines](#userEmails) and [per-file lines](#perFile) support
-annotations, for other lines (e.g. [file lines](#fileKeyword) and [include
-lines](#includeKeyword)) annotations are interpreted as [comments](#comments)
-and are silently ignored.
+**NOTE:** Only [email lines](#userEmails) and [per-file lines](#perFile) that
+assign code ownership directly to users support annotations, for other lines
+(e.g. [file lines](#fileKeyword), [include lines](#includeKeyword) and
+[per-file lines](#perFile) that reference other `OWNERS` files via the
+[file](#fileKeyword) keyword) annotations are interpreted as
+[comments](#comments) and are silently ignored.
### <a id="comments">Comments
diff --git a/resources/Documentation/rest-api.md b/resources/Documentation/rest-api.md
index 0185851..c97ba2c 100644
--- a/resources/Documentation/rest-api.md
+++ b/resources/Documentation/rest-api.md
@@ -475,6 +475,71 @@
(visible) users is returned, as many as are needed to fill up the requested
limit.
+#### Request
+
+```
+ GET /projects/foo%2Fbar/branches/master/code_owners/docs%2Findex.md HTTP/1.0
+```
+
+#### Response
+
+```
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "code_owners": [
+ {
+ "account": {
+ "_account_id": 1000096
+ }
+ },
+ {
+ "account": {
+ "_account_id": 1001439
+ }
+ },
+ {
+ "account": {
+ "_account_id": 1007265
+ }
+ },
+ {
+ "account": {
+ "_account_id": 1009877
+ }
+ },
+ {
+ "account": {
+ "_account_id": 1002930
+ }
+ }
+ ],
+ "code_owner_configs": [
+ {
+ "project": "foo/bar",
+ "branch": "master",
+ "path": "/docs/OWNERS"
+ },
+ {
+ "project": "foo/bar",
+ "branch": "master",
+ "path": "/OWNERS",
+ "imports": [
+ {
+ "project": "foo",
+ "branch": "master",
+ "path": "/OWNERS",
+ "import_mode": "ALL",
+ }
+ ]
+ }
+ ]
+ }
+```
+
#### <a id="scoringFactors">Scoring Factors
The following factors are taken into account for computing the scores of the
@@ -550,36 +615,6 @@
The same applies for [default code owners](config-guide.html#codeOwners).
-#### Request
-
-```
- GET /projects/foo%2Fbar/branches/master/code_owners/docs%2Findex.md HTTP/1.0
-```
-
-#### Response
-
-```
- HTTP/1.1 200 OK
- Content-Disposition: attachment
- Content-Type: application/json; charset=UTF-8
-
- )]}'
- {
- "code_owners": [
- {
- "account": {
- "_account_id": 1000096
- }
- },
- {
- "account": {
- "_account_id": 1001439
- },
- }
- ]
- }
-```
-
#### <a id="batch-list-code-owners"> Batch Request
There is no REST endpoint that allows to retrieve code owners for multiple
@@ -918,6 +953,22 @@
---
+### <a id="code-owner-config-file-info"> CodeOwnerConfigFileInfo
+The `CodeOwnerConfigFileInfo` entity contains information about a code owner
+config file and its imports.
+
+| Field Name | | Description |
+| ---------- | -------- | ----------- |
+| `project` || The name of the project from which the code owner config was loaded, or for unresolved imports, from which the code owner config was supposed to be loaded.
+| `branch` || The name of the branch from which the code owner config was loaded, or for unresolved imports, from which the code owner config was supposed to be loaded.
+| `path` || The absolute path of the code owner config file.
+| `imports` | optional | Imported code owner config files as [CodeOwnerConfigFileInfo](#code-owner-config-file-info) entities.
+| `unresolved_imports` | optional | Imported code owner config files that couldn't be resolved as [CodeOwnerConfigFileInfo](#code-owner-config-file-info) entities.
+| `unresolved_error_message` | optional | Message explaining why this code owner config couldn't be resolved. Only set if the `CodeOwnerConfigFileInfo` represents an import code owner config file that couldn't be resolved.
+| `import_mode` | optional | The import mode (`ALL` or `GLOBAL_CODE_OWNER_SETS_ONLY`). Only set if the `CodeOwnerConfigFileInfo` represents an imported code owner config file.
+
+---
+
### <a id="code-owner-config-info"> CodeOwnerConfigInfo
The `CodeOwnerConfigInfo` entity contains information about a code owner config
for a path.
@@ -1042,6 +1093,7 @@
| ------------- | -------- | ----------- |
| `code_owners` | | List of code owners as [CodeOwnerInfo](#code-owner-info) entities. The code owners are sorted by a score that is computed from mutliple [scoring factors](#scoringFactors).
| `owned_by_all_users` | optional | Whether the path is owned by all users. Not set if `false`.
+| `code_owner_configs` || The code owner config files that have been inspected to gather the code owners as [CodeOwnerConfigFileInfo](#code-owner-config-file-info) entities.
| `debug_logs` | optional | Debug logs that may help to understand why a user is or isn't suggested as a code owner. Only set if requested via `--debug`. This information is purely for debugging and the output may be changed at any time. This means bot callers must not parse the debug logs.
### <a id="file-code-owner-status-info"> FileCodeOwnerStatusInfo