package com.temenos.interaction.sdk.rimdsl;
/*
* #%L
* interaction-sdk
* %%
* Copyright (C) 2012 - 2013 Temenos Holdings N.V.
* %%
* This program 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.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.log.NullLogSystem;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.junit.Assert;
import org.junit.Test;
import com.google.common.io.CharStreams;
import com.temenos.interaction.core.entity.Metadata;
import com.temenos.interaction.core.entity.MetadataParser;
import com.temenos.interaction.sdk.JPAResponderGen;
import com.temenos.interaction.sdk.command.Commands;
import com.temenos.interaction.sdk.command.Parameter;
import com.temenos.interaction.sdk.interaction.state.IMEntityState;
import com.temenos.interaction.sdk.interaction.state.IMPseudoState;
import com.temenos.interaction.sdk.interaction.state.IMState;
import com.temenos.interaction.sdk.interaction.IMResourceStateMachine;
import com.temenos.interaction.sdk.interaction.InteractionModel;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.junit.Ignore;
import org.odata4j.edm.EdmDataServices;
/**
* Unit test for {@link RimDslGenerator}.
*/
public class TestRimDslGenerator {
private final static String RIM_LINE_SEP = System.getProperty("line.separator");
public final static String METADATA_AIRLINE_XML_FILE = "AirlinesMetadata.xml";
public final static String RIM_DSL_AIRLINE_SIMPLE_FILE = "AirlinesSimple.rim";
public final static String RIM_DSL_AIRLINE_FILE = "Airlines.rim";
public final static String RIM_DSL_AIRLINE_NON_STRICT_ODATA_FILE = "AirlinesNonStrictOData.rim";
public final static String METADATA_BANKING_XML_FILE = "Banking.xml";
public final static String RIM_DSL_BANKING_FILE = "Banking.rim";
public final static Parameter COMMAND_SERVICE_DOCUMENT = new Parameter("ServiceDocument", false, "");
public final static Parameter COMMAND_EDM_DATA_SERVICES = new Parameter("edmDataServices", true, "");
public final static Parameter COMMAND_METADATA = new Parameter("Metadata", false, "");
public final static Parameter COMMAND_METADATA_SOURCE_ODATAPRODUCER = new Parameter("producer", true, "odataProducer");
public final static Parameter COMMAND_METADATA_SOURCE_MODEL = new Parameter("edmMetadata", true, "edmMetadata");
@Test
public void testGenerateRimDslAirlinesSimple() {
//Define the basic interaction model based on the available metadata
Metadata metadata = parseMetadata(METADATA_AIRLINE_XML_FILE);
InteractionModel interactionModel = new SortedInteractionModel(metadata);
//Add transitions
IMResourceStateMachine rsmFlightSchedule = interactionModel.findResourceStateMachine("FlightSchedule");
IMResourceStateMachine rsmAirport = interactionModel.findResourceStateMachine("Airport");
IMResourceStateMachine rsmFlight = interactionModel.findResourceStateMachine("Flight");
IMResourceStateMachine rsmPassenger = interactionModel.findResourceStateMachine("Passenger");
rsmFlightSchedule.addTransitionToEntityState("flightschedule", rsmAirport, "departureAirport", "departureAirportCode", null);
rsmFlightSchedule.addTransitionToEntityState("flightschedule", rsmAirport, "arrivalAirport", "arrivalAirportCode", null);
rsmFlightSchedule.addPseudoStateTransition("FlightSchedules", "created", "FlightSchedules", "POST", null, "CreateEntity", null, true);
rsmFlight.addPseudoStateTransition("Flights", "created", "Flights", "POST", null, "CreateEntity", null, true);
rsmAirport.addPseudoStateTransition("Airports", "created", "Airports", "POST", null, "CreateEntity", null, true);
rsmPassenger.addPseudoStateTransition("Passengers", "created", "Passengers", "POST", null, "CreateEntity", null, true);
//Run the generator
RimDslGenerator generator = new RimDslGenerator(createVelocityEngine());
String dsl = generator.generateRimDsl(interactionModel, JPAResponderGen.getDefaultCommands(), true);
//Check results
assertTrue(dsl != null && !dsl.equals(""));
assertEquals(stripEmptyLinesAndTrim(readTextFile(RIM_DSL_AIRLINE_SIMPLE_FILE)), stripEmptyLinesAndTrim(dsl));
}
@Test
public void testGenerateRimDslAirlines() {
InteractionModel interactionModel = createAirlineModelDSL(null);
//Run the generator
RimDslGenerator generator = new RimDslGenerator(createVelocityEngine());
String dsl = generator.generateRimDsl(interactionModel, JPAResponderGen.getDefaultCommands(), true);
//Check results
assertTrue(dsl != null && !dsl.equals(""));
assertEquals(stripEmptyLinesAndTrim(readTextFile(RIM_DSL_AIRLINE_FILE)), stripEmptyLinesAndTrim(dsl));
}
@Test
public void testGenerateRimDslMapAirlines() {
InteractionModel interactionModel = createAirlineModelDSL(null);
interactionModel.findResourceStateMachine("FlightSchedule").setRimName("Flight_Schedule");
interactionModel.findResourceStateMachine("Passenger").setRimName("Passenger");
//Run the generator
RimDslGenerator generator = new RimDslGenerator(createVelocityEngine());
// returns a map of specified resource state machine name and dsl
Map<String,String> dslMap = generator.generateRimDslMap(interactionModel, JPAResponderGen.getDefaultCommands(), true);
String rimDSL = null;
// check results for ServiceDocument
String root = interactionModel.getName();
rimDSL = dslMap.get(root);
rimDSL = stripEmptyLinesAndTrim(rimDSL);
assertTrue(rimDSL.contains("event GET"));
assertTrue(rimDSL.contains("initial resource ServiceDocument"));
assertTrue(rimDSL.contains("GET -> Flight_Schedule.FlightSchedules"));
assertFalse(rimDSL.contains("resource FlightSchedules"));
assertTrue(rimDSL.contains("GET -> Flights"));
assertTrue(rimDSL.contains("GET -> Passenger.Passengers"));
assertFalse(rimDSL.contains("resource Passengers"));
assertTrue(rimDSL.contains("GET *-> flightschedule_departureAirport {"));
// check results for FlightSchedules
rimDSL = dslMap.get("Flight_Schedule");
rimDSL = stripEmptyLinesAndTrim(rimDSL);
assertTrue(rimDSL.contains("rim Flight_Schedule {"));
assertTrue(rimDSL.contains("event GET"));
assertTrue(rimDSL.contains("resource FlightSchedules"));
assertTrue(rimDSL.contains(stripEmptyLinesAndTrim("GET *-> flightschedule {" + RIM_LINE_SEP
+ "\t\tparameters [ id=\"{flightScheduleID}\" ]" + RIM_LINE_SEP
+ "\t}")));
assertTrue(rimDSL.contains("resource flightschedule_departureAirport"));
assertTrue(rimDSL.contains("path: \"/FlightSchedules({id})/departureAirport\""));
// check results for Passengers
rimDSL = dslMap.get("Passenger");
assertTrue(rimDSL.contains("rim Passenger {"));
assertTrue(rimDSL.contains("resource Passengers"));
}
@Test
public void testGenerateRimDslMapAirlinesOnError() {
InteractionModel interactionModel = createAirlineModelDSL(null);
// flight defines an onerror
interactionModel.findResourceStateMachine("Flight").setRimName("Flight");
//Run the generator
RimDslGenerator generator = new RimDslGenerator(createVelocityEngine());
// returns a map of specified resource state machine name and dsl
Map<String,String> dslMap = generator.generateRimDslMap(interactionModel, JPAResponderGen.getDefaultCommands(), true);
// check results for Flight
String rimDSL = dslMap.get("Flight");
assertTrue(rimDSL.contains("rim Flight {"));
assertTrue(rimDSL.contains("onerror --> FlightResponder.ErrorMessages"));
}
@Test
public void testGenerateRimDslDomain() {
InteractionModel interactionModel = createAirlineModelDSL("airline");
//Run the generator
RimDslGenerator generator = new RimDslGenerator(createVelocityEngine());
String dsl = generator.generateRimDsl(interactionModel, JPAResponderGen.getDefaultCommands(), true);
//Check results
assertTrue(dsl != null && !dsl.equals(""));
assertTrue(dsl.contains("domain airline {"));
}
@Test
public void testGenerateRimDslAirlinesNonStrictOData() {
InteractionModel interactionModel = createAirlineModelDSL(null);
//Run the generator
RimDslGenerator generator = new RimDslGenerator(createVelocityEngine());
String dsl = generator.generateRimDsl(interactionModel, JPAResponderGen.getDefaultCommands(), false);
//Check results
assertTrue(dsl != null && !dsl.equals(""));
assertEquals(stripEmptyLinesAndTrim(readTextFile(RIM_DSL_AIRLINE_NON_STRICT_ODATA_FILE)), stripEmptyLinesAndTrim(dsl));
}
@Test
public void createBankingRimDsl() {
//Define the basic interaction model based on the available metadata
Metadata metadata = parseMetadata(METADATA_BANKING_XML_FILE);
InteractionModel interactionModel = new InteractionModel(metadata);
interactionModel.setBasepath("/{companyid}");
interactionModel.setExceptionState(new IMEntityState("InteractionException", "", Commands.GET_EXCEPTION));
IMState rsErrors = new IMEntityState("Errors", "", Commands.GET_NOOP);
interactionModel.addErrorHandlerState(rsErrors);
Commands commands = JPAResponderGen.getDefaultCommands();
commands.addCommand("AuthoriseEntity", "com.temenos.interaction.commands.odata.UpdateEntityCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER);
commands.addCommand("ReverseEntity", "com.temenos.interaction.commands.odata.UpdateEntityCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER);
commands.addCommand("GETReversedEntities", "com.temenos.interaction.commands.odata.GETEntitiesCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER);
commands.addCommand("GETReversedEntity", "com.temenos.interaction.commands.odata.GETEntityCommand", COMMAND_METADATA_SOURCE_ODATAPRODUCER);
commands.addRimEvent("AUTHORISE", "PUT");
commands.addRimEvent("REVERSE", "PUT");
//Add state transitions
IMResourceStateMachine rsm = interactionModel.findResourceStateMachine("Sector");
rsm.addCollectionAndEntityState("IAuth", "unauthorised input records");
rsm.addCollectionAndEntityState("Reversed", "reversed records", "GETReversedEntities", "GETReversedEntity");
IMPseudoState pseudoState = rsm.addPseudoStateTransition("Sectors", "input", "Sectors", "POST", null, "CreateEntity", null, true);
pseudoState.addAutoTransition(rsm.getResourceState("sector_IAuth"), "GET");
pseudoState.addAutoTransition(rsm.getResourceState("sector"), "GET");
pseudoState.setErrorHandlerState(rsErrors);
pseudoState = rsm.addPseudoStateTransition("sector_IAuth", "authorise", "PUT", "authorise", "AuthoriseEntity", "edit", false);
pseudoState.addAutoTransition(rsm.getResourceState("sector_IAuth"), "GET");
pseudoState.addAutoTransition(rsm.getResourceState("sector"), "GET");
pseudoState = rsm.addPseudoStateTransition("sector_IAuth", "delete", "DELETE", "delete", "DeleteEntity", "edit", false);
pseudoState.addAutoTransition(rsm.getResourceState("sector"), "GET");
pseudoState = rsm.addPseudoStateTransition("sector", "reverse", "sector_Reversed", "REVERSE", "reverse", "ReverseEntity", "edit", false);
pseudoState.addAutoTransition(rsm.getResourceState("sector_Reversed"), "GET");
//Run the generator
RimDslGenerator generator = new RimDslGenerator(createVelocityEngine());
String dsl = generator.generateRimDsl(interactionModel, commands, false);
assertEquals(stripEmptyLinesAndTrim(readTextFile(RIM_DSL_BANKING_FILE)), stripEmptyLinesAndTrim(dsl));
}
public InteractionModel createAirlineModelDSL(String domain) {
//Define the basic interaction model based on the available metadata
Metadata metadata = parseMetadata(METADATA_AIRLINE_XML_FILE);
InteractionModel interactionModel = new SortedInteractionModel(metadata);
interactionModel.setDomain(domain);
interactionModel.setExceptionState(new IMEntityState("InteractionException", "", Commands.GET_EXCEPTION));
IMState rsResponseErrors = new IMEntityState("ErrorMessages", "", Commands.GET_NOOP);
interactionModel.addErrorHandlerState(rsResponseErrors);
//Add transitions
IMResourceStateMachine rsmFlightSchedule = interactionModel.findResourceStateMachine("FlightSchedule");
IMResourceStateMachine rsmAirport = interactionModel.findResourceStateMachine("Airport");
IMResourceStateMachine rsmFlight = interactionModel.findResourceStateMachine("Flight");
IMResourceStateMachine rsmPassenger = interactionModel.findResourceStateMachine("Passenger");
rsmFlight.addTransitionToEntityState("flight", rsmFlightSchedule, "flightschedule", "flightScheduleNum", "flightschedule");
rsmFlightSchedule.addTransitionToEntityState("flightschedule", rsmAirport, "departureAirport", "departureAirportCode", "departureAirport");
rsmFlightSchedule.addTransitionToEntityState("flightschedule", rsmAirport, "arrivalAirport", "arrivalAirportCode", "arrivalAirport");
rsmPassenger.addTransitionToEntityState("passenger", rsmFlight, "flight", "flightID", "flight");
rsmAirport.addTransitionToCollectionState("airport", rsmFlightSchedule, "departures", "departureAirportCode eq '{code}'", "departureAirportCode", "departures");
rsmAirport.addTransitionToCollectionState("airport", rsmFlightSchedule, "arrivals", "arrivalAirportCode eq '{code}'", "arrivalAirportCode", "arrivals");
rsmFlightSchedule.addPseudoStateTransition("FlightSchedules", "created", "FlightSchedules", "POST", null, "CreateEntity", null, true);
rsmFlight.addPseudoStateTransition("Flights", "created", "Flights", "POST", null, "CreateEntity", null, true);
rsmAirport.addPseudoStateTransition("Airports", "created", "Airports", "POST", null, "CreateEntity", null, true);
rsmPassenger.addPseudoStateTransition("Passengers", "created", "Passengers", "POST", null, "CreateEntity", null, true);
//Add error handler
IMState rsflightCreated = rsmFlight.getPseudoState("Flights", "created");
rsflightCreated.setErrorHandlerState(rsResponseErrors);
return interactionModel;
}
/*
* Create a velocity engine which loads velocity
* templates from the classpath.
*/
@SuppressWarnings("deprecation")
private VelocityEngine createVelocityEngine() {
VelocityEngine ve = new VelocityEngine();
ve.setProperty(VelocityEngine.RESOURCE_LOADER, "classpath");
ve.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER + ".class", ClasspathResourceLoader.class.getName());
ve.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new NullLogSystem());
ve.init();
return ve;
}
/*
* Parse a metadata file
*/
private Metadata parseMetadata(String metadataFile) {
MetadataParser parser = new MetadataParser();
InputStream is = parser.getClass().getClassLoader().getResourceAsStream(metadataFile);
Metadata metadata = parser.parse(is);
Assert.assertNotNull(metadata);
return metadata;
}
/*
* Read a text file
*/
private String readTextFile(String textFile) {
InputStream is = null;
try {
is = this.getClass().getClassLoader().getResourceAsStream(textFile);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String read;
while ((read = br.readLine()) != null) {
sb.append(read).append(System.getProperty("line.separator"));
}
return sb.toString();
} catch (IOException ioe) {
fail(ioe.getMessage());
} finally {
IOUtils.closeQuietly(is);
}
// cannot get here as fail(ioe.getMessage()) will prevent it throwing exception
return null;
}
@Test
public void testGetRIM() {
//Parse the test metadata
MetadataParser parser = new MetadataParser();
InputStream is = parser.getClass().getClassLoader().getResourceAsStream(METADATA_AIRLINE_XML_FILE);
Metadata metadata = parser.parse(is);
Assert.assertNotNull(metadata);
//Define the interaction model
InteractionModel interactionModel = new InteractionModel(metadata);
IMResourceStateMachine rsmFlightSchedule = interactionModel.findResourceStateMachine("FlightSchedule");
IMResourceStateMachine rsmAirport = interactionModel.findResourceStateMachine("Airport");
IMResourceStateMachine rsmFlight = interactionModel.findResourceStateMachine("Flight");
IMResourceStateMachine rsmPassenger = interactionModel.findResourceStateMachine("Passenger");
rsmFlight.addTransitionToEntityState("flight", rsmFlightSchedule, "flightschedule", "flightScheduleNum", null);
rsmFlightSchedule.addTransitionToEntityState("flightschedule", rsmAirport, "departureAirport", "departureAirportCode", null);
rsmFlightSchedule.addTransitionToEntityState("flightschedule", rsmAirport, "arrivalAirport", "arrivalAirportCode", null);
rsmAirport.addTransitionToCollectionState("airport", rsmFlightSchedule, "departures", "departureAirportCode eq '{code}'", "departureAirportCode", null);
rsmAirport.addTransitionToCollectionState("airport", rsmFlightSchedule, "arrivals", "arrivalAirportCode eq '{code}'", "arrivalAirportCode", null);
rsmPassenger.addTransitionToEntityState("passenger", rsmFlight, "flight", "flightID", null);
//Run the generator
RimDslGenerator generator = new RimDslGenerator(createVelocityEngine());
Commands commands = JPAResponderGen.getDefaultCommands();
String rimDSL = null;
try {
InputStream isRimDsl = generator.getRIM(interactionModel, commands, true);
assertNotNull(isRimDsl);
rimDSL = CharStreams.toString(new InputStreamReader(isRimDsl, "UTF-8"));
}
catch(Exception age) {
fail(age.getMessage());
}
rimDSL = stripEmptyLinesAndTrim(rimDSL);
//Check the rim dsl
assertTrue(rimDSL.contains("initial resource ServiceDocument"));
assertTrue(rimDSL.contains("GET -> FlightSchedules"));
assertTrue(rimDSL.contains("resource FlightSchedules"));
assertTrue(rimDSL.contains(stripEmptyLinesAndTrim("GET *-> flightschedule {" + RIM_LINE_SEP
+ "\t\tparameters [ id=\"{flightScheduleID}\" ]" + RIM_LINE_SEP
+ "\t}")));
assertTrue(rimDSL.contains(stripEmptyLinesAndTrim("GET *-> flightschedule_departureAirport {" + RIM_LINE_SEP
+ "\t\tparameters [ id=\"{flightScheduleID}\" ]" + RIM_LINE_SEP
+ "\t}")));
assertTrue(rimDSL.contains("resource flightschedule_departureAirport"));
assertTrue(rimDSL.contains("path: \"/FlightSchedules({id})/departureAirport\""));
assertTrue(rimDSL.contains("GET -> Passengers"));
assertTrue(rimDSL.contains("resource Passengers"));
}
private String stripEmptyLinesAndTrim(String source) {
StringBuffer lines = new StringBuffer();
BufferedReader reader = new BufferedReader(new StringReader(source));
String line = null;
try {
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.equals("")) {
lines.append(line).append('\n');
}
}
} catch (IOException ex) {
// can not really happen in StringReader
}
try {
reader.close();
} catch (IOException ex) {
// ignored on purpose
}
return lines.toString();
}
public static class SortedInteractionModel extends InteractionModel {
public SortedInteractionModel(EdmDataServices edmds) {
super(edmds);
}
public SortedInteractionModel(Metadata metadata) {
super(metadata);
}
public SortedInteractionModel() {
super();
}
@Override
public List<IMResourceStateMachine> getResourceStateMachines() {
List<IMResourceStateMachine> list = new ArrayList(super.getResourceStateMachines());
Collections.sort(list, new Comparator<IMResourceStateMachine>() {
@Override
public int compare(IMResourceStateMachine t1, IMResourceStateMachine t2) {
return t1.getEntityState().getName().compareTo(t2.getEntityState().getName());
}
});
return list;
}
}
}