All Downloads are FREE. Search and download functionalities are using the official Maven repository.

step.core.execution.ExecutionServices Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (C) 2020, exense GmbH
 *  
 * This file is part of STEP
 *  
 * STEP is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *  
 * STEP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *  
 * You should have received a copy of the GNU Affero General Public License
 * along with STEP.  If not, see .
 ******************************************************************************/
package step.core.execution;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Singleton;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import org.bson.types.ObjectId;
import step.automation.packages.execution.AutomationPackageExecutor;
import step.controller.services.async.AsyncTaskStatus;
import step.core.access.User;
import step.core.accessors.AbstractOrganizableObject;
import step.core.artefacts.reports.ReportNode;
import step.core.artefacts.reports.aggregated.AggregatedReportView;
import step.core.artefacts.reports.aggregated.AggregatedReportViewBuilder;
import step.core.collections.SearchOrder;
import step.core.deployment.AbstractStepAsyncServices;
import step.core.deployment.ControllerServiceException;
import step.core.deployment.FindByCriteraParam;
import step.core.entities.EntityManager;
import step.core.execution.model.*;
import step.core.repositories.RepositoryObjectManager;
import step.core.repositories.RepositoryObjectReference;
import step.framework.server.Session;
import step.framework.server.security.Secured;
import step.framework.server.tables.service.TableService;
import step.framework.server.tables.service.bulk.TableBulkOperationReport;
import step.framework.server.tables.service.bulk.TableBulkOperationRequest;

import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Singleton
@Path("executions")
@Tag(name = "Executions")
public class ExecutionServices extends AbstractStepAsyncServices {

	protected ExecutionAccessor executionAccessor;
	private TableService tableService;

	@PostConstruct
	public void init() throws Exception {
		super.init();
		executionAccessor = getContext().getExecutionAccessor();
		tableService = getContext().require(TableService.class);
	}

	@Operation(description = "Starts an execution with the given parameters.")
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.TEXT_PLAIN)
	@Path("/start")
	@Secured(right = "plan-execute")
	public String execute(ExecutionParameters executionParams) {
		checkRightsOnBehalfOf("plan-execute", executionParams.getUserID());
		applyUserIdFromSessionIfNotSpecified(executionParams);
		return getScheduler().execute(executionParams);
	}

	private void applyUserIdFromSessionIfNotSpecified(ExecutionParameters executionParams) {
		// explicitly defined user id has a priority
		if (executionParams.getUserID() == null) {
			applyUserIdFromSession(executionParams);
		}
	}

	private void applyUserIdFromSession(ExecutionParameters executionParams) {
		Session session = getSession();
		if (session != null) {
			User user = session.getUser();
			if (user != null) {
				executionParams.setUserID(user.getUsername());
			}
		}
	}

	@Operation(description = "Returns all executions.")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="execution-read")
	public List getAll(@QueryParam("skip") Integer skip, @QueryParam("limit") Integer limit) {
		if(skip != null && limit != null) {
			return executionAccessor.getRange(skip, limit);
		} else {
			return getAll(0, 1000);
		}
	}

	@Operation(description = "Returns the execution with the given execution id.")
	@GET
	@Path("/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="execution-read")
	public Execution getExecutionById(@PathParam("id") String id) {
		return executionAccessor.get(id);
	}

	@Operation(description = "Stops the execution with the given execution id.")
	@GET
	@Path("/{id}/stop")
	@Secured(right="plan-execute")
	public Void abort(@PathParam("id") String executionID) {
		ExecutionContext context = getExecutionRunnable(executionID);
		if(context!=null) {
			ExecutionEngineRunner.abort(context);
		}
		return null;
	}

	@Operation(description = "Force stop the execution with the given execution id.")
	@GET
	@Path("/{id}/force-stop")
	@Secured(right = "plan-execute")
	public Void forceStop(@PathParam("id") String executionID) {
		ExecutionContext context = getExecutionRunnable(executionID);
		if (context != null) {
			ExecutionEngineRunner.forceAbort(context);
		}
		return null;
	}

	@Operation(description = "Returns the executions matching the provided attributes.")
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	@Path("/search")
	@Secured(right="execution-read")
	public Execution getExecutionByAttribute(Map attributes) {
		return executionAccessor.findByAttributes(attributes);
	}
    
	@Operation(description = "Returns a list of executions by the provided ids.")
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	@Path("/search/by/ids")
	@Secured(right="execution-read")
	public List getExecutionsByIds(List ids) {
		return executionAccessor.findByIds(ids).collect(Collectors.toList());
	}

	@Operation(description = "Returns a map of execution ID to names by the provided ids.")
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	@Path("/search/names/by/ids")
	@Secured(right="execution-read")
	public Map getExecutionsNamesByIds(List ids) {
		return executionAccessor.findByIds(ids).collect(Collectors.toMap(e -> e.getId().toHexString(), Execution::getDescription));
	}

	@Operation(description = "Returns the execution matching the provided repository object reference.")
	@POST
	@Path("/search/by/ref")
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	@Secured(right="execution-read")
	public List getExecutionsByRepositoryObjectReference(RepositoryObjectReference objectReference) {
		return getContext().getExecutionAccessor().getTestExecutionsByArtefactURL(objectReference);
	}

	@Operation(description = "Returns the execution matching the given criteria.")
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Path("/search/by/critera")
	@Secured(right="execution-read")
	public List findByCritera(FindByCriteraParam param) {
		return ((ExecutionAccessorImpl) getContext().getExecutionAccessor()).findByCritera(param.getCriteria(), 
				param.getStart().getTime(), param.getEnd().getTime(), new SearchOrder("endTime", -1),
				param.getSkip(), param.getLimit());
	}

	@Operation(description = "Returns the report nodes of the given execution and matching the given class.")
	@GET
	@Path("/{id}/reportnodes")
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="execution-read")
	public List getReportNodesByExecutionID(@PathParam("id") String executionID, @QueryParam("class") String reportNodeClass, @QueryParam("limit") int limit) {
		List result = new ArrayList<>();
		Iterator iterator;
		if(reportNodeClass!=null) {
			try (Stream reportNodesByExecutionID = getContext().getReportAccessor().getReportNodesByExecutionIDAndClass(executionID, reportNodeClass, limit)) {
				result = reportNodesByExecutionID.collect(Collectors.toList());
			}
		} else {
			try (Stream reportNodesByExecutionID = getContext().getReportAccessor().getReportNodesByExecutionID(executionID, limit)) {
				result = reportNodesByExecutionID.collect(Collectors.toList());
			}
		}
		return result;
	}

	@Operation(description = "Returns the list of report nodes with contributing errors for the given execution")
	@GET
	@Path("/{id}/reportnodes-with-errors")
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right = "execution-read")
	public List getReportNodeWithContributingErrors(@PathParam("id") String executionId, @QueryParam("skip") Integer skip, @QueryParam("limit") Integer limit) {
		skip = skip != null ? skip : 0;
		limit = limit != null ? limit : 1000;
		Stream stream = getContext().getReportAccessor().getReportNodesWithContributingErrors(executionId, skip, limit);
		return stream.collect(Collectors.toList());
	}

	@Operation(description = "Returns the list of report nodes by execution id and artefact hash")
	@GET
	@Path("/{id}/reportnodes-by-hash/{hash}")
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right = "execution-read")
	public List getReportNodesByArtefactHash(@PathParam("id") String executionId, @PathParam("hash") String hash, @QueryParam("skip") Integer skip, @QueryParam("limit") Integer limit) {
		skip = skip != null ? skip : 0;
		limit = limit != null ? limit : 1000;
		try (Stream stream = getContext().getReportAccessor().getReportNodesByArtefactHash(executionId, hash, skip, limit)) {
			return stream.collect(Collectors.toList());
		}
	}

	@Operation(description = "Returns the full aggregated report view for the provided execution.")
	@GET
	@Path("/{id}/report/aggregated")
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right = "execution-read")
	public AggregatedReportView getFullAggregatedReportView(@PathParam("id") String executionId) {
		return getAggregatedReportView(executionId, new AggregatedReportViewBuilder.AggregatedReportViewRequest(null));
	}

	@Operation(description = "Returns an aggregated report view for the provided execution and aggregation parameters.")
	@POST
	@Path("/{id}/report/aggregated")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right = "execution-read")
	public AggregatedReportView getAggregatedReportView(@PathParam("id") String executionId, AggregatedReportViewBuilder.AggregatedReportViewRequest request) {
		ExecutionEngineContext executionEngineContext = getScheduler().getExecutor().getExecutionEngine().getExecutionEngineContext();
		AggregatedReportViewBuilder aggregatedReportViewBuilder = new AggregatedReportViewBuilder(executionEngineContext, executionId);
		return aggregatedReportViewBuilder.buildAggregatedReportView(request);
	}

	@Operation(description = "Updates the provided execution.")
	@POST
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	@Secured(right="execution-write")
	public Execution saveExecution(Execution execution) {
		return executionAccessor.save(execution);
	}

	@Operation(description = "Delete the execution with the given execution id, use the housekeeping services for full deletion")
	@DELETE
	@Path("/{id}")
	@Consumes(MediaType.APPLICATION_JSON)
	@Secured(right="execution-delete")
	public void deleteExecution(@PathParam("id") String id) {
		Execution execution = executionAccessor.get(id);
		if (execution.getStatus().equals(ExecutionStatus.ENDED)) {
			throw new ControllerServiceException("Only ended executions can be deleted.");
		}
		executionAccessor.remove(new ObjectId(id));
	}

	@Operation(description = "Restart multiple executions according to the provided parameters.")
	@POST
	@Path("/bulk/restart")
	@Consumes(MediaType.APPLICATION_JSON)
	@Secured(right="plan-bulk-execute")
	public AsyncTaskStatus restartExecutions(TableBulkOperationRequest request) {
		Consumer consumer = t -> {
			try {
				ExecutionParameters executionParameters = executionAccessor.get(t).getExecutionParameters();
				applyUserIdFromSession(executionParameters);
				execute(executionParameters);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		};
		return scheduleAsyncTaskWithinSessionContext(h ->
				tableService.performBulkOperation(EntityManager.executions, request, consumer, getSession()));
	}

	@Operation(description = "Stop multiple executions according to the provided parameters.")
	@POST
	@Path("/bulk/stop")
	@Consumes(MediaType.APPLICATION_JSON)
	@Secured(right="plan-bulk-execute")
	public AsyncTaskStatus stopExecutions(TableBulkOperationRequest request) {
		Consumer consumer = t -> {
			try {
				abort(t);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		};
		return scheduleAsyncTaskWithinSessionContext(h ->
				tableService.performBulkOperation(EntityManager.executions, request, consumer, getSession()));
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy