Merge "Add isFlowsEnabled API"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index e0dee26..a12e1ce 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -3722,10 +3722,37 @@
HTTP/1.1 204 No Content
----
-
[[flow-endpoints]]
== Flow Endpoints
+
+[[is-flows-enabled]]
+=== Is Flows Enabled
+--
+'GET /changes/link:#change-id[\{change-id\}]/is-flows-enabled'
+--
+
+Returns whether flows are enabled for this change.
+
+As result an link:#enabled-info[IsFlowsEnabledInfo] entity is returned.
+
+.Request
+----
+ GET /changes/myProject~65178/is-flows-enabled HTTP/1.0
+----
+
+.Response
+----
+ HTTP/1.1 200 OK
+ Content-Disposition: attachment
+ Content-Type: application/json; charset=UTF-8
+
+ )]}'
+ {
+ "enabled": true
+ }
+----
+
[[list-flows]]
=== List Flows
--
@@ -8322,6 +8349,16 @@
link:#rebase-edit[rebases the change edit] and conflicts are allowed.
|===========================
+[[enabled-info]]
+=== IsFlowsEnabledInfo
+The `IsFlowsEnabledInfo` entity contains information about whether flows is enabled.
+
+[options="header",cols="1,^1,5"]
+|===========================
+|Field Name ||Description
+|`enabled` ||A boolean indicating whether the feature is enabled.
+|===========================
+
[[evaluate-change-query-expression-result-info]]
=== EvaluateChangeQueryExpressionResultInfo
The `EvaluateChangeQueryExpressionResultInfo` entity contains the result of
diff --git a/java/com/google/gerrit/acceptance/TestExtensions.java b/java/com/google/gerrit/acceptance/TestExtensions.java
index cd6b5ee..97fcf0e 100644
--- a/java/com/google/gerrit/acceptance/TestExtensions.java
+++ b/java/com/google/gerrit/acceptance/TestExtensions.java
@@ -30,6 +30,7 @@
import com.google.gerrit.entities.Project;
import com.google.gerrit.entities.SubmitRecord;
import com.google.gerrit.exceptions.StorageException;
+import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.ChangeUtil;
import com.google.gerrit.server.PluginPushOption;
import com.google.gerrit.server.ValidationOptionsListener;
@@ -234,6 +235,13 @@
}
@Override
+ public Boolean isFlowsEnabled(Project.NameKey projectName, Change.Id changeId)
+ throws RestApiException {
+ // Always return true for testing purposes.
+ return true;
+ }
+
+ @Override
public Optional<Flow> getFlow(FlowKey flowKey) throws StorageException {
return Optional.ofNullable(flows.get(flowKey));
}
diff --git a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
index 6d7b800..7af343a 100644
--- a/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
+++ b/java/com/google/gerrit/extensions/api/changes/ChangeApi.java
@@ -31,6 +31,7 @@
import com.google.gerrit.extensions.common.EvaluateChangeQueryExpressionResultInfo;
import com.google.gerrit.extensions.common.FlowInfo;
import com.google.gerrit.extensions.common.FlowInput;
+import com.google.gerrit.extensions.common.IsFlowsEnabledInfo;
import com.google.gerrit.extensions.common.MergePatchSetInput;
import com.google.gerrit.extensions.common.PureRevertInfo;
import com.google.gerrit.extensions.common.RebaseChainInfo;
@@ -103,6 +104,8 @@
/** Look up a flow of this change by its UUID. */
FlowApi flow(String flowUuid) throws RestApiException;
+ IsFlowsEnabledInfo isFlowsEnabled() throws RestApiException;
+
/** Get the flows of this change/ */
List<FlowInfo> flows() throws RestApiException;
diff --git a/java/com/google/gerrit/extensions/common/IsFlowsEnabledInfo.java b/java/com/google/gerrit/extensions/common/IsFlowsEnabledInfo.java
new file mode 100644
index 0000000..21e4734
--- /dev/null
+++ b/java/com/google/gerrit/extensions/common/IsFlowsEnabledInfo.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2025 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.extensions.common;
+
+/**
+ * Representation of a boolean flag in the REST API.
+ *
+ * <p>This class determines the JSON format of boolean flags in the REST API.
+ */
+public class IsFlowsEnabledInfo {
+ public boolean enabled;
+
+ public IsFlowsEnabledInfo(boolean enabled) {
+ this.enabled = enabled;
+ }
+}
diff --git a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
index d3bcbfc..9ce3d79 100644
--- a/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
+++ b/java/com/google/gerrit/server/api/changes/ChangeApiImpl.java
@@ -58,6 +58,7 @@
import com.google.gerrit.extensions.common.FlowInput;
import com.google.gerrit.extensions.common.Input;
import com.google.gerrit.extensions.common.InputWithMessage;
+import com.google.gerrit.extensions.common.IsFlowsEnabledInfo;
import com.google.gerrit.extensions.common.MergePatchSetInput;
import com.google.gerrit.extensions.common.PureRevertInfo;
import com.google.gerrit.extensions.common.RebaseChainInfo;
@@ -119,6 +120,7 @@
import com.google.gerrit.server.restapi.change.SuggestChangeReviewers;
import com.google.gerrit.server.restapi.flow.CreateFlow;
import com.google.gerrit.server.restapi.flow.FlowCollection;
+import com.google.gerrit.server.restapi.flow.IsFlowsEnabled;
import com.google.gerrit.server.restapi.flow.ListFlows;
import com.google.gerrit.util.cli.CmdLineParser;
import com.google.inject.Inject;
@@ -188,6 +190,7 @@
private final PutMessage putMessage;
private final CreateFlow createFlow;
private final ListFlows listFlows;
+ private final IsFlowsEnabled isFlowsEnabled;
private final Provider<EvaluateChangeQueryExpression> evaluateChangeQueryExpressionProvider;
private final Provider<GetPureRevert> getPureRevertProvider;
private final DynamicOptionParser dynamicOptionParser;
@@ -246,6 +249,7 @@
PutMessage putMessage,
CreateFlow createFlow,
ListFlows listFlows,
+ IsFlowsEnabled isFlowsEnabled,
Provider<EvaluateChangeQueryExpression> evaluateChangeQueryExpressionProvider,
Provider<GetPureRevert> getPureRevertProvider,
DynamicOptionParser dynamicOptionParser,
@@ -302,6 +306,7 @@
this.putMessage = putMessage;
this.createFlow = createFlow;
this.listFlows = listFlows;
+ this.isFlowsEnabled = isFlowsEnabled;
this.evaluateChangeQueryExpressionProvider = evaluateChangeQueryExpressionProvider;
this.getPureRevertProvider = getPureRevertProvider;
this.dynamicOptionParser = dynamicOptionParser;
@@ -352,6 +357,15 @@
}
@Override
+ public IsFlowsEnabledInfo isFlowsEnabled() throws RestApiException {
+ try {
+ return isFlowsEnabled.apply(change).value();
+ } catch (Exception e) {
+ throw asRestApiException("Cannot check if flows are enabled", e);
+ }
+ }
+
+ @Override
public List<FlowInfo> flows() throws RestApiException {
try {
return listFlows.apply(change).value();
diff --git a/java/com/google/gerrit/server/flow/FlowService.java b/java/com/google/gerrit/server/flow/FlowService.java
index 32d8b56..25fd799 100644
--- a/java/com/google/gerrit/server/flow/FlowService.java
+++ b/java/com/google/gerrit/server/flow/FlowService.java
@@ -20,6 +20,7 @@
import com.google.gerrit.entities.Project;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.ExtensionPoint;
+import com.google.gerrit.extensions.restapi.RestApiException;
import java.util.Optional;
/**
@@ -34,6 +35,21 @@
*/
@ExtensionPoint
public interface FlowService {
+
+ /**
+ * Checks if Flows is enabled for change.
+ *
+ * <p>Can be used to disable flows at a change/project level. Implementations can have user
+ * information injected to disable it for a specific user.
+ *
+ * @param projectName The name of the project that contains the change.
+ * @param changeId The ID of the change for which the flows should be listed.
+ * @return If flows is enabled for the user.
+ * @throws RestApiException thrown if checking flow access has failed
+ */
+ public Boolean isFlowsEnabled(Project.NameKey projectName, Change.Id changeId)
+ throws RestApiException;
+
/**
* Create a new flow.
*
diff --git a/java/com/google/gerrit/server/restapi/flow/FlowRestApiModule.java b/java/com/google/gerrit/server/restapi/flow/FlowRestApiModule.java
index bcc241f..442e6b1 100644
--- a/java/com/google/gerrit/server/restapi/flow/FlowRestApiModule.java
+++ b/java/com/google/gerrit/server/restapi/flow/FlowRestApiModule.java
@@ -33,5 +33,7 @@
get(FLOW_KIND).to(GetFlow.class);
delete(FLOW_KIND).to(DeleteFlow.class);
+
+ get(CHANGE_KIND, "is-flows-enabled").to(IsFlowsEnabled.class);
}
}
diff --git a/java/com/google/gerrit/server/restapi/flow/IsFlowsEnabled.java b/java/com/google/gerrit/server/restapi/flow/IsFlowsEnabled.java
new file mode 100644
index 0000000..941e87e
--- /dev/null
+++ b/java/com/google/gerrit/server/restapi/flow/IsFlowsEnabled.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2025 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.server.restapi.flow;
+
+import com.google.gerrit.extensions.common.IsFlowsEnabledInfo;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.flow.FlowServiceUtil;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+/**
+ * REST endpoint to check if Flows is enabled for the user.
+ *
+ * <p>This REST endpoint handles {@code GET /change/<change-id>/isFlowsEnabled} requests.
+ */
+@Singleton
+public class IsFlowsEnabled implements RestReadView<ChangeResource> {
+ private final FlowServiceUtil flowServiceUtil;
+
+ @Inject
+ IsFlowsEnabled(FlowServiceUtil flowServiceUtil) {
+ this.flowServiceUtil = flowServiceUtil;
+ }
+
+ @Override
+ public Response<IsFlowsEnabledInfo> apply(ChangeResource changeResource) throws RestApiException {
+ IsFlowsEnabledInfo enabledInfo =
+ new IsFlowsEnabledInfo(
+ flowServiceUtil
+ .getFlowServiceOrThrow()
+ .isFlowsEnabled(changeResource.getProject(), changeResource.getId()));
+ return Response.ok(enabledInfo);
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/FlowRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/FlowRestApiBindingsIT.java
index f568886..f5953d2 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/FlowRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/FlowRestApiBindingsIT.java
@@ -40,7 +40,10 @@
@Inject private ExtensionRegistry extensionRegistry;
private static final ImmutableList<RestCall> CHANGE_ENDPOINTS =
- ImmutableList.of(RestCall.get("/changes/%s/flows"), RestCall.post("/changes/%s/flows"));
+ ImmutableList.of(
+ RestCall.get("/changes/%s/flows"),
+ RestCall.get("/changes/%s/is-flows-enabled"),
+ RestCall.post("/changes/%s/flows"));
private static final ImmutableList<RestCall> FLOW_ENDPOINTS =
ImmutableList.of(