blob: f1f1ea8763db4de180c2e931c70d683040af4b33 [file] [log] [blame]
// 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 static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.FlowActionInfo;
import com.google.gerrit.extensions.common.FlowExpressionInfo;
import com.google.gerrit.extensions.common.FlowInfo;
import com.google.gerrit.extensions.common.FlowInput;
import com.google.gerrit.extensions.common.FlowStageInfo;
import com.google.gerrit.extensions.common.FlowStageState;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.server.flow.Flow;
import com.google.gerrit.server.flow.FlowAction;
import com.google.gerrit.server.flow.FlowCreation;
import com.google.gerrit.server.flow.FlowExpression;
import com.google.gerrit.server.flow.FlowStage;
import com.google.gerrit.server.flow.FlowStageEvaluationStatus;
/**
* Produces flow-related entities, like {@link FlowInfo}s, which are serialized to JSON afterwards.
*/
public class FlowJson {
/** Formats the given {@link Flow} instance as a {@link FlowInfo}. */
public static FlowInfo format(Flow flow) {
requireNonNull(flow, "flow");
FlowInfo flowInfo = new FlowInfo();
flowInfo.uuid = flow.key().uuid();
flowInfo.owner = new AccountInfo(flow.ownerId().get());
flowInfo.setCreated(flow.createdOn());
flowInfo.stages = flow.stages().stream().map(FlowJson::format).collect(toImmutableList());
if (flow.lastEvaluatedOn().isPresent()) {
flowInfo.setLastEvaluated(flow.lastEvaluatedOn().get());
}
return flowInfo;
}
/** Formats the given {@link FlowStage} instance as a {@link FlowStageInfo}. */
private static FlowStageInfo format(FlowStage flowStage) {
requireNonNull(flowStage, "flowStage");
FlowStageInfo flowStageInfo = new FlowStageInfo();
flowStageInfo.expression = format(flowStage.expression());
flowStageInfo.state = mapState(flowStage.status().state());
flowStageInfo.message = flowStage.status().message().orElse(null);
return flowStageInfo;
}
/** Formats the given {@link FlowExpression} instance as a {@link FlowExpressionInfo}. */
private static FlowExpressionInfo format(FlowExpression flowExpression) {
requireNonNull(flowExpression, "flowExpression");
FlowExpressionInfo flowExpressionInfo = new FlowExpressionInfo();
flowExpressionInfo.condition = flowExpression.condition();
if (flowExpression.action().isPresent()) {
flowExpressionInfo.action = format(flowExpression.action().get());
}
return flowExpressionInfo;
}
/** Formats the given {@link FlowAction} instance as a {@link FlowActionInfo}. */
private static FlowActionInfo format(FlowAction flowAction) {
requireNonNull(flowAction, "flowAction");
FlowActionInfo flowActionInfo = new FlowActionInfo();
flowActionInfo.name = flowAction.name();
flowActionInfo.parameters = flowAction.parameters();
return flowActionInfo;
}
/**
* Maps the given {@link com.google.gerrit.server.flow.FlowStageEvaluationStatus.State} to a
* {@link FlowStageState}.
*/
@VisibleForTesting
public static FlowStageState mapState(FlowStageEvaluationStatus.State flowStageState) {
requireNonNull(flowStageState, "flowStageState");
return switch (flowStageState) {
case UNKNOWN ->
throw new IllegalStateException("The flow stage state has not been initialized");
case DONE -> FlowStageState.DONE;
case PENDING -> FlowStageState.PENDING;
case FAILED -> FlowStageState.FAILED;
case TERMINATED -> FlowStageState.TERMINATED;
};
}
/**
* Create a {@link FlowCreation} from the given {@link FlowInput}.
*
* @throws BadRequestException thrown if mandatory properties are missing
*/
public static FlowCreation createFlowCreation(
Project.NameKey projectName, Change.Id changeId, Account.Id ownerId, FlowInput flowInput)
throws BadRequestException {
requireNonNull(projectName, "projectName");
requireNonNull(changeId, "changeId");
requireNonNull(ownerId, "ownerId");
requireNonNull(flowInput, "flowInput");
if (flowInput.stageExpressions == null || flowInput.stageExpressions.isEmpty()) {
throw new BadRequestException("at least one stage expression is required");
}
if (Iterables.getLast(flowInput.stageExpressions).action == null) {
throw new BadRequestException("the last stage expression is required to have an action");
}
FlowCreation.Builder flowCreationBuilder =
FlowCreation.builder().projectName(projectName).changeId(changeId).ownerId(ownerId);
for (FlowExpressionInfo flowExpressionInfo : flowInput.stageExpressions) {
flowCreationBuilder.addStageExpression(createFlowExpression(flowExpressionInfo));
}
return flowCreationBuilder.build();
}
/**
* Create a {@link FlowExpression} from the given {@link FlowExpressionInfo}.
*
* @throws BadRequestException thrown if mandatory properties are missing
*/
public static FlowExpression createFlowExpression(FlowExpressionInfo flowExpressionInfo)
throws BadRequestException {
requireNonNull(flowExpressionInfo, "flowExpressionInfo");
if (Strings.isNullOrEmpty(flowExpressionInfo.condition)) {
throw new BadRequestException("condition in stage expression is required");
}
var builder = FlowExpression.builder().condition(flowExpressionInfo.condition);
if (flowExpressionInfo.action != null) {
builder.action(createFlowAction(flowExpressionInfo.action));
}
return builder.build();
}
/**
* Create a {@link FlowAction} from the given {@link FlowActionInfo}.
*
* @throws BadRequestException thrown if mandatory properties are missing
*/
public static FlowAction createFlowAction(FlowActionInfo flowActionInfo)
throws BadRequestException {
requireNonNull(flowActionInfo, "flowActionInfo");
if (Strings.isNullOrEmpty(flowActionInfo.name)) {
throw new BadRequestException("name in action is required");
}
FlowAction.Builder flowActionBuilder = FlowAction.builder().name(flowActionInfo.name);
if (flowActionInfo.parameters != null) {
flowActionBuilder.parameters(ImmutableList.copyOf(flowActionInfo.parameters));
}
return flowActionBuilder.build();
}
/**
* Private constructor to prevent instantiation of this class.
*
* <p>This class contains only static methods and hence never needs to be instantiated.
*/
private FlowJson() {}
}