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, the inbuilt AI assistant knows the format and using it is recommended.
Manual verification and debugging can be iterated and verified in https://try.jsonata.org/.
Example jsonata transformer configuration (see transformer.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",
"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) )"
}
}
Output Format
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 prompt to generte JSONata outside of the built-in assistant
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, [])
Reserved graph keys (JSONata)
The graph uses these keys with reserved meaning:
idandtypefor all elements.sourceandtargetfor edges.
For edges, source and target must be ids of connected nodes.
If your payload already has source or target fields with another meaning, remap those payload fields to non-reserved names (for example payloadSource and payloadTarget) in your JSONata expression to avoid conflicts with internal graph behavior.
Example pattern (preserve payload fields + produce valid edge keys):
{
"id": "EDGE_1",
"type": "ChildClasses",
"source": "NODE_PARENT_ID",
"target": "NODE_CHILD_ID",
"payloadSource": "raw payload source value",
"payloadTarget": "raw payload target value"
}
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 macro is identified with double curly brackets e.g. {{contextId}}. Macros are resolved in two phases:
- Config initialization —
{{pref.*}}and{{env.*}}macros are resolved across the entire configuration (including datasets, services, URIs, payloads, and any other string value). - Service invocation — Runtime macros like
{{contextId}}and{{securityContext}}are resolved per service call.
| 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 |
secondaryContextId | Physical id of secondary context object |
securityContext | The active security context |
tenant | The current tenant identifier |
widgetId | The widget instance id |
datetime | Current date/time in ISO format |
select:<property> | select a property off the node |
pref.<name> | Value of a widget preference matching <name>. Resolved at config init time. See Preference macros. |
env.<name> | Value of an environment variable matching <name>. Resolved at config init time. |
"exampleUsingMacros" : {
"uri": "/resources/issues/batch?tenant={{tenant}}&xrequestedwith=xmlhttprequest",
...
"payload": {
"requests": [
{
...
"url": "resources/issues/{{contextId}}/related",
"body": {
"physicalId": "{{contextId}}",
}
}
]
}
}
Preference macros
Widget preferences defined in the configuration's preferences array can be injected into any string value in the config using the {{pref.<name>}} syntax. This works in service URIs, payloads, dataset configurations, headers, and any other config field.
Define preferences in the root configuration:
"preferences": [
{
"type": "text",
"name": "promote-state",
"label": "Promote To State"
},
{
"type": "text",
"name": "expand-level",
"label": "Expand Level"
}
]
Then reference them anywhere in the configuration:
"promoteService": {
"uri": "/resources/v1/modeler/dslc/maturity/changeState",
"method": "POST",
"payload": {
"data": [
{
"id": "{{contextId}}",
"nextState": "{{pref.promote-state}}"
}
]
},
"addCSRFToken": true,
"securityContextHeaderPattern": "ctx::{{securityContext}}"
}
In this example, {{pref.promote-state}} is resolved first (from the user-set preference value), then {{contextId}} and {{securityContext}} are resolved at service call time.
Preference macros work in datasets and all other configuration areas too — not just services. They are resolved on the entire config object before any component processes it.
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 | Controls whether a service should be called for the current context element. Supports condition.node and condition.edge cytoscapejs selector expressions. | object | no | "condition": {"node": "[type='VPMReference']"} |
contextParams | Context variables for the service. | array string | no | ["contextId"] |
datasets | Links to datasets (custom extension). (See more) | string | no | "changeRequestData" |
deferred | If true, excludes the service from the initial parallel batch and executes it only after all non-deferred services (including their chained services) have completed. | boolean | no | true |
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 | 3dpsace\n3dplan |
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","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"} |
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.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", "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) )" } } |
Search
This section summarizes the $searchStr / searchStr syntax. The official documentation is weak and information is scatteded in many different examples. This section summarize that information. There are two variants used in the platform, in practice, both refer to the same search string concept. Which one to use depends on endpoint.
The search parameter value is a single string expression.
- Free text term
- Structured filter inside parentheses, for example
(current:Frozen),(modified>"2021-09-01T00:00:00Z") - Multiple criteria combined with
+,OR,AND
URL Example:
GET resources/v1/modeler/cdoc/documents/search?searchStr=TitleOfDocument+(current:Frozen)&$top=30&$skip=10
searchStr Examples:
searchStr=Widget
searchStr=Widget+(current:Frozen)
searchStr=Widget+(current:Frozen)+(type:Document)
searchStr="FYD" OR (modified>"2021-09-01T00:00:00Z" AND modified<"2021-09-23T00:00:00Z")
Pagination and limits
Many endpoints that describe searchStr or $searchStr also document:
- Default page size around 50 items
- Optional increase with
$top, often up to 1000 - Pagination with
$skip