added newVersion endpoint for TplTestStep and TplTest

Signed-off-by: Alexander Nehmer <a.nehmer@science-computing.de>
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ResourceConstants.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ResourceConstants.java
index 8ad824f..b6d8b4a 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ResourceConstants.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/ResourceConstants.java
@@ -50,6 +50,11 @@
 	public static final String REQUESTPARAM_ID = "ID";
 
 	/**
+	 * Parameter holding the {@link Entity}s name in the URI path
+	 */
+	public static final String REQUESTPARAM_NAME = "NAME";
+
+	/**
 	 * Parameter holding an additional {@link Entity}s id in the URI path, e.g. for
 	 * a {@link TemplateComponent} when {@link REQUESTPARAM_ID} holds the id of the
 	 * {@link TemplateRoot}.
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateRootResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateRootResource.java
index 2d02130..9f86259 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateRootResource.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateRootResource.java
@@ -41,7 +41,7 @@
 import org.eclipse.mdm.businessobjects.entity.MDMEntityAction;
 import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
 import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
-import org.eclipse.mdm.businessobjects.service.TemplateRootService;
+import org.eclipse.mdm.businessobjects.service.TemplateService;
 import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
 
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -57,7 +57,7 @@
 public class TemplateRootResource {
 
 	@EJB
-	private TemplateRootService tplRootService;
+	private TemplateService tplService;
 
 	/**
 	 * Returns the found {@link TemplateRoot}.
@@ -73,7 +73,7 @@
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id) {
-		return tplRootService
+		return tplService
 				.find(sourceName, TemplateRoot.class, id, ServiceUtils.getContextTypeSupplier(contextTypeParam))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
@@ -92,7 +92,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @QueryParam("filter") String filter) {
-		return tplRootService
+		return tplService
 				.findAll(sourceName, TemplateRoot.class, filter, ServiceUtils.getContextTypeSupplier(contextTypeParam))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
@@ -110,7 +110,7 @@
 	@Consumes(MediaType.APPLICATION_JSON)
 	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, MDMEntityResponse body) {
-		return tplRootService
+		return tplService
 				.create(sourceName, TemplateRoot.class,
 						L(ServiceUtils.getContextTypeSupplier(contextTypeParam),
 								decompose(body::getData).<MDMEntity>getAt(0).get(MDMEntity::getName)))
@@ -135,9 +135,9 @@
 	public Response patch(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id,
 			MDMEntityResponse body) {
-		return tplRootService
+		return tplService
 				.update(sourceName,
-						tplRootService.find(sourceName, TemplateRoot.class, id,
+						tplService.find(sourceName, TemplateRoot.class, id,
 								ServiceUtils.getContextTypeSupplier(contextTypeParam)),
 						decompose(body::getData).<MDMEntity>getValueAt(0))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
@@ -156,9 +156,9 @@
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@PathParam(REQUESTPARAM_CONTEXTTYPE) String contextTypeParam, @PathParam(REQUESTPARAM_ID) String id) {
-		return tplRootService
+		return tplService
 				.delete(sourceName,
-						tplRootService.find(sourceName, TemplateRoot.class, id,
+						tplService.find(sourceName, TemplateRoot.class, id,
 								ServiceUtils.getContextTypeSupplier(contextTypeParam)))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
@@ -187,7 +187,7 @@
 					.entity(String.format("Given action '%s' is not supported", entityAction.getType().name())).build();
 		}
 
-		return tplRootService.createNewVersion(sourceName, ServiceUtils.getContextTypeSupplier(contextTypeParam), id)
+		return tplService.createTplRootVersion(sourceName, ServiceUtils.getContextTypeSupplier(contextTypeParam), id)
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
@@ -204,7 +204,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("/searchattributes")
 	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
-		return ServiceUtils.buildSearchAttributesResponse(sourceName, TemplateRoot.class, tplRootService);
+		return ServiceUtils.buildSearchAttributesResponse(sourceName, TemplateRoot.class, tplService);
 	}
 
 	/**
@@ -218,6 +218,6 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("/localizations")
 	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
-		return ServiceUtils.buildLocalizationResponse(sourceName, TemplateRoot.class, tplRootService);
+		return ServiceUtils.buildLocalizationResponse(sourceName, TemplateRoot.class, tplService);
 	}
 }
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestResource.java
index 9a26798..b82debd 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestResource.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestResource.java
@@ -15,6 +15,7 @@
 package org.eclipse.mdm.businessobjects.boundary;
 
 import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_NAME;
 import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
 import static org.eclipse.mdm.businessobjects.utils.Decomposer.decompose;
 import static org.eclipse.mdm.businessobjects.utils.ServiceUtils.L;
@@ -33,11 +34,13 @@
 import javax.ws.rs.core.Response.Status;
 
 import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.TemplateRoot;
 import org.eclipse.mdm.api.dflt.model.TemplateTest;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStep;
 import org.eclipse.mdm.businessobjects.entity.MDMEntity;
 import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
 import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
-import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.service.TemplateService;
 import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
 
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -53,7 +56,7 @@
 public class TemplateTestResource {
 
 	@EJB
-	private EntityService entityService;
+	private TemplateService tplService;
 
 	/**
 	 * Returns the found {@link TemplateTest}.
@@ -66,7 +69,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id) {
-		return entityService.find(sourceName, TemplateTest.class, id)
+		return tplService.find(sourceName, TemplateTest.class, id)
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
@@ -83,7 +86,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@QueryParam("filter") String filter) {
-		return entityService.findAll(sourceName, TemplateTest.class, filter)
+		return tplService.findAll(sourceName, TemplateTest.class, filter)
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
@@ -99,7 +102,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Consumes(MediaType.APPLICATION_JSON)
 	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, MDMEntityResponse body) {
-		return entityService
+		return tplService
 				.create(sourceName, TemplateTest.class,
 						L(decompose(body::getData).<MDMEntity>getAt(0).get(MDMEntity::getName)))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED))
@@ -122,8 +125,7 @@
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response patch(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
 			MDMEntityResponse body) {
-		return entityService
-				.update(sourceName, entityService.find(sourceName, TemplateTest.class, id),
+		return tplService.update(sourceName, tplService.find(sourceName, TemplateTest.class, id),
 						decompose(body::getData).<MDMEntity>getValueAt(0))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
@@ -131,6 +133,27 @@
 	}
 
 	/**
+	 * Creates new version of given {@link TemplateTestStep}.
+	 * 
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @param id         the identifier of the {@link TemplateRoot} to create new
+	 *                   version of
+	 * @return a new version of given {@link TemplateTestStep}
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_NAME + "}/versions")
+	public Response createNewVersion(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_NAME) String name) {
+
+		return tplService.createTplTestVersion(sourceName, name)
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED)) //
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
 	 * Deletes and returns the deleted {@link TemplateTest}.
 	 * 
 	 * @param id The identifier of the {@link TemplateTest} to delete.
@@ -141,7 +164,7 @@
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@PathParam(REQUESTPARAM_ID) String id) {
-		return entityService.delete(sourceName, entityService.find(sourceName, TemplateTest.class, id))
+		return tplService.delete(sourceName, tplService.find(sourceName, TemplateTest.class, id))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
@@ -157,7 +180,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("/searchattributes")
 	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
-		return ServiceUtils.buildSearchAttributesResponse(sourceName, TemplateTest.class, entityService);
+		return ServiceUtils.buildSearchAttributesResponse(sourceName, TemplateTest.class, tplService);
 	}
 
 	/**
@@ -170,6 +193,6 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("/localizations")
 	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
-		return ServiceUtils.buildLocalizationResponse(sourceName, TemplateTest.class, entityService);
+		return ServiceUtils.buildLocalizationResponse(sourceName, TemplateTest.class, tplService);
 	}
 }
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepResource.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepResource.java
index 413e10d..664d566 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepResource.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/boundary/TemplateTestStepResource.java
@@ -15,6 +15,7 @@
 package org.eclipse.mdm.businessobjects.boundary;
 
 import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_ID;
+import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_NAME;
 import static org.eclipse.mdm.businessobjects.boundary.ResourceConstants.REQUESTPARAM_SOURCENAME;
 import static org.eclipse.mdm.businessobjects.utils.Decomposer.decompose;
 import static org.eclipse.mdm.businessobjects.utils.ServiceUtils.L;
@@ -34,11 +35,12 @@
 
 import org.eclipse.mdm.api.base.model.ContextType;
 import org.eclipse.mdm.api.base.model.Environment;
+import org.eclipse.mdm.api.dflt.model.TemplateRoot;
 import org.eclipse.mdm.api.dflt.model.TemplateTestStep;
 import org.eclipse.mdm.businessobjects.entity.MDMEntity;
 import org.eclipse.mdm.businessobjects.entity.MDMEntityResponse;
 import org.eclipse.mdm.businessobjects.entity.SearchAttribute;
-import org.eclipse.mdm.businessobjects.service.EntityService;
+import org.eclipse.mdm.businessobjects.service.TemplateService;
 import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
 
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -54,7 +56,7 @@
 public class TemplateTestStepResource {
 
 	@EJB
-	private EntityService entityService;
+	private TemplateService tplService;
 
 	/**
 	 * Returns the found {@link TemplateTestStep}.
@@ -69,7 +71,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response find(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id) {
-		return entityService.find(sourceName, TemplateTestStep.class, id)
+		return tplService.find(sourceName, TemplateTestStep.class, id)
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
@@ -89,7 +91,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response findAll(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@QueryParam("filter") String filter) {
-		return entityService.findAll(sourceName, TemplateTestStep.class, filter)
+		return tplService.findAll(sourceName, TemplateTestStep.class, filter)
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
@@ -105,7 +107,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Consumes(MediaType.APPLICATION_JSON)
 	public Response create(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, MDMEntityResponse body) {
-		return entityService
+		return tplService
 				.create(sourceName, TemplateTestStep.class,
 						L(decompose(body::getData).<MDMEntity>getAt(0).get(MDMEntity::getName)))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED)) //
@@ -128,8 +130,7 @@
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response patch(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName, @PathParam(REQUESTPARAM_ID) String id,
 			MDMEntityResponse body) {
-		return entityService
-				.update(sourceName, entityService.find(sourceName, TemplateTestStep.class, id),
+		return tplService.update(sourceName, tplService.find(sourceName, TemplateTestStep.class, id),
 						decompose(body::getData).<MDMEntity>getValueAt(0))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
@@ -137,6 +138,27 @@
 	}
 
 	/**
+	 * Creates new version of given {@link TemplateTestStep}.
+	 * 
+	 * @param sourceName name of the source (MDM {@link Environment} name)
+	 * @param id         the identifier of the {@link TemplateRoot} to create new
+	 *                   version of
+	 * @return a new version of given {@link TemplateTestStep}
+	 */
+	@POST
+	@Produces(MediaType.APPLICATION_JSON)
+	@Consumes(MediaType.APPLICATION_JSON)
+	@Path("/{" + REQUESTPARAM_NAME + "}/versions")
+	public Response createNewVersion(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
+			@PathParam(REQUESTPARAM_NAME) String name) {
+
+		return tplService.createTplTestStepVersion(sourceName, name)
+				.map(e -> ServiceUtils.buildEntityResponse(e, Status.CREATED)) //
+				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
+				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
+	}
+
+	/**
 	 * Deletes and returns the deleted {@link TemplateTestStep}.
 	 * 
 	 * @param id The identifier of the {@link TemplateTestStep} to delete.
@@ -147,7 +169,7 @@
 	@Path("/{" + REQUESTPARAM_ID + "}")
 	public Response delete(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName,
 			@PathParam(REQUESTPARAM_ID) String id) {
-		return entityService.delete(sourceName, entityService.find(sourceName, TemplateTestStep.class, id))
+		return tplService.delete(sourceName, tplService.find(sourceName, TemplateTestStep.class, id))
 				.map(e -> ServiceUtils.buildEntityResponse(e, Status.OK)) //
 				.recover(ServiceUtils.ERROR_RESPONSE_SUPPLIER) //
 				.getOrElse(ServiceUtils.SERVER_ERROR_RESPONSE);
@@ -163,7 +185,7 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("/searchattributes")
 	public Response getSearchAttributes(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
-		return ServiceUtils.buildSearchAttributesResponse(sourceName, TemplateTestStep.class, entityService);
+		return ServiceUtils.buildSearchAttributesResponse(sourceName, TemplateTestStep.class, tplService);
 	}
 
 	/**
@@ -176,6 +198,6 @@
 	@Produces(MediaType.APPLICATION_JSON)
 	@Path("/localizations")
 	public Response localize(@PathParam(REQUESTPARAM_SOURCENAME) String sourceName) {
-		return ServiceUtils.buildLocalizationResponse(sourceName, TemplateTestStep.class, entityService);
+		return ServiceUtils.buildLocalizationResponse(sourceName, TemplateTestStep.class, tplService);
 	}
 }
\ No newline at end of file
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/TemplateRootService.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/TemplateService.java
similarity index 61%
rename from org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/TemplateRootService.java
rename to org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/TemplateService.java
index d06c762..a218aca 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/TemplateRootService.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/service/TemplateService.java
@@ -34,6 +34,11 @@
 import org.eclipse.mdm.api.dflt.model.TemplateComponent;
 import org.eclipse.mdm.api.dflt.model.TemplateRoot;
 import org.eclipse.mdm.api.dflt.model.TemplateSensor;
+import org.eclipse.mdm.api.dflt.model.TemplateTest;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStep;
+import org.eclipse.mdm.api.dflt.model.TemplateTestStepUsage;
+import org.eclipse.mdm.businessobjects.utils.EntityNotFoundException;
+import org.eclipse.mdm.businessobjects.utils.EntityNotFoundException.Type;
 
 import io.vavr.Tuple;
 import io.vavr.Value;
@@ -43,28 +48,129 @@
  * {@link TemplateRoot} service handling corresponding operations
  * 
  * @author Philipp Schweinbenz, science+computing AG Tuebingen (Atos SE)
+ * @author Alexander Nehmer, science+computing AG Tuebingen (Atos SE)
  *
  */
 @Stateless
-public class TemplateRootService extends EntityService {
+public class TemplateService extends EntityService {
 
 	/**
-	 * Create new version of {@link TemplateRoot} with given id
+	 * Create new version of {@link TemplateTest} with given name. The link linked
+	 * {@link TemplateTestStep}s are copied.
+	 * 
+	 * @param sourceNameSupplier a {@link Value} with the source name (MDM
+	 *                           {@link Environment} name)
+	 * @param name               name of entity to create new version of
+	 * @return new version of the {@link TemplateTestStep} with the given name
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Entity> Try<T> createTplTestVersion(String sourceName, String name) {
+		return Try.of(() -> {
+			List<TemplateTest> tplTests = getEntityManager(sourceName)
+					.mapTry(em -> em.loadAll(TemplateTest.class, name)).get();
+			if (tplTests.isEmpty()) {
+				throw new EntityNotFoundException(TemplateTestStep.class, name, Type.NAME);
+			}
+
+			ApplicationContext appContext = connectorService.getContextByName(sourceName);
+			EntityFactory entityFactory = appContext.getEntityFactory().get();
+			EntityManager entityManager = appContext.getEntityManager().get();
+
+			// get latest version
+			Comparator<TemplateTest> versionComparator = Comparator.comparing(TemplateTest::getVersion);
+			TemplateTest latestTplTest = tplTests.stream().max(versionComparator).get();
+
+			TemplateTest newTplTest = entityFactory.createTemplateTest(latestTplTest.getName());
+			newTplTest.setVersion(latestTplTest.getVersion() + 1);
+
+			Set<Entity> entities = (Set<Entity>) DataAccessHelper.execute(entityManager,
+					Tuple.of(newTplTest, DataAccessHelper.CREATE));
+
+			// set TplTestSteps as in latestVersion
+			for (TemplateTestStepUsage tplTestStepUsage : latestTplTest.getTemplateTestStepUsages()) {
+				TemplateTestStepUsage newUsage = entityFactory.createTemplateTestStepUsage(tplTestStepUsage.getName(),
+						newTplTest, tplTestStepUsage.getTemplateTestStep());
+				DataAccessHelper.execute(entityManager, Tuple.of(newUsage, DataAccessHelper.CREATE));
+			}
+
+			return (T) entities.toArray()[0];
+		});
+	}
+
+	/**
+	 * Create new version of {@link TemplateTestStep} with given name. The linked
+	 * TplRoots are copied from the given {@link TemplateTestStep}
+	 * 
+	 * @param sourceNameSupplier a {@link Value} with the source name (MDM
+	 *                           {@link Environment} name)
+	 * @param name               name of entity to create new version of
+	 * @return new version of the {@link TemplateTestStep} with the given name
+	 */
+	@SuppressWarnings("unchecked")
+	public <T extends Entity> Try<T> createTplTestStepVersion(String sourceName, String name) {
+		return Try.of(() -> {
+			List<TemplateTestStep> tplTestSteps = getEntityManager(sourceName)
+					.mapTry(em -> em.loadAll(TemplateTestStep.class, name)).get();
+			if (tplTestSteps.isEmpty()) {
+				throw new EntityNotFoundException(TemplateTestStep.class, name, Type.NAME);
+			}
+			TemplateTestStep givenTplTestStep = tplTestSteps.get(0);
+			ApplicationContext appContext = connectorService.getContextByName(sourceName);
+			TemplateTestStep newTplTestStep = copyTplTestStep(appContext.getEntityFactory().get(),
+					appContext.getEntityManager().get(), givenTplTestStep);
+			Set<Entity> entities = (Set<Entity>) DataAccessHelper.execute(getEntityManager(sourceName).get(),
+					Tuple.of(newTplTestStep, DataAccessHelper.CREATE));
+
+			return (T) entities.toArray()[0];
+		});
+	}
+
+	/**
+	 * Create a copy of a given {@link TemplateTestStep} and set version to latest +
+	 * 1
+	 * 
+	 * @param entityFactory    {@link EntityFactory} to create new
+	 *                         {@link TemplateTestStep}
+	 * @param entityManager    {@link EntityManager} to get last version of given
+	 *                         {@link TemplateTestStep}
+	 * @param givenTplTestStep {@link TemplateTestStep} to create new version of
+	 * @return copy of the given {@link TemplateTestStep}
+	 */
+	private TemplateTestStep copyTplTestStep(EntityFactory entityFactory, EntityManager entityManager,
+			TemplateTestStep givenTplTestStep) {
+
+		TemplateTestStep newTemplateTestStep = entityFactory.createTemplateTestStep(givenTplTestStep.getName());
+		Comparator<TemplateTestStep> versionComparator = Comparator.comparing(TemplateTestStep::getVersion);
+		Optional<Integer> latestVersion = entityManager.loadAll(TemplateTestStep.class, newTemplateTestStep.getName())
+				.stream().max(versionComparator).map(TemplateTestStep::getVersion);
+		if (latestVersion.isPresent()) {
+			newTemplateTestStep.setVersion(latestVersion.get() + 1);
+		}
+
+		for (TemplateRoot tplRoot : givenTplTestStep.getTemplateRoots()) {
+			newTemplateTestStep.setTemplateRoot(tplRoot);
+		}
+
+		return newTemplateTestStep;
+	}
+
+	/**
+	 * * Create new version of {@link TemplateRoot} with given id
 	 * 
 	 * @param sourceNameSupplier  a {@link Value} with the source name (MDM
 	 *                            {@link Environment} name)
 	 * @param contextTypeSupplier a {@link Value} with the contextType of entity to
 	 *                            create new version of.
 	 * @param id                  id of entity to create new version of
-	 * @return
+	 * @return new version of the {@link TemplateRoot} with the given id
 	 */
 	@SuppressWarnings("unchecked")
-	public <T extends Entity> Try<T> createNewVersion(String sourceName, Value<ContextType> contextTypeSupplier,
+	public <T extends Entity> Try<T> createTplRootVersion(String sourceName, Value<ContextType> contextTypeSupplier,
 			String id) {
 		return Try.of(() -> {
 			TemplateRoot givenTplRoot = find(sourceName, TemplateRoot.class, id, contextTypeSupplier).get();
 			ApplicationContext appContext = connectorService.getContextByName(sourceName);
-			TemplateRoot newTplRoot = createNewTplRootVersion(appContext.getEntityFactory().get(),
+			TemplateRoot newTplRoot = copyTplRoot(appContext.getEntityFactory().get(),
 					appContext.getEntityManager().get(), givenTplRoot);
 			Set<Entity> entities = (Set<Entity>) DataAccessHelper.execute(getEntityManager(sourceName).get(),
 					Tuple.of(newTplRoot, DataAccessHelper.CREATE));
@@ -74,7 +180,7 @@
 	}
 
 	/**
-	 * Create new version of given {@link TemplateRoot}
+	 * Create a copy of a given {@link TemplateRoot} and set version to latest + 1
 	 * 
 	 * @param entityFactory {@link EntityFactory} to create new {@link TemplateRoot}
 	 * @param entityManager {@link EntityManager} to get last version of given
@@ -82,7 +188,7 @@
 	 * @param givenTplRoot  {@link TemplateRoot} to create new version of
 	 * @return
 	 */
-	private TemplateRoot createNewTplRootVersion(EntityFactory entityFactory, EntityManager entityManager,
+	private TemplateRoot copyTplRoot(EntityFactory entityFactory, EntityManager entityManager,
 			TemplateRoot givenTplRoot) {
 		TemplateRoot newTemplateRoot = entityFactory.createTemplateRoot(givenTplRoot.getContextType(),
 				givenTplRoot.getName());
@@ -112,24 +218,6 @@
 	}
 
 	/**
-	 * Copy given {@link TemplateAttribute}s into given {@link TemplateComponent}
-	 * 
-	 * @param entityFactory {@link EntityFactory} to create new entities
-	 * @param tplComp       {@link TemplateComponent} into given
-	 *                      {@link TemplateAttribute}s shall be copied
-	 * @param givenTplAttrs {@link TemplateAttribute}s which shall be copied
-	 */
-	private void copyTplAttrs(EntityFactory entityFactory, TemplateComponent tplComp,
-			List<TemplateAttribute> givenTplAttrs) {
-		for (TemplateAttribute tplAttr : givenTplAttrs) {
-			TemplateAttribute newTplAttr = tplComp.getTemplateAttributes().stream()
-					.filter(ta -> ta.getName().equals(tplAttr.getName())).findFirst()
-					.orElseGet(() -> entityFactory.createTemplateAttribute(tplAttr.getName(), tplComp));
-			mergeEntities(tplAttr, newTplAttr, Entity.ATTR_NAME, Entity.ATTR_MIMETYPE);
-		}
-	}
-
-	/**
 	 * Copy given child {@link TemplateComponent}s into given
 	 * {@link TemplateComponent}
 	 * 
@@ -153,6 +241,24 @@
 	}
 
 	/**
+	 * Copy given {@link TemplateAttribute}s into given {@link TemplateComponent}
+	 * 
+	 * @param entityFactory {@link EntityFactory} to create new entities
+	 * @param tplComp       {@link TemplateComponent} into given
+	 *                      {@link TemplateAttribute}s shall be copied
+	 * @param givenTplAttrs {@link TemplateAttribute}s which shall be copied
+	 */
+	private void copyTplAttrs(EntityFactory entityFactory, TemplateComponent tplComp,
+			List<TemplateAttribute> givenTplAttrs) {
+		for (TemplateAttribute tplAttr : givenTplAttrs) {
+			TemplateAttribute newTplAttr = tplComp.getTemplateAttributes().stream()
+					.filter(ta -> ta.getName().equals(tplAttr.getName())).findFirst()
+					.orElseGet(() -> entityFactory.createTemplateAttribute(tplAttr.getName(), tplComp));
+			mergeEntities(tplAttr, newTplAttr, Entity.ATTR_NAME, Entity.ATTR_MIMETYPE);
+		}
+	}
+
+	/**
 	 * Copy given {@link TemplateSeonsor}s into given {@link TemplateComponent}
 	 * 
 	 * @param entityFactory   {@link EntityFactory} to create new entities
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/EntityNotFoundException.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/EntityNotFoundException.java
index 5a5ca75..7ec5115 100755
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/EntityNotFoundException.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/utils/EntityNotFoundException.java
@@ -26,6 +26,10 @@
 

 	private static final long serialVersionUID = 6862157710262117670L;

 

+	public enum Type {

+		ID, NAME;

+	}

+

 	/**

 	 * Default constructor with causing exception

 	 * 

@@ -41,9 +45,21 @@
 	 * Default constructor without causing exception

 	 * 

 	 * @param entityClass Class of entity that could not be found

+	 * @param identifier  identifier of entity (either {@link Type.id} or

+	 *                    {@link Type.name} that could not be found

+	 * @param type        type of identifier

+	 */

+	public EntityNotFoundException(Class<? extends Entity> entityClass, String identifier, Type type) {

+		super(entityClass.getSimpleName() + " with " + type.toString() + " " + identifier + " not found.");

+	}

+

+	/**

+	 * Default constructor without causing exception

+	 * 

+	 * @param entityClass Class of entity that could not be found

 	 * @param id          id of entity that could not be found

 	 */

 	public EntityNotFoundException(Class<? extends Entity> entityClass, String id) {

-		super(entityClass.getSimpleName() + " with ID " + id + " not found.");

+		this(entityClass, id, Type.ID);

 	}

 }