Services
Services
Services are configured separately and independently from each UI component, please refer to Understanding the graph model for better understanding of the high level solution and how services fits the overall picture.
Transformers - Parsing service data
To populate the client data store (graph), a transformer is applied. The transformer parses the service response payload into nodes and edges and populates the graph data store. To facilitate standard and documented services, built-in transformers are included. It is possible to register you own transformers to parse any undocumented or external service into the data store, and use the UI components just as usual for any data. See Resource deployment for detail on how to deploy plugin code. Refer to Plugin code extensions for transformer coding details. To use an extension point it out in your widget definition Widget.
The framework comes with several built-in transformers. The transformer is defined using the transformer property.
JSONata configurable transformer
To enable parsing of most payload formats a highly configurable jsonata transformer is included. JSONata is very versatile and could be used for most cases to avoid writing custom parsing code.
JSONata is a lightweight query and transformation language for JSON data. Inspired by the 'location path' semantics of XPath 3.1, it allows sophisticated queries to be expressed in a compact and intuitive notation. A rich complement of built in operators and functions is provided for manipulating and combining extracted data, and the results of queries can be formatted into any JSON output structure using familiar JSON object and array syntax. Coupled with the facility to create user defined functions, advanced expressions can be built to tackle any JSON query and transformation task.
JSONata syntax can be tricky, use of AI is recommended (https://claude.ai/ is a great option). The result can be iterated and verified in https://try.jsonata.org/.
Your JSONata expression must produce the following format to be accepted by the graph:
[
{
"id" : "nodeID",
"type" : "nodeType",
...additional properties goes here
},
{
"id" : "edgeID",
"type" : "edgeType",
"source" : "nodeID",
"target" : "nodeID",
...additional properties goes here
}
]
Example jsonata transformer configuration (see transformer.inputs.expression)
"dslibLibraryClassifiableClasses" : {
"description": "Expands a libaray onto subclasses to which the user has Classify Access",
"uri": "/resources/v1/modeler/dslib/dslib:Library/{{contextId}}?$mask=dslib:ExpandClassifiableClassesMask",
"transformer": {
"name": "jsonata",
"inputs": {
"expression": "( $f := function($n, $p) { $n.member.( $c := $; $o := $sift($c, function($v, $k) { $k != 'ChildClasses' }); $r := $p ? [ $o, { 'id': $p & '_ChildClasses_' & $c.id, 'type': 'ChildClasses', 'source': $p, 'target': $c.id } ] : [ $o ]; $append($r, $f($c.ChildClasses, $c.id)) ) }; $f($, null) )"
}
}
}
Example prompt to generte JSONata
Create a JSONata (http://docs.jsonata.org) expression that transforms my 'Example Payload' so that it converts all members into the 'Target Format'.
- Include all properties except ChildClasses
- Use single quotes
- Make it a one liner
Target format:
'''
[
{
"id" : "nodeID",
"type" : "nodeType",
...additional properties goes here
},
{
"id" : "<parentNodeID>_ChildClasses_<nodeID>",
"type" : "ChildClasses",
"source" : "parentNodeID",
"target" : "nodeID",
...additional properties goes here
}
]
'''
Example Payload:
'''
{
"totalItems": 1,
"member": [
{
"id": "BCA5C3EC746500006231E6320002BA22",
"name": "Library-R1132101359392-00000001",
"title": "Electronic components",
"libraryUsage": "Definition",
"description": "",
"type": "General Library",
"modified": "11\/5\/2025 8:48:55 AM",
"created": "3\/16\/2022 1:29:22 PM",
"revision": "-",
"state": "Active",
"owner": "Philippe RINERO",
"organization": "Company Name",
"collabspace": "Common Space",
"ChildClasses": {
"totalItems": 1,
"member": [
{
"id": "BCA5C3EC746500006231E6550010F302",
"name": "Class-R1132101359392-00000001",
"title": "Passive",
"classUsage": "Definition",
"description": "",
"type": "General Class",
"state": "Active",
"modified": "11\/5\/2025 8:48:55 AM",
"created": "3\/16\/2022 1:29:57 PM",
"revision": "BCA5C3EC746500006231E6320002BA22",
"owner": "Philippe RINERO",
"organization": "Company Name",
"collabspace": "Common Space",
"ChildClasses": {
"totalItems": 1,
"member": [
{
"id": "BCA5C3EC746500006231E7D10005BD88",
"name": "Class-R1132101359392-00000002",
"title": "Resistor",
"classUsage": "Definition",
"description": "",
"type": "General Class",
"state": "Active",
"modified": "11\/5\/2025 8:48:55 AM",
"created": "3\/16\/2022 1:36:17 PM",
"revision": "BCA5C3EC746500006231E6550010F302",
"owner": "Philippe RINERO",
"organization": "Company Name",
"collabspace": "Common Space"
}
]
}
}
]
}
}
],
"nlsLabel": {
"id": "Id",
"name": "Name",
"title": "Title",
"libraryUsage": "Purpose",
"classUsage": "Purpose",
"description": "Description",
"type": "Type",
"modified": "Modification Date",
"created": "Creation Date",
"revision": "Revision",
"state": "State",
"owner": "Owner"
}
}
'''
AI JSONata response (after 2 iterations asking it to fix a syntax error 't.apply is not a function' and to improve performance**)**
$reduce($map(**.member, function($n){$append([$sift($n, function($v,$k){$k!='ChildClasses'})], $n.revision!='-' ? [{'id':$n.revision&'_ChildClasses_'&$n.id,'type':'ChildClasses','source':$n.revision,'target':$n.id}] : [])}), $append, [])
Chaining services
service A → service B → service C
It is quite common that data is a combination of multiple chained services e.g. first call a service get all the instances (relationships), then based on that result get all references (objects). To chain services use the services property. A chained service will use the eles that the transformer of the parent service returns.
"services": {
"engItemChangeControl": {
"uri": "/resources/v1/modeler/dseng/dseng:EngItem/{{contextId}}/dslc:changeControl",
...
"services": ["changeAction"] // Chain call
},
"changeAction" : {
"uri": "/resources/v1/modeler/dslc/changeaction/{{contextId}}",
...
}
}
Macros
All service configuration support macros. A macros is typically a physical id or similar. A macro is identified with double curly brackets e.g. {{contextId}}.
| Key | Description |
|---|---|
contextId | Physical id of the dropped context object or the id property of the related node (e.g. for column or chained data) |
parentContextId | Physical id of the parent node of current in case structure |
securityContext | TODO |
select:<property> | select a property off the node |
"exampleUsingMacros" : {
"uri": "/resources/issues/batch?tenant={{tenant}}&xrequestedwith=xmlhttprequest",
...
"payload": {
"requests": [
{
...
"url": "resources/issues/{{contextId}}/related",
"body": {
"physicalId": "{{contextId}}",
}
}
]
}
}
Caching (performance)
This configuration based on Caching mechanism. Caching is a technique used to store data temporarily so that it doesn't need to be fetched again from the original source every time it's requested. This can help improve performance and reduce the load on your backend systems, as repeated requests for the same data don't need to make another call to the server.
A common use case for caching is to avoid making repeated calls to the server within a short period. For example, if you are requesting a list of data multiple times within a few minutes, caching the response ensures that you don't overload the server with duplicate requests. The system will use the cached data instead of querying the server again for the same information.
- Caching helps improve performance by storing data temporarily.
- If real-time updates are not critical, cache the data to avoid unnecessary calls to the server.
- Disabling caching ensures fresh data but might reduce performance due to more frequent server calls.
"defaultServiceParams": {
"enableCache": false,
"cacheTimelimit": 1
}
| Prop Name | Description | Data Type | Required | Example |
|---|---|---|---|---|
enableCache | Enables caching if true. | boolean | no | false |
cacheTimelimit | Cache duration in minutes. | number | no | 1 |
Inherit service templates
service (based on)? service TEMPLATE
One service definition can be inherited to another using inheritedServiceName . Inheritance allows to efficiently maintain small variations in how to call a service.
"template" : {
"uri": "/resources/v1/modeler/dsiss/issue/{{contextId}}",
...
},
"inheritExample" : {
"inheritedServiceName": "template",
"uri" : "/resources/v1/modeler/dsiss/issue/{{contextId}}?$mask=example" // Inherits and overrides a prop
}
Service configuration properties
| Prop Name | Description | Data Type | Required | Example |
|---|---|---|---|---|
absoluteURI | Treats URI as absolute if true. | boolean | no | true |
addCSRFToken | Adds CSRF token for security if true. | boolean | no | true |
condition | Defines a where clause to control for what data to call the service. e.g. only call for change approval data for nodes in a certain state. | object | no | "condition": {"node": "[type='VPMReference']"s} |
contextParams | Context variables for the service. | array string | no | ["contextId"] |
datasets | Links to datasets (custom extension). (See more) | string | no | "changeRequestData" |
excludePropsFromParent | Inherit properties from the parent service while excluding a specified list of properties. | array string | no | ["uri"] |
headers | Custom HTTP headers. | object | no | {"Content-Type": "application/json"} |
inheritedServiceName | Inherit properties from another service into the current service. | string | no | "baseService" |
localization | Localized data settings. | object | no | {"lang": "en"} |
method | HTTP method (e.g., GET, PATCH, POST). Defaults to GET. | string | no | "GET" |
multipleFetch | True in case form data submit and perform some multiple service call iteratively before service chain call. | boolean | no | "multipleFetch":true |
payload | The payload content to passed with POST & PATCH requests. All properties and below structure wil be stringified and includede. The following MACROS are evaluated ['{{contextId}}',..]. For in-cell edit services, payload can use '{{FIELD_IDENTIFIER}}' and '{{FIELD_VALUE}}': FIELD_IDENTIFIER is resolved from column.edit.editSelect (or fallback column data select) and FIELD_VALUE is resolved from the edited cell value. Example: { '{{FIELD_IDENTIFIER}}': '{{FIELD_VALUE}}', 'cestamp': '{{cestamp}}' }. | object | no | {"title": "{{title}}"} |
preRequestTransformer | Pre-request payload or context data transformer. This is used in chain service input used as payload of second service. | string | no | "updateIssueTransformer" |
relationType | When a service do not include edge (relation) detail this value would be used as edge type. | string | no | "resolvedBy" |
removeNonEditedFields | Strips unchanged fields if true. | boolean | no | true |
runOnce | Call the service just once with the same arguments for that view. Useful to improve performance when the same data is present multiple times in the same view e.g. recurring person data such as owner in a table, if true. | boolean | no | true |
serviceDomain | Domain scope for the service. | string | no | "modeler" |
serviceCaller | Custom caller for the service. | string | no | "customCaller" |
serviceCallerProps | Options for the custom caller. | object | no | {"timeout": 5000} |
services | This props used to acheive chaining call another service once current complete. | array string | no | services:["fetchIssueDetails"] |
securityContextHeaderPattern | Security context for authentication. | string | no | "ctx::VPLMProjectLeader.Company Name.Common Space" |
skipGraphSave | Skips service response to persist in graph true. | boolean | no | true |
shouldBeanPersist | Set to true if the data is added to the graph node and stays in sync with the bean after the service call, if true. | boolean | no | true |
transformer | A built in or plugin transformer responsible for parsing the service response to data store. | string object | yes | {"name":"jsonata","inputs":{"expression":"( $f := function($n, $p) { $n.member.( $c := $; $o := $sift($c, function($v, $k) { $k != 'ChildClasses' }); $r := $p ? [ $o, { 'id': $p & 'ChildClasses' & $c.id, 'type': 'ChildClasses', 'source': $p, 'target': $c.id } ] : [ $o ]; $append($r, $f($c.ChildClasses, $c.id)) ) }; $f($, null) )"}} |
uri | API endpoint with optional macros. | string | yes | "/resources/issues"/{{contextId}} |
uriParams | Optional URI query params. Object keys override matching query params already defined in uri while preserving any other params from uri. | object (string values) | no | {"$top": "50", "$mask": "dseng:EngItemMask.Default"} |
validFor | Restricts service to specific object types. | string | no | "Issue" |
useRootContextIds | Set to true use context node as context id or parent Id in some special service callm. | boolean | no | true |
3DEXPERIENCE services
DS public service API
Best practice is using the documented and supported services listed on Dassault Systemes Developer Assistance (3ds.com). Widget configuration based on documented services is most stable over time and require the least possible maintenance.
DS undocumented service API
There are other undocumented platform services used by many of the OOTB Widget apps. Using such service could be beneficial and enable better performance or other capabilities, with the risk of change and more maintenance over time. It is possible to configure such services, jsonata can be used to parse the payload data to the data store (graph).
Authentication
All service calls uses the 3DEXPERIENCE platform authenticated API's.
Built in for 3DEXPERIENCE
To enable quick widget configuration and have standard 3DEXPERIENCE service data loaded into the graph, transformers to parse service data and complete service configurations are included. Many of the documented DS public API services are included in the built in common service configurations. The built in service configurations are found in the admin UI, from the online studio and is accessible by the AI assistant. Configurations can be included using widget the extensions property.
3DEXPERIENCE transformers
| Transformer | Description | Example |
|---|---|---|
6w | Design to parse and transform response from services classified as 6w. | "relatedDocs" : { "description": "Get all documents realated to a VPMReference", "uri": "/resources/v1/modeler/documents/parentId/{{contextId}}?parentRelName=SpecificationDocument,Reference Document", "transformer": "6w", "relationType": "Document" } |
corpus | Design to parse and transform response from services classified as corpus. | "mfgItems" : { "uri": "/resources/v1/modeler/dsmfg/dsmfg:MfgItem/search?$top=1000", "transformer": "corpus" } |
cvservlet | Design to parse and transform response from services classified as 6w. | "getFiltersDetailsFromIds": { "uri": "/resources/FilterBIAPI/Filter/getFiltersFromIds?xrequestedwith=xmlhttprequest", "method": "POST", "securityContextHeaderPattern": "ctx::{{securityContext}}", "addCSRFToken": true, "payload": ["{{contextId}}"], "transformer": "cvservlet" } |
locate | TODO | |
corpusgeneric | TODO | |
jsonata | Highly configurable transformer that evaluates a JSONata expression (transformer.inputs.expression) and maps response payloads into graph nodes and edges. | "dslibLibraryClassifiableClasses" : { "description": "Expands a libaray onto subclasses to which the user has Classify Access", "uri": "/resources/v1/modeler/dslib/dslib:Library/{{contextId}}?$mask=dslib:ExpandClassifiableClassesMask", "transformer": { "name": "jsonata", "inputs": { "expression": "( $f := function($n, $p) { $n.member.( $c := $; $o := $sift($c, function($v, $k) { $k != 'ChildClasses' }); $r := $p ? [ $o, { 'id': $p & 'ChildClasses' & $c.id, 'type': 'ChildClasses', 'source': $p, 'target': $c.id } ] : [ $o ]; $append($r, $f($c.ChildClasses, $c.id)) ) }; $f($, null) )" } } } |