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

step.core.plans.PlanServices 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.plans;

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 jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.StreamingOutput;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import step.artefacts.CallPlan;
import step.artefacts.handlers.PlanLocator;
import step.artefacts.handlers.SelectorHelper;
import step.controller.services.entities.AbstractEntityServices;
import step.core.GlobalContext;
import step.core.artefacts.AbstractArtefact;
import step.core.artefacts.handlers.ArtefactHandlerRegistry;
import step.core.deployment.ControllerServiceException;
import step.core.dynamicbeans.DynamicJsonObjectResolver;
import step.core.dynamicbeans.DynamicJsonValueResolver;
import step.core.entities.EntityManager;
import step.core.objectenricher.ObjectPredicate;
import step.core.objectenricher.ObjectPredicateFactory;
import step.framework.server.security.Secured;
import step.framework.server.security.SecuredContext;
import step.plans.parser.yaml.YamlPlanReader;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

@Singleton
@Path("plans")
@Tag(name = "Plans")
@Tag(name = "Entity=Plan")
@SecuredContext(key = "entity", value = "plan")
public class PlanServices extends AbstractEntityServices {

	private static final Logger log = LoggerFactory.getLogger(PlanServices.class);

	protected PlanAccessor planAccessor;
	protected PlanTypeRegistry planTypeRegistry;
	protected ObjectPredicateFactory objectPredicateFactory;
	private ArtefactHandlerRegistry artefactHandlerRegistry;

	private YamlPlanReader yamlPlanReader = new YamlPlanReader();

	public PlanServices() {
		super(EntityManager.plans);
	}

	@PostConstruct
	public void init() throws Exception {
		super.init();
		GlobalContext context = getContext();
		planAccessor = context.getPlanAccessor();
		planTypeRegistry = context.get(PlanTypeRegistry.class);
		objectPredicateFactory = context.get(ObjectPredicateFactory.class);
		artefactHandlerRegistry = context.getArtefactHandlerRegistry();
	}

	@Operation(description = "Returns a new plan instance as template.")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-write")
	public Plan newPlan(@QueryParam("type") String type, @QueryParam("template") String template) throws Exception {
		PlanType planType = planTypeRegistry.getPlanType(type);
		Plan plan = planType.newPlan(template);
		getObjectEnricher().accept(plan);
		return plan;
	}

	@Override
	protected Plan beforeSave(Plan entity) {
		PlanType planType = (PlanType) planTypeRegistry.getPlanType(entity.getClass());
		planType.onBeforeSave(entity);
		return super.beforeSave(entity);
	}

	@Operation(description = "Compiles the plan with the given id.")
	@GET
	@Path("/{id}/compile")
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-write")
	public PlanCompilationResult compilePlanWithId(@PathParam("id") String id) throws Exception {
		Plan plan = planAccessor.get(id);
		PlanCompilationResult planCompilationResult = compilePlan(plan);
		if(!planCompilationResult.isHasError()) {
			save(plan);
		}
		return planCompilationResult;
	}

	@Operation(description = "Compiles the provided plan.")
	@POST
	@Path("/compile")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-write")
	public PlanCompilationResult compilePlan(Plan plan) {
		@SuppressWarnings("unchecked")
		PlanType planType = (PlanType) planTypeRegistry.getPlanType(plan.getClass());
		PlanCompiler planCompiler = planType.getPlanCompiler();
		PlanCompilationResult planCompilationResult = new PlanCompilationResult();
		try {
			plan = planCompiler.compile(plan);
			planCompilationResult.setPlan(plan);
		} catch(PlanCompilerException e) {
			planCompilationResult.setHasError(true);
			planCompilationResult.setErrors(e.getErrors());
		}
		return planCompilationResult;
	}

	@Override
	protected Plan cloneEntity(Plan plan) {
		// Delegate clone to plan type manager
		@SuppressWarnings("unchecked")
		PlanType planType = (PlanType) planTypeRegistry.getPlanType(plan.getClass());
		Plan clonePlan = planType.clonePlan(plan, true);
		assignNewId(clonePlan.getRoot());
		return clonePlan;
	}

	@Operation(description = "Returns the first plan matching the given attributes.")
	@POST
	@Path("/search")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-read")
	public Plan getPlanByAttributes(Map attributes) {
		return planAccessor.findByAttributes(attributes);
	}

	@Operation(description = "Returns all the plans.")
	@GET
	@Path("/all")
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-read")
	public List getAllPlans(@QueryParam("skip") Integer skip, @QueryParam("limit") Integer limit) {
		if(skip != null && limit != null) {
			return planAccessor.getRange(skip, limit);
		} else {
			return getAllPlans(0, 1000);
		}
	}

	@Operation(description = "Returns the plan referenced by the given artifact within the given plan.")
	@GET
	@Path("/{id}/artefacts/{artefactid}/lookup/plan")
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-read")
	public Plan lookupPlan(@PathParam("id") String id, @PathParam("artefactid") String artefactId) {
		Plan plan = get(id);
		Plan result = null;
		PlanNavigator planNavigator = new PlanNavigator(plan);
		CallPlan artefact = (CallPlan) planNavigator.findArtefactById(artefactId);
		DynamicJsonObjectResolver dynamicJsonObjectResolver = new DynamicJsonObjectResolver(new DynamicJsonValueResolver(getContext().getExpressionHandler()));
		SelectorHelper selectorHelper = new SelectorHelper(dynamicJsonObjectResolver);
		PlanLocator planLocator = new PlanLocator(getContext().getPlanAccessor(), selectorHelper);
		ObjectPredicate objectPredicate = objectPredicateFactory.getObjectPredicate(getSession());
		try {
			result = planLocator.selectPlan(artefact, objectPredicate, null);
		} catch (RuntimeException e) {}
		return result;
	}

	@Operation(description = "Returns the plan referenced by the given CallPlan.")
	@POST
	@Path("/lookup")
	@Produces(MediaType.APPLICATION_JSON)
	@Consumes(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-read")
	public Plan lookupCallPlan(CallPlan callPlan) {
		Plan result = null;
		DynamicJsonObjectResolver dynamicJsonObjectResolver = new DynamicJsonObjectResolver(new DynamicJsonValueResolver(getContext().getExpressionHandler()));
		SelectorHelper selectorHelper = new SelectorHelper(dynamicJsonObjectResolver);
		PlanLocator planLocator = new PlanLocator(getContext().getPlanAccessor(), selectorHelper);
		ObjectPredicate objectPredicate = objectPredicateFactory.getObjectPredicate(getSession());
		try {
			result = planLocator.selectPlan(callPlan, objectPredicate, null);
		} catch (RuntimeException e) {}
		return result;
	}

	@Operation(description = "Clones the provided artefact.")
	@POST
	@Path("/artefacts/clone")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-write")
	public AbstractArtefact cloneArtefact(AbstractArtefact artefact) {
		assignNewId(artefact);
		return artefact;
	}

	@Operation(description = "Clones the provided artefacts.")
	@POST
	@Path("/artefacts/clonemany")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-write")
	public List cloneArtefacts(List artefacts) {
		return artefacts.stream().map(this::cloneArtefact).collect(Collectors.toList());
	}

	@Operation(description = "Returns the list of artefact types that can be used as control within Plans")
	@GET
	@Path("/artefact/types")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-read")
	public Set getArtefactTypes() {
		return artefactHandlerRegistry.getControlArtefactNames();
	}

	@Operation(description = "Returns the artefact with the given id.")
	@GET
	@Path("/artefact/types/{id}")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-read")
	public AbstractArtefact getArtefactType(@PathParam("id") String type) throws Exception {
		return artefactHandlerRegistry.getArtefactTypeInstance(type);
	}

	@Operation(description = "Returns the list of artefact types that can be used as root element of Plans.")
	@GET
	@Path("/artefact/templates")
	@Consumes(MediaType.APPLICATION_JSON)
	@Produces(MediaType.APPLICATION_JSON)
	@Secured(right="{entity}-read")
	public Set getArtefactTemplates() {
		return new TreeSet<>(artefactHandlerRegistry.getRootArtefactNames());
	}


	@Operation(description = "Returns the plan in yaml format.")
	@GET
	@Path("/{id}/yaml")
	@Produces(MediaType.TEXT_PLAIN)
	@Secured(right="{entity}-read")
	public Response getYamlPlan(@PathParam("id") String id) {
		Plan plan = planAccessor.get(id);
		StreamingOutput fileStream = new StreamingOutput() {
			@Override
			public void write(java.io.OutputStream output) {
				try {
					yamlPlanReader.writeYamlPlan(output, plan);
				} catch (Exception ex) {
					log.error("Serialization error", ex);
					throw new ControllerServiceException("Serialization error when converting to YAML plan, check the controller logs for more details", ex);
				}
			}
		};

		String mimeType = "text/plain; charset=utf-8";
		return Response.ok(fileStream, mimeType).build();

	}

	private void assignNewId(AbstractArtefact artefact) {
		artefact.setId(new ObjectId());
		artefact.getChildren().forEach(this::assignNewId);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy