com.sabre.redapp.example3.desktop.template Sample
This example demonstrates how to add:
-
Callback structure to any provided command flow and to apply HTML template to the command response.
-
Call Java Service from JavaScript by registering service using Workflow Extension Point API with service id.
-
Open Details Container (UI Modal) and render form components inside the modal and define action buttons.
Pre-requisite:
Flow is already exposed by SR360 and allowed to be used by RedApp. In this example ’'Example ExtPoint’' is used.
Flow Details:
’'Example ExtPoint’' is a simple flow provided in SR360 for testing purposes. Find the the flow structure below.
This flow executes the callback service provided by RedApp and encloses the structured data in command response. Flow has id=’'com.sabre.edge.dynamo.extpoint_flow’' and exposed extension point has id=’'Example ExtPoint’'.
Development Steps:
-
Create a RedApp using development tools.
-
Create POJO class/classes with Jaxb mappings. This can be done in two ways as described below
-
Create POJO classes first and add JAXB Mapping later as shown in this example. Also create ObjectFactory.java class in the same package where POJO classes are present.
-
(or) If you have XSD which describes the structure, use XJC java tool to generate JAXB mappings. Refer to Generating JAXB classes from XSD.
Example code for Flight.javapackage com.sabre.redapp.example3.desktop.template.data; import java.io.Serializable; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; /** * Java class for Flight complex type. */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Flight", namespace = "http://redapp.sabre.com/example3/desktop/template/data", // (1) propOrder = {"airline", "number"}) @XmlRootElement(name = "Flight", namespace = "http://redapp.sabre.com/example3/desktop/template/data") // (1) public class Flight implements Serializable { private static final long serialVersionUID = 1L; @XmlElement(name = "airline", required = true) private String airline; @XmlElement(name = "number", required = true) private int number; /** * Gets the value of the airline property. * * @return possible object is {@link String } * */ public String getAirline() { return airline; } /** * Sets the value of the airline property. * * @param value allowed object is {@link String } * */ public void setAirline(String value) { this.airline = value; } /** * Gets the value of the number property. * * @return possible object is int * */ public int getNumber() { return number; } /** * Sets the value of the number property. * * @param value allowed object is int * */ public void setNumber(int value) { this.number = value; } }
-
Namespace is required in order to provide mapping information to JAXB transformer (Refer step 3.)
Example code for SampleFlights.javapackage com.sabre.redapp.example3.desktop.template.data; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; /** * Java class for SampleFlights complex type. */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "SampleFlights", namespace = "http://redapp.sabre.com/example3/desktop/template/data", // (1) propOrder = {"flights"}) @XmlRootElement(name = "SampleFlights", namespace = "http://redapp.sabre.com/example3/desktop/template/data") // (1) public class SampleFlights implements Serializable { private static final long serialVersionUID = 1L; @XmlElement(name = "flights", required = true) private List<Flight> flights; /** * Gets the value of the flights property. * * @return flight {@link Flight } * */ public List<Flight> getFlights() { if(flights == null) { flights = new ArrayList<>(); } return flights; } }
-
Namespace is required in order to provide mapping information to JAXB transformer (Refer step 3.)
-
-
Do not alter or change ObjectFactory.java file. Find the code for ObjectFactory.java below.
Example code for ObjectFactory.javapackage com.sabre.redapp.example3.desktop.template.data; import javax.xml.bind.annotation.XmlRegistry; /** * This object contains factory methods for each Java content interface and Java * element interface generated in the * com.sabre.redapp.example.web.template.data package. */ @XmlRegistry public class ObjectFactory { /** * Create a new ObjectFactory that can be used to create new instances of * schema derived classes for package: * com.sabre.redapp.example.workflow.extension.data * */ public ObjectFactory() { } /** * Create an instance of {@link Flight } * @return instance of {@link Flight } */ public Flight createFlight() { return new Flight(); } /** * Create an instance of {@link SampleFlights } * @return instance of {@link SampleFlights } */ public SampleFlights createSampleFlightStruct() { return new SampleFlights(); } }
-
-
Provide JAXB mapping to the Transformer which is responsible to convert Java object to JSON. Create transformer.properties file in src directory with package and namespace details as shown below.
File transformer.properties must contain valid package mapping – prefix pkg and valid namespace mapping – prefix ns. Both names must be the same and mustn’t contain characters outside of set [0-9a-ZA-Z_].
Example code for transformer.propertiespkg.com_sabre_redapp_example_desktop_template=com.sabre.redapp.example3.desktop.template.data// (1) ns.com_sabre_redapp_example_desktop_template=http://redapp.sabre.com/example3/desktop/template/data//(2)
-
Package name should be same where JAXB mapping classes are provided. Key should be in the format of key.<plugin-name>.
-
Namespace value should be same as the one that is provided in JAXB mapping classes. Key should be in the format of ns.<plugin-name>.
-
-
Create Flow Extension Callback Service to provide input data to Extension Point. It will need interface for the service and its implementation class. See the below sample code.
Example code for TemplateFlowExtPointService.javapackage com.sabre.redapp.example3.desktop.template.service; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointCommand; /** * Service displaying sample flights */ public interface TemplateFlowExtPointService { /** * Prepares data model to display. * * @param extPointCommand {@link FlowExtPointCommand} flow * @return {@link FlowExtPointCommand} flow */ FlowExtPointCommand execute(FlowExtPointCommand extPointCommand); }
Example code for TemplateFlowExtPointServiceImpl.javapackage com.sabre.redapp.example3.desktop.template.service.impl; import com.sabre.redapp.example3.web.template.data.Flight; import com.sabre.redapp.example3.web.template.data.SampleFlights; import com.sabre.redapp.example3.web.template.service.TemplateFlowExtPointService; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointCommand; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointDataOperation; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointResponse; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointResponseWrapper; /** * Service displaying sample flights */ public class TemplateFlowExtPointServiceImpl implements TemplateFlowExtPointService { /** * {@inheritDoc} **/ @Override public FlowExtPointCommand execute(FlowExtPointCommand flowExtPointCommand) { SampleFlights flights = new SampleFlights(); //(1) Flight flight1 = new Flight(); flight1.setAirline("Test Airline1"); flight1.setNumber(1234); flights.getFlights().add(flight1); Flight flight2 = new Flight(); flight2.setAirline("Test Airline2"); flight2.setNumber(3456); flights.getFlights().add(flight2); Flight flight3 = new Flight(); flight3.setAirline("Test Airline3"); flight3.setNumber(7890); flights.getFlights().add(flight3); FlowExtPointResponse response = new FlowExtPointResponse(); response.setStructure(flights); // (2) FlowExtPointResponseWrapper responseWrapper = new FlowExtPointResponseWrapper(); responseWrapper.setOperation(FlowExtPointDataOperation.ADD); responseWrapper.setResponse(response); // (3) flowExtPointCommand.getResponses().add(responseWrapper); // (4) return flowExtPointCommand; } }
-
Create custom data model object and fill it with data.
-
Add data model to response as structure.
-
Add response to wrapper response wrapper
-
Add wrapper to command responses
-
-
Create xml file with service definition. This file should be placed in OSG-INF directory.
This file must contain:
-
Unique name
-
Full path to class implementation
-
Full path to interface which will provide service functionalities.
Refer below for sample code.
See the below sample code for TemplateFlowExtService.xml<?xml version="1.0" encoding="UTF-8"?> <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.sabre.redapp.example3.desktop.template.service.TemplateFlowExtPointService"> <implementation class="com.sabre.redapp.example3.desktop.template.service.impl.TemplateFlowExtPointServiceImpl"/> <service> <provide interface="com.sabre.redapp.example3.desktop.template.service.TemplateFlowExtPointService"/> </service> </scr:component>
-
-
Update MANIFEST.MF file with dependencies and other configurations.
Example code for MANIFEST.MFManifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Web Template Sample Bundle-SymbolicName: com.sabre.redapp.example3.desktop.template;singleton:=true Bundle-Version: 1.0.0.qualifier Bundle-Vendor: Sabre Inc. Service-Component: OSGI-INF/TemplateFlowExtPointService.xml // (3) Eclipse-RegisterBuddy: com.sabre.edge.dynamo.transformer // (1) Require-Bundle: org.eclipse.core.runtime, com.sabre.edge.dynamo.transformer, // (1) com.sabre.edge.dynamo.ext.cdm;bundle-version="3.4.25", // (2) com.sabre.edge.redapp.contactdetails;bundle-version="1.0.0" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Bundle-ActivationPolicy: lazy
-
Add com.sabre.edge.dynamo.transformer to both Require-Bundle and Eclipse-RegistryBuddy. This is required for to pre-load JAXB Mappings. Add any other require bundle from target platform that are required for compilation.
-
Add com.sabre.edge.dynamo.ext.cdm to Require-Bundle.
-
Add callback service definition OSGI-INF/TemplateFlowExtensionPointService.xml to Service-Component.
-
-
Update plugin.xml with extension details
Example code for plugin.xml<?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.4"?> <plugin> <extension point="com.sabre.edge.cf.redapp"/> <extension point="com.sabre.edge.redapp.contactdetails.provider"> <contactDetails // (1) company="Sabre Inc." contactName="SRW Developers" email="redappssupport@sabre.com" phoneNumber="123-456-789" website="http://www.sabre.com"> </contactDetails> </extension> <extension point="com.sabre.edge.dynamo.flow.flowextpoint.registry"> <flowExtensionPoint // (2) callbackService="com.sabre.redapp.example3.web.template.service.TemplateFlowExtPointService:execute" flowId="com.sabre.edge.dynamo.extpoint_flow" extensionPointId="ExampleExtPoint"> </flowExtensionPoint> </extension> <extension point="com.sabre.edge.dynamo.web.module"> <modules> <module id="com-sabre-redapp-example-desktop-template"/> // (3) </modules> </extension> </plugin>
-
Provide the provider contact details.
-
Declare service which will be used with specified extension point.
-
The web module contribution.
-
-
Let’s see how to create a web application template to handle structured response rendered by command Extension Point. Refer to Getting Started.
-
Once TypeScript webapp module is created, follow the steps below.
-
Update Main.ts
Example code for Main.tsimport {Module} from 'sabre-ngv-core/modules/Module'; import {DtoService} from 'sabre-ngv-app/app/services/impl/DtoService'; import {DrawerService} from 'sabre-ngv-app/app/services/impl/DrawerService'; import {SampleService} from './services/SampleService'; import {SampleFlightsModel} from './models/SampleFlightsModel'; import {SampleFlightsView} from './views/SampleFlightsView'; import {FlightAction} from './views/FlightAction'; import {getService} from './Context'; import {registerService} from './Context'; export class Main extends Module { init(): void { super.init(); registerService(SampleService); // (1) const dto = getService(DtoService); dto.registerDataModel( '[d.Structure][o.ExtensionPoint_Summary][com_sabre_redapp_example_desktop_template.SampleFlights]', // (2) SampleFlightsModel ); dto.registerDataView(SampleFlightsModel, SampleFlightsView); // (3) const drawer = getService(DrawerService); // (4) drawer.addConfig(['sample-flight'], { // (5) details: [ { caption: 'Airline Name', print: '{{#with drawer-context-model}}' + {{airline}} + '{{/with}}' }, { caption: 'Airline Number', print: '{{#with drawer-context-model}}' + {{number}} + '{{/with}}' } ] }); drawer.addConfig('sample-flight', { actions: [ { class: FlightAction // (6) } ] }); } }
-
Service registration.
-
Register data model SampleFlightsModel with response structure path added in extension point. com_sabre_redapp_example_web_template is the name space provide in the example. This should be same as namespace provided in transformer.properties. Refer to step 3 for more details. SampleFlightsModel is the JAXB mapping for SampleFlightsModel.java class. This code registers SampleFlightsModel to the response rendered in this example.
-
Register SampleFlightDetailsModel data model to SampleFlightsView view.
-
Create instance of drawer service.
-
Add drawer template to the sample model ’sample-flight’. Drawer-context-model is the model returned to the specific response row.
-
Add action to the sample model ’sample-flight’
-
-
Create SampleFlightsModel.ts
Example code for SampleFlightsModel.tsimport {EnhancedResponseData} from 'sabre-ngv-app/app/common/data/dto/EnhancedResponseData'; import {DataOptions} from 'sabre-ngv-app/app/common/data/dto/DataOptions'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {AbstractModelOptions} from 'sabre-ngv-app/app/AbstractModelOptions'; import {JsonObject} from 'sabre-ngv-app/_types'; import {FlightModel} from './FlightModel'; @Initial<DataOptions>({ dataRoot: '[d.Structure][o.ExtensionPoint_Summary][com_sabre_redapp_example_desktop_template.SampleFlights][0]' // (1) }) @Initial<AbstractModelOptions>({ autoPropagateData: true, nonLazyMembers: [ 'flights' ] }) export class SampleFlightsModel extends EnhancedResponseData { getFlights() { return this.fromRoot().get<Array<JsonObject>>('[flights]').value().map(function(item: JsonObject){ // (3) return new FlightModel(item); }); } }
-
Register data root to use fromRoot() method on top of the root declared in this section.
-
Note that model TypeScript class is extended EnhancedResponseData which handles structured data response for most of the commands.
-
TypeScript method to return the model structure from response which will be handed over to view.
-
-
Create FlightModel.ts
Example code for FlightModel.tsimport {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {AbstractModel} from 'sabre-ngv-app/app/AbstractModel'; import {AbstractModelOptions} from 'sabre-ngv-app/app/AbstractModelOptions'; import {JsonObject} from 'sabre-ngv-app/_types'; @Initial<AbstractModelOptions>({ autoPropagateData: true, nonLazyMembers: [ 'airline', 'number' ] }) export class FlightModel extends AbstractModel { // (1) constructor(public flight: JsonObject) { super(flight); } // (2) getAirline(): string { return <string>this.get('airline'); } getNumber(): string { return <string>this.get('number'); } getDescription(): string { return 'This is sample flight description'; } }
-
Flight model extends AbstractModel and initializes the model with data provided in constructor.
-
Method returns flight attributes.
-
-
Create SampleFlightsView.ts
Example code for SampleFlightsView.tsimport {AbstractView} from 'sabre-ngv-app/app/AbstractView'; import {Mixin} from 'sabre-ngv-core/decorators/classes/Mixin'; import {WithModeRelatedWidget} from 'sabre-ngv-app/app/common/mixins/WithModeRelatedWidget'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {WithModeRelatedWidgetOptions} from 'sabre-ngv-app/app/common/mixins/WithModeRelatedWidgetOptions'; import {SampleFlightsModel} from '../models/SampleFlightsModel'; import {FlightExpert} from './FlightExpert'; import {FlightNovice} from './FlightNovice'; import {IModeRelatedWidgetsMap} from 'sabre-ngv-app/app/common/mixins/IModeRelatedWidgetsMap'; @Mixin(WithModeRelatedWidget) @Initial<WithModeRelatedWidgetOptions>({ // (2) modeRelatedWidgets: { 'expert-output-mode': FlightExpert, 'novice-output-mode': FlightNovice } }) export class SampleFlightsView extends AbstractView<SampleFlightsModel> implements WithModeRelatedWidget<FlightExpert, FlightNovice> { // (1) modeRelatedWidgets: IModeRelatedWidgetsMap<FlightExpert, FlightNovice>; }
-
In this example view simply extends AbstractView and implements WithModeRelatedWidget. This offers developer to provide view implementation for both Classic Style and Graphical Style modes.
-
Provide initial options for expert-output-mode and novice-output-mode with respective view implementations.
-
-
Create Classic Style mode view and template
Example code for FlightExpert.tsimport {Mixin} from 'sabre-ngv-core/decorators/classes/Mixin'; import {Template} from 'sabre-ngv-core/decorators/classes/view/Template'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {WithHighlightableChildren} from 'sabre-ngv-app/app/common/mixins/WithHighlightableChildren'; import {ListView} from 'sabre-ngv-app/app/widgets/container/ListView'; import {ListViewOptions} from 'sabre-ngv-app/app/widgets/container/ListViewOptions'; import {FlightExpertRow} from './FlightExpertRow'; import {SampleFlightsModel} from '../models/SampleFlightsModel'; @Mixin(WithHighlightableChildren) @Template('com-sabre-redapp-example-web-template:FlightExpert') // (3) @Initial<ListViewOptions>({ itemsProperty: 'model.flights' // (2) itemDescriptor: { 'class': FlightExpertRow } }) export class FlightExpert extends ListView<SampleFlightsModel, FlightExpertRow> { // (1) }
-
in this example FlightExpert view extends ListView to show the flight models in rows.
-
List view initialization with data list and view to handle each item in list
-
Handlebar template mapping
Example code for FlightExpert.html<div class="dn-line-group"> <div class="dn-line"> Sample Flight Information </div> </div> <div class="ui-container-items"><div>
-
-
Classic Style mode row view
Example code for FlightExpertRow.tsimport {AbstractView} from 'sabre-ngv-app/app/AbstractView'; import {AbstractViewOptions} from 'sabre-ngv-app/app/AbstractViewOptions'; import {Mixin} from 'com_sabre_ngv_core/decorators/classes/Mixin' import {Template} from 'sabre-ngv-core/decorators/classes/view/Template'; import {CssClass} from 'sabre-ngv-core/decorators/classes/view/CssClass'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {ViewDescriptor} from 'sabre-ngv-app/_types'; import {WithDrawer} from 'sabre-ngv-app/app/widgets/drawer/mixins/WithDrawer'; import {WithDrawerOptions} from 'sabre-ngv-app/app/widgets/drawer/mixins/WithDrawerOptions'; import {FlightModel} from '../models/FlightModel'; import {FlightDrawer} from './FlightDrawer'; @Mixin(WithDrawer) @Initial<WithDrawerOptions>({ // (2) drawerDescriptor: FlightDrawer }) @Template('com-sabre-redapp-example-desktop-template:FlightExpertRow') @CssClass('flight-row flight-expert-row') export class FlightExpertRow extends AbstractView<FlightModel> implements WithDrawer<FlightDrawer> { // (1) drawerDescriptor: ViewDescriptor<FlightDrawer>; openDrawer: () => void; // (3) closeDrawer: () => void; toggleDrawer: () => void; getDrawer: () => FlightDrawer; constructor(options?: AbstractViewOptions) { super(options); } }
-
FlightExpertRow view extends AbstractView of type Flight and implements WithDrawer
-
Drawer initialization parameters
-
Drawer methods implementations. openDrawer, closeDrawer and clopenDrawer are void in this examples and implementations can be provided according to needs.
Example code for FlightExpertRow.html<div class="dn-line-group"> <div class="dn-middle-group-box response-container"> <div class="dn-line response-row"> <div class="col-cpa-1"> <span> {{airline}} // (1) </span> </div> <div class="col-cpa-2"></div> <div class="col-cpa-3"> <span class="js-booking-classes booking-classes"> {{number}} // (1) </span> </div> </div> </div> <div class="dn-right-group-box"> <div class="dn-zombie-ghost drawer-arrow"> <i class="fa fa-chevron-down"></i> <div> <div> </div>
-
airline and number are properties of Flight model.
-
-
Create Graphical Style mode view and template
Example code for FlightNovice.tsimport {Mixin} from 'sabre-ngv-core/decorators/classes/Mixin'; import {Template} from 'sabre-ngv-core/decorators/classes/view/Template'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {WithHighlightableChildren} from 'sabre-ngv-app/app/common/mixins/WithHighlightableChildren'; import {ListView} from 'sabre-ngv-app/app/widgets/container/ListView'; import {ListViewOptions} from 'sabre-ngv-app/app/widgets/container/ListViewOptions'; import {FlightNoviceRow} from './FlightNoviceRow'; import {SampleFlightsModel} from '../models/SampleFlightsModel'; @Mixin(WithHighlightableChildren) @Template('com-sabre-redapp-example-desktop-template:FlightNovice') // (3) @Initial<ListViewOptions>({ itemsProperty: 'model.flights' // (2) itemDescriptor: { 'class': FlightNoviceRow } }) export class FlightNovice extends ListView<SampleFlightsModel, FlightNoviceRow> { // (1) }
-
in this example FlightNovice view extends ListView to show the flight models in rows.
-
List view initialization with data list and view to handle each item in list
-
Handlebar template mapping
Example code for FlightNovice.html<div class="dn-line-group"> <div class="dn-line"> Sample Flight Information </div> </div> <div class="ui-container-items"><div>
-
-
Graphical Style mode row view
Example code for FlightNoviceRow.tsimport {AbstractView} from 'sabre-ngv-app/app/AbstractView'; import {AbstractViewOptions} from 'sabre-ngv-app/app/AbstractViewOptions'; import {Mixin} from 'com_sabre_ngv_core/decorators/classes/Mixin' import {Template} from 'sabre-ngv-core/decorators/classes/view/Template'; import {CssClass} from 'sabre-ngv-core/decorators/classes/view/CssClass'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {ViewDescriptor} from 'sabre-ngv-app/_types'; import {WithDrawer} from 'sabre-ngv-app/app/widgets/drawer/mixins/WithDrawer'; import {WithDrawerOptions} from 'sabre-ngv-app/app/widgets/drawer/mixins/WithDrawerOptions'; import {FlightModel} from '../models/FlightModel'; import {FlightDrawer} from './FlightDrawer'; @Mixin(WithDrawer) @Initial<WithDrawerOptions>({ // (2) drawerDescriptor: FlightDrawer }) @Template('com-sabre-redapp-example-desktop-template:FlightNoviceRow') @CssClass('flight-row flight-novice-row') export class FlightNoviceRow extends AbstractView<FlightModel> implements WithDrawer<FlightDrawer> { // (1) drawerDescriptor: ViewDescriptor<FlightDrawer>; openDrawer: () => void; // (3) closeDrawer: () => void; toggleDrawer: () => void; getDrawer: () => FlightDrawer; constructor(options?: AbstractViewOptions) { super(options); } }
-
FlightNoviceRow view extends AbstractView of type Flight and implements WithDrawer
-
Drawer initialization parameters
-
Drawer methods implementations. openDrawer, closeDrawer and clopenDrawer are void in this examples and implementations can be provided according to needs.
Example code for FlightNoviceRow.html<div class="dn-line-group"> <div class="dn-middle-group-box response-container"> <div class="dn-line response-row"> <div class="col-cpa-1"> <span> {{airline}} // (1) </span> </div> <div class="col-cpa-2"></div> <div class="col-cpa-3"> <span class="js-booking-classes booking-classes"> {{number}} // (1) </span> </div> </div> </div> <div class="dn-right-group-box"> <div class="dn-zombie-ghost drawer-arrow"> <i class="fa fa-chevron-down"></i> <div> <div> </div>
-
airline and number are properties of Flight model.
-
-
Drawer
Example code for FlightDrawer.tsimport {Drawer} from 'sabre-ngv-app/app/widgets/drawer/views/Drawer'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {DrawerOptions} from 'sabre-ngv-app/app/widgets/drawer/views/DrawerOptions'; import {FlightModel} from './models/FlightModel'; import {AbstractViewOptions} from 'sabre-ngv-app/app/AbstractViewOptions'; @Initial<DrawerOptions>({ // (2) drawerGroups: ['sample-flight'] }) export class FlightDrawer extends Drawer<FlightModel> { //(1) initialize(options?: AbstractViewOptions): void { super.initialize(options); } }
-
FlightDrawer extends Drawer of type Flight.
-
Drawer initialization group name. Same model name should be used to add any configurations to the Drawer. Refer to Main.ts.
-
-
Action Template
Example code for FlightAction.tsimport {AbstractAction} from 'sabre-ngv-app/app/common/views/AbstractAction'; import {AbstractViewOptions} from 'sabre-ngv-app/app/AbstractViewOptions'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {Template} from 'sabre-ngv-core/decorators/classes/view/Template'; import {CssClass} from 'sabre-ngv-core/decorators/classes/view/CssClass'; import {getService} from '../Context'; import {I18nService} from 'sabre-ngv-app/app/services/impl/I18nService'; import {FlightModel} from '../models/FlightModel'; import {SampleService} from '../services/SampleService'; const i18nService: I18nService = getService(I18nService); @Initial<AbstractActionOptions>({ caption: 'Sample Action', actionName: 'action', (2) type: 'primary', templateOptions: { helpers: { _t: i18nService.getScopedHelper('com-sabre-redapp-example-desktop-template/translations') } } }) @Template('com-sabre-redapp-example-web-template:FlightAction') // (3) @CssClass('flight-sell-action') export class FlightAction extends AbstractAction<FlightModel> { // (1) selfAction() { getService(SampleService).executeCommand(); } }
-
FlightAction extends AbstractAction of type Flight.
-
Bind a method. Target method will be same as actionName with prefix self, in this case it is selfAction.
-
Handlebar template for the Sample Action.
-
Translations are loaded using i18nService. Below is the screenshot of translations.json file located under src/code/i18n/en_US/ in sample modules. en_US is the internalization code US English.
Example code for translations.json{ "SAMPLE_ACTION": "Sample Action" }
NoteKeys names in translations map must be provided with upper cases and underscores. Values from translations map are used for internationalization. Translations from en_US will be used in case when no other translation is provided. Example code for FlightAction.html<div class="button-col col"> <button class="sample-action-button btn btn-success">{{_t "SAMPLE_ACTION"}}</button> </div>
-
_t is the TypeScript member defined in FlightAction.ts.
-
-
Sample Service
Example code for ISampleService.tsexport interface ISampleService { executeCommand(): void; }
Example code for SampleService.tsimport {AbstractService} from 'sabre-ngv-app/app/services/impl/AbstractService'; import {ISampleService} from './SampleService'; import {cf} from '../Context'; export class SampleService extends AbstractService implements ISampleService { // (1) static SERVICE_NAME = 'com-sabre-redapp-example3-desktop-template-web-module-SampleService'; // (2) executeCommand() { //send sample command cf('*A').send(); // (3) } }
-
SampleService extends AbstractService and implements ISampleService.
-
Declare variable SERVICE_NAME with service name.
-
executeCommand invoking command framework with command ’*A’. This
-
-
Add any custom css in styles.less file located under styles directory
-
Sample plugin structure.
See below for the directory structure of a complete sample plugin.$ tree com.sabre.redapp.example3.desktop.template com.sabre.redapp.example3.desktop.template/ ├── build.properties ├── META-INF │ └── MANIFEST.MF ├── OSGI-INF │ └── TemplateFlowExtPointService.xml ├── plugin.xml ├── pom.xml ├── redapp.xml ├── src │ ├── com │ │ └── sabre │ │ └── redapp │ │ └── example3 │ │ └── web │ │ └── template │ │ ├── data │ │ │ ├── Flight.java │ │ │ ├── ObjectFactory.java │ │ │ └── SampleFlights.java │ │ └── service │ │ ├── impl │ │ │ └── TemplateFlowExtPointServiceImpl.java │ │ └── TemplateFlowExtPointService.java │ └── transformer.properties └── web-src └── com-sabre-redapp-example-desktop-template ├── concierge.conf.js ├── package.json └── src ├── assets │ └── sabre-logo.png ├── code │ ├── Context.ts │ ├── index.ts │ ├── Main.ts │ ├── models │ │ ├── FlightModel.ts │ │ └── SampleFlightsModel.ts │ ├── services │ │ ├── ISampleService.ts │ │ └── SampleService.ts │ └── views │ ├── FlightAction.ts │ ├── FlightDrawer.ts │ ├── FlightExpert.ts │ ├── FlightExpertRow.ts │ ├── FlightNovice.ts │ ├── FlightNoviceRow.ts │ └── SampleFlightsView.ts ├── guides │ └── README.md ├── i18n │ └── en_US │ └── translations.json ├── manifest.json ├── styles │ └── styles.less └── templates ├── FlightAction.html ├── FlightExpert.html ├── FlightExpertRow.html ├── FlightNovice.html └── FlightNoviceRow.html
-
After completing the above steps compile and build type script web module and copy the compiled script files from build/dist to the web/<modulename>/ directory.
Execution Steps:
-
Include sample plugin in Eclipse run configurations.
-
Make sure start level and auto start values are updated as stated in step 8 in Development Steps.
-
Run the application.
-
Login to application using EPR, password and PCC.
-
Execute the command ’SAMPLE://EXTPOINT’.
-
Screenshot of the output below. Click on each row to see the drawer details.
-
Action button label is rendered from the translations.json
-
Click on the SampleAction button to execute *A command. This will return ’NO DATA’
How to Call Java Service from JavaScript:
-
The same sample plugin has been extended to demonstrate this feature. In com.sabre.redapp.example3.desktop.template plugin there is another callback service class introduced.
-
Create call back service interface and implementation. ActionDataExtensionPointService.java and ActionDataExtensionPointCallbackService.java has been created to provide input data to Extension Point.
Example code for ActionDataExtensionPointService.javapackage com.sabre.redapp.example3.web.template.service; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointCommand; /** * Service displaying selected flight */ public interface ActionDataExtensionPointService { /** * Prepares data model to display. * * @param extPointCommand {@link FlowExtPointCommand} flow * @return {@link FlowExtPointCommand} flow */ FlowExtPointCommand execute(FlowExtPointCommand extPointCommand); }
Example code for ActionDataExtensionPointService.javapackage com.sabre.redapp.example3.web.template.service.impl; import java.util.Optional; import com.sabre.redapp.example3.web.template.data.Flight; import com.sabre.redapp.example3.web.template.data.SampleFlights; import com.sabre.redapp.example3.web.template.service.ActionDataExtensionPointService; import com.sabre.stl.pos.srw.nextgen.flow.ext.utils.FlowExtPointCommandUtils; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointCommand; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointDataOperation; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointRequestWrapper; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointResponse; import com.sabre.stl.pos.srw.nextgen.flow.ext.v2.FlowExtPointResponseWrapper; /** * Service displaying selected flight */ public class ActionDataExtensionPointServiceImpl implements ActionDataExtensionPointService { /** * {@inheritDoc} **/ @Override public FlowExtPointCommand execute(FlowExtPointCommand flowExtPointCommand) { Optional <Flight> flightRq = FlowExtPointCommandUtils.getRequestWrapper(flowExtPointCommand, Flight.class) //(1) .map(FlowExtPointRequestWrapper::getRequest) .map(Flight.class::cast); if (flightRq.isPresent()) { SampleFlights flights = new SampleFlights(); // (2) Flight flight = new Flight(); flight.setAirline("Selected Airline"); flight.setNumber(flightRq.get().getNumber()); flights.getFlights().add(flight); FlowExtPointResponse response = new FlowExtPointResponse(); response.setStructure(flights); FlowExtPointResponseWrapper responseWrapper = new FlowExtPointResponseWrapper(); // (3) responseWrapper.setOperation(FlowExtPointDataOperation.ADD); responseWrapper.setResponse(response); // (4) flowExtPointCommand.getResponses().add(responseWrapper); // (5) } return flowExtPointCommand; } }
-
Retrieve data from request.
-
Prepare data for response.
-
Create response wrapper
-
Add data to wrapper
-
Add wrapper to response
-
-
Create xml file with service definition. This file should be placed in OSG-INF directory.
Example code for ActionDataExtensionPointService.xml<?xml version="1.0" encoding="UTF-8"?> <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="com.sabre.redapp.example3.desktop.template.service.ActionDataExtensionPointService"> <implementation class="com.sabre.redapp.example3.desktop.template.service.impl.ActionDataExtensionPointServiceImpl"/> <service> <provide interface="com.sabre.redapp.example3.desktop.template.service.ActionDataExtensionPointService"/> </service> </scr:component>
-
Update MANIFEST.MF file with new service in Service-Component section.
Example code for ActionDataExtensionPointService.xmlService-Component: OSGI-INF/TemplateFlowExtPointService.xml, OSGI-INF/ActionDataExtensionPointService.xml
-
Provide Callback registration for RedApp Flow Extension Point Task in plugin.xml. The redapp flow id is dynamo.api.executor and extension point id is execute.
Example code for ActionDataExtensionPointService.xml<extension point="com.sabre.edge.dynamo.flow.flowextpoint.registry"> <flowExtensionPoint callbackService="com.sabre.redapp.example3.desktop.template.service.TemplateFlowExtPointService:execute" flowId="com.sabre.edge.dynamo.extpoint_flow" extensionPointId="ExampleExtPoint"> </flowExtensionPoint> <flowExtensionPoint callbackService="com.sabre.redapp.example3.desktop.template.service.ActionDataExtensionPointService:execute" extensionPointId="execute" flowId="dynamo.api.executor"> </flowExtensionPoint> </extension>
-
In this sample we are trying to call java service from javascript when we click on Sample Action button in drawer. In SampleService.ts we will send sample command by passing the command name and service id as shown below.
Format: NGV://command_name/registered_service_class_name:method
Example: cf('NGV://REDAPP/SERVICE/com.sabre.redapp.example3.desktop.template.ActionDataExtensionPointService:execute')
Example code for SampleService.tsimport {AbstractService} from 'sabre-ngv-app/app/services/impl/AbstractService'; import {ISampleService} from './SampleService'; import {cf} from '../Context'; export class SampleService extends AbstractService implements ISampleService { // (1) static SERVICE_NAME = 'com-sabre-redapp-example3-desktop-template-web-module-SampleService'; // (2) executeCommand() { //send sample command cf('NGV://REDAPP/SERVICE/com.sabre.redapp.example3.desktop.template.ActionDataExtensionPointService:execute') .setLocalPreference('uiBlocking', true) // (4) .setLocalPreference('silentRequest', true) // (5) .addRequestDataObject({ 'com_sabre_redapp_example_web_template.Flight': flight // (6) }) .send(); } }
-
SampleService extends AbstractService and implements ISampleService
-
Initialize special static variable SERVICE_NAME with service name.
-
Execute command invoking sample backend service ActionDataExtensionPointService
-
Set property to block UI until it recieve response from backend.
-
Set property to not display response in dynamo. Returned model will be handled by FlightsDetailView
-
Add data to request. In this case it is current flight.
-
-
Here is the response we get after sending the above command when clicked on Sample Action button.
Open Details View (UI Modal):
As part of this same sample (com.sabre.redapp.example3.web.template) we will show how to open details view, a basically UI modal, using LayerService and how to render form after the modal opens and define actions buttons in the Details View.
-
Create new view for flight details
Example code for FlightDetailsView.tsimport {AbstractView} from "sabre-ngv-app/app/AbstractView"; import {AbstractViewOptions} from "sabre-ngv-app/app/AbstractViewOptions"; import {Template} from "sabre-ngv-core/decorators/classes/view/Template"; import {FlightModel} from '../models/FlightModel'; @Template('com-sabre-redapp-example-desktop-template:FlightDetailsView') export class FlightDetailsView extends AbstractView<FlightModel> { constructor(options?: AbstractViewOptions) { super(options); } initialize() { super.initialize(); this.getModel().set('flight', {'number': this.getModel().getNumber(), 'airline': this.getModel().getAirline()}); // (1) this.render(); } }
-
Set up model values for template
Example code for FlightDetailsView.html<div><p>This is Red App Template Flight Details View!</p> </div> <br> {{#with flight}} // (1) <div class="myDiv"> Airline: {{airline}} // (2) </div> <div class="myDiv"> Flight number: {{number}} // (2) </div> {{/with}}
-
Access data from model
-
Read properties
-
-
Create new action for flight details view
Example code for FlightAction.tsimport {AbstractAction} from 'sabre-ngv-app/app/common/views/AbstractAction'; import {AbstractActionOptions} from 'sabre-ngv-app/app/common/views/AbstractActionOptions'; import {Initial} from 'sabre-ngv-core/decorators/classes/Initial'; import {Template} from 'sabre-ngv-core/decorators/classes/view/Template'; import {CssClass} from 'sabre-ngv-core/decorators/classes/view/CssClass'; import {getService} from '../Context'; import {I18nService} from 'sabre-ngv-app/app/services/impl/I18nService'; import {FlightModel} from '../models/FlightModel'; import {LayerService} from 'sabre-ngv-core/services/LayerService'; import {FlightDetailsView} from '../views/FlightDetailsView'; const i18nService: I18nService = getService(I18nService); @Initial<AbstractActionOptions>({ caption: 'Details Action', actionName: 'action', type: 'secondary', templateOptions: { helpers: { _t: i18nService.getScopedHelper('com-sabre-redapp-example-desktop-template/translations') } } }) @Template('com-sabre-redapp-example-web-template:FlightDetailsAction') @CssClass('flight-sell-action') export class FlightDetailsAction extends AbstractAction<FlightModel> { selfAction() { let flight : FlightModel = this.getModel().get('sample-flight'); getService(LayerService).showInModal( // (1) new FlightDetailsView({model: flight}), // (2) { title: 'Flight details View', display: 'areaView' }); } }
-
Use LayerService to show view
-
Pass model to view
Example code for FlightDetailsAction.html<div class="button-col col"> <button class="sample-action-button btn btn-success">{{_t "FLIGHT_DETAILS_ACTION"}}</button> // (1) </div>
-
Button name will be taken from translations.json
Example code for translations.json{ "SAMPLE_ACTION": "Sample Action", "FLIGHT_DETAILS_ACTION": "Flight details" // (1) }
-
Add new translation
-
-
Add new action to drawer in Main.ts
Example code for FlightAction.tsimport {Module} from 'sabre-ngv-core/modules/Module'; import {DtoService} from 'sabre-ngv-app/app/services/impl/DtoService'; import {DrawerService} from 'sabre-ngv-app/app/services/impl/DrawerService'; import {SampleService} from './services/SampleService'; import {SampleFlightsModel} from './models/SampleFlightsModel'; import {SampleFlightsView} from './views/SampleFlightsView'; import {FlightAction} from './views/FlightAction'; import {getService} from './Context'; import {registerService} from './Context'; import {FlightDetailsAction} from './views/FlightDetailsAction'; export class Main extends Module { init(): void { super.init(); registerService(SampleService); const dto = getService(DtoService); dto.registerDataModel( '[d.Structure][o.ExtensionPoint_Summary][com_sabre_redapp_example_desktop_template.SampleFlights]', SampleFlightsModel ); dto.registerDataView(SampleFlightsModel, SampleFlightsView); const drawer = getService(DrawerService); drawer.addConfig(['sample-flight'], { details: [ { caption: 'Airline Name', print: '{{#with drawer-context-model}}' + '{{airline}}' + '{{/with}}' }, { caption: 'Airline Number', print: '{{#with drawer-context-model}}' + '{{number}}' + '{{/with}}' } ] }); drawer.addConfig('sample-flight', { actions: [ { class: FlightAction }, { class: FlightDetailsAction // (1) }, ] }); } }
-
Add new detail action to drawer
-
Execution Steps:
-
Include sample plugin in Eclipse run configurations.
-
Run the application.
-
Login to application using EPR, password and PCC.
-
Execute the command ’SAMPLE://EXTPOINT’.
-
Screenshot of the output below. Click on each row to see the drawer details.
-
Click Flight Details button to open details view.