SlideShare a Scribd company logo
Sirius Web Advanced:
Customize and extend the platform
Stéphane Bégaudeau
Sirius Web Architect
stephane.begaudeau@obeo.fr | sbegaudeau
Sirius Web
■ Everything you liked in Sirius Desktop, available on a modern cloud-based stack
■ Graphical and Domain specific tooling
■ Defined by a configuration file
■ Deployed on a web server
■ Rendered in a web browser
■ Collaborative support
https://www.eclipse.org/sirius/sirius-web.html
Obeo Studio
■ All of Sirius Web with additional collaborative and access control features
■ Authentication and authorization
■ Public/Private projects
■ Role based access control
■ Indicators of active users
https://www.obeosoft.com/en/products/obeo-studio
Completely customizable
■ Configure Sirius Web and Obeo Studio with the concepts from your domain
■ Define the graphical representation that you need
■ Diagrams
■ Tools
■ Forms
■ Validation
■ Import existing Sirius desktop configuration easily
What’s in Sirius Web?
READY-TO-USE
Modeling framework
to define and render
graphical applications
in the web
What’s in Sirius Web?
READY-TO-USE
Modeling framework
to define and render
graphical applications
in the web MODEL SERVER
Open source model
server components
with a GraphQL API
What’s in Sirius Web?
READY-TO-USE
Modeling framework
to define and render
graphical applications
in the web MODEL SERVER
MODEL APPLICATION
Open source model
application (diagram,
properties, forms…)
Open source model
server components
with a GraphQL API
Built on top of awesome technologies
The Sirius Web ecosystem
Code-based customization
Code-based customization
■ Customize icons
■ Customize the child creation proposals available in the explorer
■ Advanced behavior for diagram tools
■ Java services
■ Java based representation descriptions
Provide Java services
@Service
public class CustomServicesProvider implements IJavaServiceProvider {
@Override
public List<Class<?>> getServiceClasses(View view) {
return List.of(CustomServices.class);
}
}
public class CustomServices {
public String getValue(EObject self, String name) {
return self.eClass().eGet(self.eClass().getEStructuralFeature(name)).toString();
}
}
aql:self.getValue('name')
Register representation descriptions
@Configuration
public class RepresentationDescriptionRegistryConfigurer implements IRepresentationDescriptionRegistryConfigurer {
@Override
public void addRepresentationDescriptions(IRepresentationDescriptionRegistry registry) {
DiagramDescription diagramDescription = DiagramDescription.newDiagramDescription("customDiagram")
.nodeDescriptions(List.of())
.edgeDescriptions(List.of())
.toolSections(List.of())
.build();
registry.add(diagramDescription);
}
}
Extension of the platform
Let’s extend the platform!
■ Add a new kind of representation
■ Contribute it to our backend
■ Describe it in our GraphQL API
■ Integrate it in the frontend of your application
■ Synchronize our data with another application
Sirius Web Advanced : Customize and Extend the Platform
Map based representation
■ Map representation based on Google Maps
■ A dedicated metamodel
■ to create semantic elements with map-related attributes
■ Display and refresh our map in real time when the objects are modified
Backend
■ Add support for the map representation
■ Representation description and instance
■ Creation process
■ Event processor
■ GraphQL API
Register metamodel
@Configuration
public class MapPackageConfiguration {
@Bean
public EPackage mapPackage() {
EClass mapEClass = EcoreFactory.eINSTANCE.createEClass();
mapEClass.setName("Map");
// Contribute the attributes of the class: lat, lng, zoom
EPackage mapEPackage = EcoreFactory.eINSTANCE.createEPackage();
mapEPackage.setName("Map");
mapEPackage.setNsPrefix("map");
mapEPackage.setNsURI("https://www.eclipse.org/sirius/map");
mapEPackage.getEClassifiers().add(mapEClass);
return mapEPackage;
}
}
Sirius Web Advanced : Customize and Extend the Platform
Sirius Web Advanced : Customize and Extend the Platform
MapDescription.java
public class MapDescription implements IRepresentationDescription {
@Override
public String getId() {
return "map";
}
@Override
public String getLabel() {
return "Map";
}
@Override
public Predicate<VariableManager> getCanCreatePredicate() {
return variableManager -> variableManager.get("class", EClass.class)
.filter(eClass -> eClass.getEPackage().getNsURI().equals("https://www.eclipse.org/sirius/map"))
.isPresent();
}
}
Map.java
public class Map implements IRepresentation, ISemanticRepresentation {
public static final String KIND = IRepresentation.KIND_PREFIX + "?type=Map";
private String id;
private String descriptionId = "map";
private String targetObjectId;
private String label;
private double lng;
private double lat;
private int zoom;
}
Representation description registration
@Configuration
public class MapRepresentationDescriptionRegistryConfigurer implements IRepresentationDescriptionRegistryConfigurer
{
@Override
public void addRepresentationDescriptions(IRepresentationDescriptionRegistry registry) {
registry.add(new MapDescription());
}
}
Create maps
@Service
public class CreateMapEventHandler implements IEditingContextEventHandler {
@Override
public boolean canHandle(IEditingContext editingContext, IInput input) {
// Check that the input is for the creation of a map representation
}
@Override
public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDescriptionSink,
IEditingContext editingContext, IInput input) {
Map map = new Map(...);
this.representationPersistenceService.save(editingContext, map);
changeDescriptionSink.tryEmitNext(
new ChangeDescription(ChangeKind.REPRESENTATION_CREATION, editingContext.getId(), input)
);
payloadSink.tryEmitValue(new CreateRepresentationSuccessPayload(input.getId(), map));
}
}
Tell Jackson how to read the map
@Service
public class MapDeserialiser implements IRepresentationDeserializer {
@Override
public boolean canHandle(ObjectNode root) {
return Optional.ofNullable(root.get("kind"))
.map(JsonNode::asText)
.filter(Map.KIND::equals)
.isPresent();
}
@Override
public Optional<IRepresentation> handle(ObjectMapper mapper, ObjectNode root) {
try {
return Optional.of(mapper.readValue(root.toString(), Map.class));
} catch (JsonProcessingException exception) {}
return Optional.empty();
}
}
Sirius Web Advanced : Customize and Extend the Platform
Backend Architecture
GraphQL
SubscriptionMapEventDataFetcher
MapEventProcessor
MapEventProcessorFactory
EditingContextEventProcessorRegistry
EditingContextEventProcessor
MapEventFlux
map.graphqls
GraphQL Schema
extend type Subscription {
mapEvent(input: MapEventInput!): MapEventPayload!
}
input MapEventInput { id: ID! editingContextId: ID! mapId: ID! }
union MapEventPayload = ErrorPayload | SubscribersUpdatedEventPayload | MapRefreshedEventPayload
type MapRefreshedEventPayload { id: ID! map: Map! }
type Map implements Representation {
id: ID!
metadata: RepresentationMetadata!
lng: Float!
lat: Float!
zoom: Int!
}
type MapDescription implements RepresentationDescription {}
GraphQL Subscription
@SubscriptionDataFetcher(type = "Subscription", field = "mapEvent")
public class SubscriptionMapEventDataFetcher implements IDataFetcherWithFieldCoordinates<Publisher<IPayload>> {
@Override
public Publisher<IPayload> get(DataFetchingEnvironment environment) throws Exception {
Object argument = environment.getArgument("input");
var input = this.objectMapper.convertValue(argument, MapEventInput.class);
var mapConfiguration = new MapConfiguration(input.getMapId());
return this.editingContextEventProcessorRegistry
.getOrCreateEditingContextEventProcessor(input.getEditingContextId())
.flatMap(processor -> processor.acquireRepresentationEventProcessor(
IMapEventProcessor.class, mapConfiguration, input
))
.map(representationEventProcessor -> representationEventProcessor.getOutputEvents(input))
.orElse(Flux.empty());
}
}
Subscribe to the representation
@Service
public class MapEventProcessorFactory implements IRepresentationEventProcessorFactory {
@Override
public <T extends IRepresentationEventProcessor> Optional<T> createRepresentationEventProcessor(Class<T>
representationEventProcessorClass, IRepresentationConfiguration configuration, IEditingContext editingContext) {
var optionalMap = this.representationSearchService.findById(editingContext, mapConfiguration.getId(),
Map.class);
if (optionalMap.isPresent()) {
Map map = optionalMap.get();
IRepresentationEventProcessor mapEventProcessor = new MapEventProcessor(...);
return Optional.of(mapEventProcessor)
.filter(representationEventProcessorClass::isInstance)
.map(representationEventProcessorClass::cast);
}
return Optional.empty();
}
}
Subscribe to the representation
public class MapEventProcessor implements IMapEventProcessor {
@Override
public void refresh(ChangeDescription changeDescription) {
// Refresh, save the new version of the map and send it using the mapEventFlux
}
@Override
public Flux<IPayload> getOutputEvents(IInput input) {
return Flux.merge(
this.mapEventFlux.getFlux(input),
this.subscriptionManager.getFlux(input)
);
}
@Override
public void dispose() {
this.subscriptionManager.dispose();
this.mapEventFlux.dispose();
}
}
Sirius Web Advanced : Customize and Extend the Platform
Frontend
■ On the frontend side
■ New representation component using React
■ Register the component in the entry point of the application
■ Subscribe to map events using our GraphQL API
MapRepresentation.tsx
export const MapRepresentation = ({ editingContextId, representationId }: RepresentationComponentProps) => {
const [state, setState] = useState<MapRepresentationState>({ center: null, zoom: 10 });
useSubscription(subscription, { ... });
return (
<div>
{state.center ? (
<GoogleMapReact
bootstrapURLKeys={{ key: 'XXXXXXXXXXXX' }}
center={state.center}
zoom={state.zoom}></GoogleMapReact>
) : null}
</div>
);
};
MapRepresentation.tsx
const subscription = gql`
subscription getMapEvent($input: MapEventInput!) {
mapEvent(input: $input) {
... on MapRefreshedEventPayload {
map {
lng
lat
zoom
}
}
}
}
`;
MapRepresentation.tsx
useSubscription(subscription, {
variables,
fetchPolicy: 'no-cache',
onSubscriptionData: ({ subscriptionData }) => {
const { data } = subscriptionData;
if (data && data.mapEvent.__typename === 'MapRefreshedEventPayload') {
const { map: { lng, lat, zoom } } = data.mapEvent;
setState((prevState) => {
return { ...prevState, center: { lng, lat }, zoom };
});
}
},
});
index.tsx
const registry = {
getComponent: (representation: Representation): RepresentationComponent | null => {
const query = representation.kind.substring(representation.kind.indexOf('?') + 1, representation.kind.length);
const params = new URLSearchParams(query);
const type = params.get('type');
if (type === 'Map') {
return MapRepresentation;
} else if (type === 'Diagram') {
return DiagramWebSocketContainer;
}
return null;
},
};
index.tsx
ReactDOM.render(
<ApolloProvider client={ApolloGraphQLClient}>
<BrowserRouter>
<ThemeProvider theme={siriusWebTheme}>
<CssBaseline />
<div style={style}>
<RepresentationContext.Provider value={{ registry }}>
<Main />
</RepresentationContext.Provider>
</div>
</ThemeProvider>
</BrowserRouter>
</ApolloProvider>,
document.getElementById('root')
);
Sirius Web Advanced : Customize and Extend the Platform
Sirius Web Advanced : Customize and Extend the Platform
DEMO
Additional ideas
■ Update the properties of the object when the map is modified
■ Listen to change in coordinates or zoom level
■ Send a mutation to the backend
■ Update the data in the MapEventProcessor and refresh the map
Remote service
synchronization
Publish a gist with the coordinates
MapEventProcessor.java
public class MapEventProcessor implements IMapEventProcessor {
@Override
public void refresh(ChangeDescription changeDescription) {
// Refresh, save the new version of the map and send it using the mapEventFlux
LocalDateTime now = LocalDateTime.now();
String datetime = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
String content = datetime + " --- Latitude: " + lat + ", Longitude: " + lng; //$NON-NLS-1$//$NON-NLS-2$
String body = "{"description":"Update","files":{"MapData":{"content":"" + content + ""}}}";
var uri = URI.create("https://api.github.com/gists/58875db0a0146ccd7d17945079f489e1");
var httpClient = HttpClient.newHttpClient();
var httpRequest = HttpRequest.newBuilder()
.uri(uri)
.method("PATCH", HttpRequest.BodyPublishers.ofString(body))
.header("Accept", "application/vnd.github.v3+json")
.header("Authorization", "token XXXXXXXXXXXXXXXXXXX")
.build();
httpClient.send(httpRequest, BodyHandlers.ofString());
}
}
Synchronize with a remote service
■ You can check to see if there are any difference with the previous version first
■ Perform the request asynchronously to improve performance
■ In order to synchronize data the other way
■ Send a GraphQL query to Sirius Web
■ Contribute an IEditingContextEventHandler to perform the change
Sirius Web Advanced : Customize and Extend the Platform
Integration in other products
Integration in web applications
MODEL SERVER
Leverage our GraphQL API
over HTTP and WebSocket to
interact with your servers
GRAPHICAL EDITORS
Manipulate your Sirius Web
graphical editors from your
app (diagrams, forms, etc)
Getting started
■ To start integrating Sirius Web in a cloud IDE, you’ll need
■ The latest release of @eclipse-sirius/sirius-components
■ React components for our graphical editors
■ An instance of a Sirius Web server
■ HTTP and WebSocket GraphQL API
VSCode example
Sirius Web Advanced : Customize and Extend the Platform
Node
Node HTML Document
Project TreeView
■ Used to manipulate Sirius Web / Obeo Studio projects
■ Leverage our GraphQL API over HTTP
Project TreeView
■ Retrieve the projects using a GraphQL query
private fetchProjects(): Promise<ProjectData[]> {
const queryURL = `${this.serverAddress}/api/graphql`;
const headers = { headers: { Cookie: this.cookie } };
const graphQLQuery = `
query getProjects($page: Int!) {
viewer {
id
projects(page: $page) {
edges {
node {
id
name
visibility
}
}
}
}
}
`;
Explorer TreeView
■ Used to display the model elements from the project
■ Based on the configuration of the explorer of the server
■ Can be parameterized
■ Based on a tree representation
■ Using a GraphQL subscription for real time update
■ Based on the graphql-ws protocol
Explorer TreeView
Sirius Web Advanced : Customize and Extend the Platform
VS Code WebView
VS Code WebView
public static getWebviewContent(webView: vscode.Webview, webViewContext: WebViewContext): string {
const reactAppPathOnDisk = vscode.Uri.file(path.join(webViewContext.extensionPath, 'siriusweb', 'siriusweb.js'));
const reactAppUri = webView.asWebviewUri(reactAppPathOnDisk);
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${webViewContext.representationLabel}</title>
<script>
window.acquireVsCodeApi = acquireVsCodeApi;
window.serverAddress = '${webViewContext.serverAddress}';
window.username = '${webViewContext.username}';
window.password = '${webViewContext.password}';
window.editingContextId = '${webViewContext.editingContextId}';
window.representationId = '${webViewContext.representationId}';
window.representationLabel = '${webViewContext.representationLabel}';
window.representationKind = '${webViewContext.representationKind}';
</script>
</head>
<body>
<script src="${reactAppUri}"></script>
</body>
</html>`;
}
VS Code WebView
import { DiagramWebSocketContainer, PropertiesWebSocketContainer, Selection } from '@eclipse-sirius/sirius-components';
export const App = ({...}: AppProps) => {
let component;
if (representationKind === 'Diagram') {
component = (
<DiagramWebSocketContainer
editingContextId={state.editingContextId}
representationId={state.representationId}
readOnly={false}
selection={state.selection}
setSelection={setSelection}
/>
);
} else {
component = (
<PropertiesWebSocketContainer
editingContextId={state.editingContextId}
readOnly={false}
selection={state.selection}
/>
);
}
}
Sirius Web Advanced : Customize and Extend the Platform
Sirius Web Advanced : Customize and Extend the Platform
Thank you!

More Related Content

PDF
Sirius Web 101 : Create a Modeler With No Code
PDF
Tailoring Arcadia Framework in Thales UK
PDF
CapellaDays2022 | ThermoFisher - ESI TNO | A method for quantitative evaluati...
PDF
CKA Certified Kubernetes Administrator Notes
PPT
Executable UML and SysML Workshop
PDF
Connecting Textual Requirements with Capella Models
PPS
Software design principles
PDF
MBSE and Model-Based Testing with Capella
Sirius Web 101 : Create a Modeler With No Code
Tailoring Arcadia Framework in Thales UK
CapellaDays2022 | ThermoFisher - ESI TNO | A method for quantitative evaluati...
CKA Certified Kubernetes Administrator Notes
Executable UML and SysML Workshop
Connecting Textual Requirements with Capella Models
Software design principles
MBSE and Model-Based Testing with Capella

What's hot (20)

PPTX
Test Coverage
PPT
Domain Driven Design (DDD)
PPTX
STPA Analysis of Automotive Safety Using Arcadia and Capella
PDF
Getting Git Right
PDF
GITS Class #16: CI/CD (Continuous Integration & Continuous Deployment) with G...
PDF
How To Write A Test Case In Software Testing | Edureka
PPTX
Kubernetes Introduction
PPTX
Introduction to docker
PPTX
Git branching strategies
PDF
Continuous Integration/Deployment with Gitlab CI
PDF
TypeScript and Angular workshop
PDF
Kubernetes extensibility: CRDs & Operators
PDF
Getting started with Spring Security
PPTX
Docker Compose | Docker Compose Tutorial | Docker Tutorial For Beginners | De...
PPTX
Software Architecture and Design - An Overview
PDF
Clean Architecture
PDF
An Introduction to Redux
PPTX
Data Types, Variables, and Constants in C# Programming
PDF
EKS Workshop
PDF
Networking in Docker
Test Coverage
Domain Driven Design (DDD)
STPA Analysis of Automotive Safety Using Arcadia and Capella
Getting Git Right
GITS Class #16: CI/CD (Continuous Integration & Continuous Deployment) with G...
How To Write A Test Case In Software Testing | Edureka
Kubernetes Introduction
Introduction to docker
Git branching strategies
Continuous Integration/Deployment with Gitlab CI
TypeScript and Angular workshop
Kubernetes extensibility: CRDs & Operators
Getting started with Spring Security
Docker Compose | Docker Compose Tutorial | Docker Tutorial For Beginners | De...
Software Architecture and Design - An Overview
Clean Architecture
An Introduction to Redux
Data Types, Variables, and Constants in C# Programming
EKS Workshop
Networking in Docker
Ad

Similar to Sirius Web Advanced : Customize and Extend the Platform (20)

PDF
@Ionic native/google-maps
PDF
Zero to One : How to Integrate a Graphical Editor in a Cloud IDE (27.10.2021)
PDF
Maps API on_mobile_dev_festbangkok
PDF
Intro To Google Maps
PDF
Intro to HTML5
PPTX
SharePoint Saturday Belgium 2018 - APIs, APIs everywhere!
PPTX
APIs, APIs Everywhere!
PPTX
ArcGIS API for Javascript Tutorial
PDF
Developing Spatial Applications with Google Maps and CARTO
PPTX
SharePoint Conference 2018 - APIs, APIs everywhere!
PDF
22Flutter.pdf
PDF
GoogleVisualr - A Ruby library for Google Visualization API
PDF
Angular server side rendering - Strategies & Technics
PDF
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
PPT
INAC Online Hazards Database App
PDF
Webgl para JavaScripters
PDF
Adding where to your ruby apps
PDF
GDG Mediterranean Dev Fest Code lab #DevFestMed15 da android ad android wear ...
PDF
Building Universal Web Apps with React ForwardJS 2017
PDF
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
@Ionic native/google-maps
Zero to One : How to Integrate a Graphical Editor in a Cloud IDE (27.10.2021)
Maps API on_mobile_dev_festbangkok
Intro To Google Maps
Intro to HTML5
SharePoint Saturday Belgium 2018 - APIs, APIs everywhere!
APIs, APIs Everywhere!
ArcGIS API for Javascript Tutorial
Developing Spatial Applications with Google Maps and CARTO
SharePoint Conference 2018 - APIs, APIs everywhere!
22Flutter.pdf
GoogleVisualr - A Ruby library for Google Visualization API
Angular server side rendering - Strategies & Technics
Maciej Treder ''Angular Universal - a medicine for the Angular + SEO/CDN issu...
INAC Online Hazards Database App
Webgl para JavaScripters
Adding where to your ruby apps
GDG Mediterranean Dev Fest Code lab #DevFestMed15 da android ad android wear ...
Building Universal Web Apps with React ForwardJS 2017
ESRI Dev Meetup: Building Distributed JavaScript Map Widgets
Ad

More from Obeo (20)

PDF
Digitally assisted design for safety analysis
PDF
INCOSE IS 2023 | You deserve more than the best in class MBSE tool
PDF
CapellaDays2022 | Saratech | Interface Control Document Generation and Linkag...
PDF
CapellaDays2022 | Politecnico di Milano | Interplanetary Space Mission as a r...
PDF
CapellaDays2022 | NavalGroup | Closing the gap between traditional engineerin...
PDF
CapellaDays2022 | Thales | Stairway to heaven: Climbing the very first steps
PDF
CapellaDays2022 | COMAC - PGM | How We Use Capella for Collaborative Design i...
PDF
CapellaDays2022 | CILAS - ArianeGroup | CILAS feedback about Capella use
PDF
CapellaDays2022 | SIEMENS | Expand MBSE into Model-based Production Engineeri...
PDF
Gestion applicative des données, un REX du Ministère de l'Éducation Nationale
PDF
Simulation with Python and MATLAB® in Capella
PDF
From Model-based to Model and Simulation-based Systems Architectures
PDF
Sirius Project, Now and In the Future
PDF
Visualizing, Analyzing and Optimizing Automotive Architecture Models using Si...
PDF
Defining Viewpoints for Ontology-Based DSLs
PDF
Development of DSL for Context-Aware Mobile Applications
PDF
SimfiaNeo - Workbench for Safety Analysis powered by Sirius
PDF
Get into MBSE-MBSA process with a dedicated toolchain
PDF
Capella annual meeting 2022
PDF
Générez automatiquement vos diagrammes d'architecture | Webinaire Obeo SmartEA
Digitally assisted design for safety analysis
INCOSE IS 2023 | You deserve more than the best in class MBSE tool
CapellaDays2022 | Saratech | Interface Control Document Generation and Linkag...
CapellaDays2022 | Politecnico di Milano | Interplanetary Space Mission as a r...
CapellaDays2022 | NavalGroup | Closing the gap between traditional engineerin...
CapellaDays2022 | Thales | Stairway to heaven: Climbing the very first steps
CapellaDays2022 | COMAC - PGM | How We Use Capella for Collaborative Design i...
CapellaDays2022 | CILAS - ArianeGroup | CILAS feedback about Capella use
CapellaDays2022 | SIEMENS | Expand MBSE into Model-based Production Engineeri...
Gestion applicative des données, un REX du Ministère de l'Éducation Nationale
Simulation with Python and MATLAB® in Capella
From Model-based to Model and Simulation-based Systems Architectures
Sirius Project, Now and In the Future
Visualizing, Analyzing and Optimizing Automotive Architecture Models using Si...
Defining Viewpoints for Ontology-Based DSLs
Development of DSL for Context-Aware Mobile Applications
SimfiaNeo - Workbench for Safety Analysis powered by Sirius
Get into MBSE-MBSA process with a dedicated toolchain
Capella annual meeting 2022
Générez automatiquement vos diagrammes d'architecture | Webinaire Obeo SmartEA

Recently uploaded (20)

PDF
System and Network Administration Chapter 2
PDF
AI in Product Development-omnex systems
PPTX
Introduction to Artificial Intelligence
PPTX
Online Work Permit System for Fast Permit Processing
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
medical staffing services at VALiNTRY
PPTX
ISO 45001 Occupational Health and Safety Management System
PPTX
CHAPTER 2 - PM Management and IT Context
PDF
Design an Analysis of Algorithms II-SECS-1021-03
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
How to Migrate SBCGlobal Email to Yahoo Easily
PPTX
ai tools demonstartion for schools and inter college
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
PPTX
Transform Your Business with a Software ERP System
PPTX
VVF-Customer-Presentation2025-Ver1.9.pptx
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PPT
Introduction Database Management System for Course Database
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
System and Network Administration Chapter 2
AI in Product Development-omnex systems
Introduction to Artificial Intelligence
Online Work Permit System for Fast Permit Processing
Which alternative to Crystal Reports is best for small or large businesses.pdf
medical staffing services at VALiNTRY
ISO 45001 Occupational Health and Safety Management System
CHAPTER 2 - PM Management and IT Context
Design an Analysis of Algorithms II-SECS-1021-03
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
How to Migrate SBCGlobal Email to Yahoo Easily
ai tools demonstartion for schools and inter college
Softaken Excel to vCard Converter Software.pdf
Raksha Bandhan Grocery Pricing Trends in India 2025.pdf
Transform Your Business with a Software ERP System
VVF-Customer-Presentation2025-Ver1.9.pptx
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
Introduction Database Management System for Course Database
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf

Sirius Web Advanced : Customize and Extend the Platform

  • 1. Sirius Web Advanced: Customize and extend the platform Stéphane Bégaudeau Sirius Web Architect stephane.begaudeau@obeo.fr | sbegaudeau
  • 2. Sirius Web ■ Everything you liked in Sirius Desktop, available on a modern cloud-based stack ■ Graphical and Domain specific tooling ■ Defined by a configuration file ■ Deployed on a web server ■ Rendered in a web browser ■ Collaborative support https://www.eclipse.org/sirius/sirius-web.html
  • 3. Obeo Studio ■ All of Sirius Web with additional collaborative and access control features ■ Authentication and authorization ■ Public/Private projects ■ Role based access control ■ Indicators of active users https://www.obeosoft.com/en/products/obeo-studio
  • 4. Completely customizable ■ Configure Sirius Web and Obeo Studio with the concepts from your domain ■ Define the graphical representation that you need ■ Diagrams ■ Tools ■ Forms ■ Validation ■ Import existing Sirius desktop configuration easily
  • 5. What’s in Sirius Web? READY-TO-USE Modeling framework to define and render graphical applications in the web
  • 6. What’s in Sirius Web? READY-TO-USE Modeling framework to define and render graphical applications in the web MODEL SERVER Open source model server components with a GraphQL API
  • 7. What’s in Sirius Web? READY-TO-USE Modeling framework to define and render graphical applications in the web MODEL SERVER MODEL APPLICATION Open source model application (diagram, properties, forms…) Open source model server components with a GraphQL API
  • 8. Built on top of awesome technologies
  • 9. The Sirius Web ecosystem
  • 11. Code-based customization ■ Customize icons ■ Customize the child creation proposals available in the explorer ■ Advanced behavior for diagram tools ■ Java services ■ Java based representation descriptions
  • 12. Provide Java services @Service public class CustomServicesProvider implements IJavaServiceProvider { @Override public List<Class<?>> getServiceClasses(View view) { return List.of(CustomServices.class); } } public class CustomServices { public String getValue(EObject self, String name) { return self.eClass().eGet(self.eClass().getEStructuralFeature(name)).toString(); } } aql:self.getValue('name')
  • 13. Register representation descriptions @Configuration public class RepresentationDescriptionRegistryConfigurer implements IRepresentationDescriptionRegistryConfigurer { @Override public void addRepresentationDescriptions(IRepresentationDescriptionRegistry registry) { DiagramDescription diagramDescription = DiagramDescription.newDiagramDescription("customDiagram") .nodeDescriptions(List.of()) .edgeDescriptions(List.of()) .toolSections(List.of()) .build(); registry.add(diagramDescription); } }
  • 14. Extension of the platform
  • 15. Let’s extend the platform! ■ Add a new kind of representation ■ Contribute it to our backend ■ Describe it in our GraphQL API ■ Integrate it in the frontend of your application ■ Synchronize our data with another application
  • 17. Map based representation ■ Map representation based on Google Maps ■ A dedicated metamodel ■ to create semantic elements with map-related attributes ■ Display and refresh our map in real time when the objects are modified
  • 18. Backend ■ Add support for the map representation ■ Representation description and instance ■ Creation process ■ Event processor ■ GraphQL API
  • 19. Register metamodel @Configuration public class MapPackageConfiguration { @Bean public EPackage mapPackage() { EClass mapEClass = EcoreFactory.eINSTANCE.createEClass(); mapEClass.setName("Map"); // Contribute the attributes of the class: lat, lng, zoom EPackage mapEPackage = EcoreFactory.eINSTANCE.createEPackage(); mapEPackage.setName("Map"); mapEPackage.setNsPrefix("map"); mapEPackage.setNsURI("https://www.eclipse.org/sirius/map"); mapEPackage.getEClassifiers().add(mapEClass); return mapEPackage; } }
  • 22. MapDescription.java public class MapDescription implements IRepresentationDescription { @Override public String getId() { return "map"; } @Override public String getLabel() { return "Map"; } @Override public Predicate<VariableManager> getCanCreatePredicate() { return variableManager -> variableManager.get("class", EClass.class) .filter(eClass -> eClass.getEPackage().getNsURI().equals("https://www.eclipse.org/sirius/map")) .isPresent(); } }
  • 23. Map.java public class Map implements IRepresentation, ISemanticRepresentation { public static final String KIND = IRepresentation.KIND_PREFIX + "?type=Map"; private String id; private String descriptionId = "map"; private String targetObjectId; private String label; private double lng; private double lat; private int zoom; }
  • 24. Representation description registration @Configuration public class MapRepresentationDescriptionRegistryConfigurer implements IRepresentationDescriptionRegistryConfigurer { @Override public void addRepresentationDescriptions(IRepresentationDescriptionRegistry registry) { registry.add(new MapDescription()); } }
  • 25. Create maps @Service public class CreateMapEventHandler implements IEditingContextEventHandler { @Override public boolean canHandle(IEditingContext editingContext, IInput input) { // Check that the input is for the creation of a map representation } @Override public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDescriptionSink, IEditingContext editingContext, IInput input) { Map map = new Map(...); this.representationPersistenceService.save(editingContext, map); changeDescriptionSink.tryEmitNext( new ChangeDescription(ChangeKind.REPRESENTATION_CREATION, editingContext.getId(), input) ); payloadSink.tryEmitValue(new CreateRepresentationSuccessPayload(input.getId(), map)); } }
  • 26. Tell Jackson how to read the map @Service public class MapDeserialiser implements IRepresentationDeserializer { @Override public boolean canHandle(ObjectNode root) { return Optional.ofNullable(root.get("kind")) .map(JsonNode::asText) .filter(Map.KIND::equals) .isPresent(); } @Override public Optional<IRepresentation> handle(ObjectMapper mapper, ObjectNode root) { try { return Optional.of(mapper.readValue(root.toString(), Map.class)); } catch (JsonProcessingException exception) {} return Optional.empty(); } }
  • 29. GraphQL Schema extend type Subscription { mapEvent(input: MapEventInput!): MapEventPayload! } input MapEventInput { id: ID! editingContextId: ID! mapId: ID! } union MapEventPayload = ErrorPayload | SubscribersUpdatedEventPayload | MapRefreshedEventPayload type MapRefreshedEventPayload { id: ID! map: Map! } type Map implements Representation { id: ID! metadata: RepresentationMetadata! lng: Float! lat: Float! zoom: Int! } type MapDescription implements RepresentationDescription {}
  • 30. GraphQL Subscription @SubscriptionDataFetcher(type = "Subscription", field = "mapEvent") public class SubscriptionMapEventDataFetcher implements IDataFetcherWithFieldCoordinates<Publisher<IPayload>> { @Override public Publisher<IPayload> get(DataFetchingEnvironment environment) throws Exception { Object argument = environment.getArgument("input"); var input = this.objectMapper.convertValue(argument, MapEventInput.class); var mapConfiguration = new MapConfiguration(input.getMapId()); return this.editingContextEventProcessorRegistry .getOrCreateEditingContextEventProcessor(input.getEditingContextId()) .flatMap(processor -> processor.acquireRepresentationEventProcessor( IMapEventProcessor.class, mapConfiguration, input )) .map(representationEventProcessor -> representationEventProcessor.getOutputEvents(input)) .orElse(Flux.empty()); } }
  • 31. Subscribe to the representation @Service public class MapEventProcessorFactory implements IRepresentationEventProcessorFactory { @Override public <T extends IRepresentationEventProcessor> Optional<T> createRepresentationEventProcessor(Class<T> representationEventProcessorClass, IRepresentationConfiguration configuration, IEditingContext editingContext) { var optionalMap = this.representationSearchService.findById(editingContext, mapConfiguration.getId(), Map.class); if (optionalMap.isPresent()) { Map map = optionalMap.get(); IRepresentationEventProcessor mapEventProcessor = new MapEventProcessor(...); return Optional.of(mapEventProcessor) .filter(representationEventProcessorClass::isInstance) .map(representationEventProcessorClass::cast); } return Optional.empty(); } }
  • 32. Subscribe to the representation public class MapEventProcessor implements IMapEventProcessor { @Override public void refresh(ChangeDescription changeDescription) { // Refresh, save the new version of the map and send it using the mapEventFlux } @Override public Flux<IPayload> getOutputEvents(IInput input) { return Flux.merge( this.mapEventFlux.getFlux(input), this.subscriptionManager.getFlux(input) ); } @Override public void dispose() { this.subscriptionManager.dispose(); this.mapEventFlux.dispose(); } }
  • 34. Frontend ■ On the frontend side ■ New representation component using React ■ Register the component in the entry point of the application ■ Subscribe to map events using our GraphQL API
  • 35. MapRepresentation.tsx export const MapRepresentation = ({ editingContextId, representationId }: RepresentationComponentProps) => { const [state, setState] = useState<MapRepresentationState>({ center: null, zoom: 10 }); useSubscription(subscription, { ... }); return ( <div> {state.center ? ( <GoogleMapReact bootstrapURLKeys={{ key: 'XXXXXXXXXXXX' }} center={state.center} zoom={state.zoom}></GoogleMapReact> ) : null} </div> ); };
  • 36. MapRepresentation.tsx const subscription = gql` subscription getMapEvent($input: MapEventInput!) { mapEvent(input: $input) { ... on MapRefreshedEventPayload { map { lng lat zoom } } } } `;
  • 37. MapRepresentation.tsx useSubscription(subscription, { variables, fetchPolicy: 'no-cache', onSubscriptionData: ({ subscriptionData }) => { const { data } = subscriptionData; if (data && data.mapEvent.__typename === 'MapRefreshedEventPayload') { const { map: { lng, lat, zoom } } = data.mapEvent; setState((prevState) => { return { ...prevState, center: { lng, lat }, zoom }; }); } }, });
  • 38. index.tsx const registry = { getComponent: (representation: Representation): RepresentationComponent | null => { const query = representation.kind.substring(representation.kind.indexOf('?') + 1, representation.kind.length); const params = new URLSearchParams(query); const type = params.get('type'); if (type === 'Map') { return MapRepresentation; } else if (type === 'Diagram') { return DiagramWebSocketContainer; } return null; }, };
  • 39. index.tsx ReactDOM.render( <ApolloProvider client={ApolloGraphQLClient}> <BrowserRouter> <ThemeProvider theme={siriusWebTheme}> <CssBaseline /> <div style={style}> <RepresentationContext.Provider value={{ registry }}> <Main /> </RepresentationContext.Provider> </div> </ThemeProvider> </BrowserRouter> </ApolloProvider>, document.getElementById('root') );
  • 42. DEMO
  • 43. Additional ideas ■ Update the properties of the object when the map is modified ■ Listen to change in coordinates or zoom level ■ Send a mutation to the backend ■ Update the data in the MapEventProcessor and refresh the map
  • 45. Publish a gist with the coordinates
  • 46. MapEventProcessor.java public class MapEventProcessor implements IMapEventProcessor { @Override public void refresh(ChangeDescription changeDescription) { // Refresh, save the new version of the map and send it using the mapEventFlux LocalDateTime now = LocalDateTime.now(); String datetime = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); String content = datetime + " --- Latitude: " + lat + ", Longitude: " + lng; //$NON-NLS-1$//$NON-NLS-2$ String body = "{"description":"Update","files":{"MapData":{"content":"" + content + ""}}}"; var uri = URI.create("https://api.github.com/gists/58875db0a0146ccd7d17945079f489e1"); var httpClient = HttpClient.newHttpClient(); var httpRequest = HttpRequest.newBuilder() .uri(uri) .method("PATCH", HttpRequest.BodyPublishers.ofString(body)) .header("Accept", "application/vnd.github.v3+json") .header("Authorization", "token XXXXXXXXXXXXXXXXXXX") .build(); httpClient.send(httpRequest, BodyHandlers.ofString()); } }
  • 47. Synchronize with a remote service ■ You can check to see if there are any difference with the previous version first ■ Perform the request asynchronously to improve performance ■ In order to synchronize data the other way ■ Send a GraphQL query to Sirius Web ■ Contribute an IEditingContextEventHandler to perform the change
  • 50. Integration in web applications MODEL SERVER Leverage our GraphQL API over HTTP and WebSocket to interact with your servers GRAPHICAL EDITORS Manipulate your Sirius Web graphical editors from your app (diagrams, forms, etc)
  • 51. Getting started ■ To start integrating Sirius Web in a cloud IDE, you’ll need ■ The latest release of @eclipse-sirius/sirius-components ■ React components for our graphical editors ■ An instance of a Sirius Web server ■ HTTP and WebSocket GraphQL API
  • 54. Node
  • 56. Project TreeView ■ Used to manipulate Sirius Web / Obeo Studio projects ■ Leverage our GraphQL API over HTTP
  • 57. Project TreeView ■ Retrieve the projects using a GraphQL query private fetchProjects(): Promise<ProjectData[]> { const queryURL = `${this.serverAddress}/api/graphql`; const headers = { headers: { Cookie: this.cookie } }; const graphQLQuery = ` query getProjects($page: Int!) { viewer { id projects(page: $page) { edges { node { id name visibility } } } } } `;
  • 58. Explorer TreeView ■ Used to display the model elements from the project ■ Based on the configuration of the explorer of the server ■ Can be parameterized ■ Based on a tree representation ■ Using a GraphQL subscription for real time update ■ Based on the graphql-ws protocol
  • 62. VS Code WebView public static getWebviewContent(webView: vscode.Webview, webViewContext: WebViewContext): string { const reactAppPathOnDisk = vscode.Uri.file(path.join(webViewContext.extensionPath, 'siriusweb', 'siriusweb.js')); const reactAppUri = webView.asWebviewUri(reactAppPathOnDisk); return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>${webViewContext.representationLabel}</title> <script> window.acquireVsCodeApi = acquireVsCodeApi; window.serverAddress = '${webViewContext.serverAddress}'; window.username = '${webViewContext.username}'; window.password = '${webViewContext.password}'; window.editingContextId = '${webViewContext.editingContextId}'; window.representationId = '${webViewContext.representationId}'; window.representationLabel = '${webViewContext.representationLabel}'; window.representationKind = '${webViewContext.representationKind}'; </script> </head> <body> <script src="${reactAppUri}"></script> </body> </html>`; }
  • 63. VS Code WebView import { DiagramWebSocketContainer, PropertiesWebSocketContainer, Selection } from '@eclipse-sirius/sirius-components'; export const App = ({...}: AppProps) => { let component; if (representationKind === 'Diagram') { component = ( <DiagramWebSocketContainer editingContextId={state.editingContextId} representationId={state.representationId} readOnly={false} selection={state.selection} setSelection={setSelection} /> ); } else { component = ( <PropertiesWebSocketContainer editingContextId={state.editingContextId} readOnly={false} selection={state.selection} /> ); } }