Merge "Return ResourceConflict error when suggested fix can't be applied."
diff --git a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
index 4748e31..a5757ef 100644
--- a/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
+++ b/java/com/google/gerrit/acceptance/DisabledChangeIndex.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.acceptance;
 
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.query.DataSource;
@@ -72,6 +73,11 @@
   }
 
   @Override
+  public void deleteAllForProject(Project.NameKey project) {
+    throw new UnsupportedOperationException("ChangeIndex is disabled");
+  }
+
+  @Override
   public void deleteAll() {
     throw new UnsupportedOperationException("ChangeIndex is disabled");
   }
diff --git a/java/com/google/gerrit/index/testing/AbstractFakeIndex.java b/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
index b06143e..68ba59b 100644
--- a/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
+++ b/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
@@ -26,7 +26,9 @@
 import com.google.gerrit.entities.Change;
 import com.google.gerrit.entities.InternalGroup;
 import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
 import com.google.gerrit.index.Index;
+import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.SchemaFieldDefs;
@@ -48,6 +50,7 @@
 import com.google.gerrit.server.index.change.ChangeIndex;
 import com.google.gerrit.server.index.group.GroupIndex;
 import com.google.gerrit.server.query.change.ChangeData;
+import com.google.gerrit.server.query.change.ChangePredicates;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.time.Instant;
@@ -56,6 +59,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.IntStream;
 import java.util.stream.Stream;
 import org.eclipse.jgit.annotations.Nullable;
@@ -247,6 +251,7 @@
       extends AbstractFakeIndex<Change.Id, ChangeData, Map<String, Object>> implements ChangeIndex {
     private final ChangeData.Factory changeDataFactory;
     private final boolean skipMergable;
+    private final IndexConfig indexConfig;
 
     @Inject
     @VisibleForTesting
@@ -254,10 +259,12 @@
         SitePaths sitePaths,
         ChangeData.Factory changeDataFactory,
         @Assisted Schema<ChangeData> schema,
-        @GerritServerConfig Config cfg) {
+        @GerritServerConfig Config cfg,
+        IndexConfig indexConfig) {
       super(schema, sitePaths, "changes");
       this.changeDataFactory = changeDataFactory;
       this.skipMergable = !MergeabilityComputationBehavior.fromConfig(cfg).includeInIndex();
+      this.indexConfig = indexConfig;
     }
 
     @Override
@@ -314,6 +321,16 @@
     public void deleteByValue(ChangeData value) {
       delete(ChangeIndex.ENTITY_TO_KEY.apply(value));
     }
+
+    @Override
+    public void deleteAllForProject(NameKey project) {
+      QueryOptions opts = QueryOptions.create(indexConfig, 0, Integer.MAX_VALUE, Set.of());
+      DataSource<ChangeData> result = getSource(ChangePredicates.project(project), opts);
+      for (FieldBundle f : result.readRaw().toList()) {
+        int changeNum = f.<Integer>getValue(ChangeField.CHANGENUM_SPEC).intValue();
+        delete(Change.id(changeNum));
+      }
+    }
   }
 
   /** Fake implementation of {@link AccountIndex} where all filtering happens in-memory. */
diff --git a/java/com/google/gerrit/lucene/ChangeSubIndex.java b/java/com/google/gerrit/lucene/ChangeSubIndex.java
index 024b102..2bc29d9 100644
--- a/java/com/google/gerrit/lucene/ChangeSubIndex.java
+++ b/java/com/google/gerrit/lucene/ChangeSubIndex.java
@@ -22,6 +22,7 @@
 
 import com.google.common.collect.ImmutableSet;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.Schema.Values;
@@ -105,6 +106,11 @@
   }
 
   @Override
+  public void deleteAllForProject(Project.NameKey project) {
+    throw new UnsupportedOperationException("don't use ChangeSubIndex directly");
+  }
+
+  @Override
   public DataSource<ChangeData> getSource(Predicate<ChangeData> p, QueryOptions opts)
       throws QueryParseException {
     throw new UnsupportedOperationException("don't use ChangeSubIndex directly");
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index e5f7787..7b88f45 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -259,6 +259,16 @@
   }
 
   @Override
+  public void deleteAllForProject(Project.NameKey project) {
+    Term allForProject = new Term(ChangeField.PROJECT_SPEC.getName(), project.get());
+    try {
+      Futures.allAsList(openIndex.delete(allForProject), closedIndex.delete(allForProject)).get();
+    } catch (ExecutionException | InterruptedException e) {
+      throw new StorageException(e);
+    }
+  }
+
+  @Override
   public void deleteAll() {
     openIndex.deleteAll();
     closedIndex.deleteAll();
diff --git a/java/com/google/gerrit/server/index/change/ChangeIndex.java b/java/com/google/gerrit/server/index/change/ChangeIndex.java
index 74e9af1..cb44a3b 100644
--- a/java/com/google/gerrit/server/index/change/ChangeIndex.java
+++ b/java/com/google/gerrit/server/index/change/ChangeIndex.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.index.change;
 
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.index.Index;
 import com.google.gerrit.index.IndexDefinition;
 import com.google.gerrit.index.query.Predicate;
@@ -35,4 +36,6 @@
   }
 
   Function<ChangeData, Change.Id> ENTITY_TO_KEY = ChangeData::getId;
+
+  public void deleteAllForProject(Project.NameKey project);
 }
diff --git a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
index 55a2023..6b540b2 100644
--- a/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/accounts/AccountIT.java
@@ -71,6 +71,7 @@
 import com.google.gerrit.acceptance.ExtensionRegistry;
 import com.google.gerrit.acceptance.ExtensionRegistry.Registration;
 import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.acceptance.PushOneCommit.Result;
 import com.google.gerrit.acceptance.Sandboxed;
 import com.google.gerrit.acceptance.TestAccount;
 import com.google.gerrit.acceptance.UseClockStep;
@@ -2672,6 +2673,23 @@
   }
 
   @Test
+  @UseClockStep
+  public void updateDrafts() throws Exception {
+    try {
+      PushOneCommit.Result r1 = createChange();
+
+      requestScopeOperations.setApiUser(user.id());
+      CommentInfo draft = createDraft(r1, PushOneCommit.FILE_NAME, "draft creation");
+      updateDraft(r1, draft, "draft update");
+      assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList()).hasSize(1);
+      assertThat(gApi.changes().id(r1.getChangeId()).current().draftsAsList().get(0).message)
+          .isEqualTo("draft update");
+    } finally {
+      cleanUpDrafts();
+    }
+  }
+
+  @Test
   public void userCanGenerateNewHttpPassword() throws Exception {
     sender.clear();
     String newPassword = gApi.accounts().self().generateHttpPassword();
@@ -2937,12 +2955,22 @@
         .isEqualTo(postUpdateStatus);
   }
 
-  private void createDraft(PushOneCommit.Result r, String path, String message) throws Exception {
+  @CanIgnoreReturnValue
+  private CommentInfo createDraft(Result r, String path, String message) throws Exception {
     DraftInput in = new DraftInput();
     in.path = path;
     in.line = 1;
     in.message = message;
-    gApi.changes().id(r.getChangeId()).current().createDraft(in);
+    return gApi.changes().id(r.getChangeId()).current().createDraft(in).get();
+  }
+
+  @CanIgnoreReturnValue
+  private CommentInfo updateDraft(Result r, CommentInfo orig, String newMessage) throws Exception {
+    DraftInput in = new DraftInput();
+    in.path = orig.path;
+    in.line = orig.line;
+    in.message = newMessage;
+    return gApi.changes().id(r.getChangeId()).current().draft(orig.id).update(in);
   }
 
   private void cleanUpDrafts() throws Exception {
diff --git a/javatests/com/google/gerrit/acceptance/server/index/change/LuceneChangeIndexerIT.java b/javatests/com/google/gerrit/acceptance/server/index/change/LuceneChangeIndexerIT.java
index b8af367..d6aa709 100644
--- a/javatests/com/google/gerrit/acceptance/server/index/change/LuceneChangeIndexerIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/index/change/LuceneChangeIndexerIT.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.acceptance.PushOneCommit;
 import com.google.gerrit.acceptance.config.GerritConfig;
 import com.google.gerrit.entities.Project.NameKey;
+import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.index.IndexDefinition;
 import com.google.gerrit.index.RefState;
 import com.google.gerrit.index.SiteIndexer.Result;
@@ -35,6 +36,7 @@
 import com.google.gerrit.testing.ConfigSuite;
 import com.google.inject.Inject;
 import java.util.Collection;
+import java.util.List;
 import java.util.Set;
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.ObjectId;
@@ -104,6 +106,21 @@
     }
   }
 
+  @Test
+  public void deleteAllForProjectDeletesFromIndex() throws Exception {
+    createChange();
+    createChange();
+    createChange();
+
+    List<ChangeInfo> result = gApi.changes().query("project:" + project.get()).get();
+    assertThat(result).hasSize(3);
+
+    index.deleteAllForProject(project);
+
+    result = gApi.changes().query("project:" + project.get()).get();
+    assertThat(result).isEmpty();
+  }
+
   private void createIndexWithMissingChangeAndReindex(ChangeIndexedCounter changeIndexedCounter)
       throws Exception {
     PushOneCommit.Result res = createChange();
diff --git a/javatests/com/google/gerrit/acceptance/ssh/CustomIndexIT.java b/javatests/com/google/gerrit/acceptance/ssh/CustomIndexIT.java
index 7fead5c..448897c 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/CustomIndexIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/CustomIndexIT.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.index.IndexConfig;
 import com.google.gerrit.index.IndexType;
 import com.google.gerrit.index.Schema;
 import com.google.gerrit.index.project.ProjectIndex;
@@ -108,7 +109,8 @@
       SitePaths sitePaths,
       ChangeData.Factory changeDataFactory,
       @Assisted Schema<ChangeData> schema,
-      @GerritServerConfig Config cfg) {
-    super(sitePaths, changeDataFactory, schema, cfg);
+      @GerritServerConfig Config cfg,
+      IndexConfig indexConfig) {
+    super(sitePaths, changeDataFactory, schema, cfg, indexConfig);
   }
 }
diff --git a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
index 1a51c00..e4ccfdb 100644
--- a/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
+++ b/javatests/com/google/gerrit/server/index/change/FakeChangeIndex.java
@@ -18,6 +18,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.gerrit.entities.Change;
+import com.google.gerrit.entities.Project;
 import com.google.gerrit.index.IndexedField;
 import com.google.gerrit.index.QueryOptions;
 import com.google.gerrit.index.Schema;
@@ -105,6 +106,11 @@
   }
 
   @Override
+  public void deleteAllForProject(Project.NameKey project) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
   public void deleteAll() {
     throw new UnsupportedOperationException();
   }
diff --git a/plugins/delete-project b/plugins/delete-project
index bd49c1b..08bc142 160000
--- a/plugins/delete-project
+++ b/plugins/delete-project
@@ -1 +1 @@
-Subproject commit bd49c1bf4212a166d1246774cb8c70d54ead31ba
+Subproject commit 08bc142c829aed843b1145b27fab5bb1d973cb51