How to add a document and a link to that document within 1 rpc request?

In an import script I would like to add a document and a link from an existing document to that newly added document. The rpc request body is (as intended by me):

[
    {
        "jsonrpc": "2.0",
        "id": "ListProjectBuffetConfigurator",
        "method": "LIST_DOCUMENTS",
        "params": {
            "documentModelName": "ProjectDM",
            "filter": {
                "filters": [
                    "Info.Name:Buffet Configurator"
                ],
                "lang": "en"
            },
            "sort": [],
            "page": {
                "offset": 0,
                "limit": 1
            },
            "facets": []
        }
    },
    {
        "jsonrpc": "2.0",
        "id": "AddLoginWorkaroundPage",
        "method": "ADD_DOCUMENT",
        "params": {
            "document": {
                "GeneralInfo": {
                    "pageType": "custom",
                    "label": "Login Workaround",
                    "name": "login-workaround",
                    "path": "login-workaround",
                    "hidden": false
                }
            },
            "documentModelName": "CustomPageDM",
            "locale": "en"
        }
    },
    {
        "jsonrpc": "2.0",
        "id": "AddLoginWorkaroundPageToProject",
        "method": "ADD_LINK",
        "params": {
            "linkDescriptor": {
                "relationshipModel": "ProjectAbstractPageRM",
                "entities": [
                    {
                        "role": "Project",
                        "docRef": "#{#ListProjectBuffetConfigurator.entries}"
                    },
                    {
                        "role": "Page",
                        "docRef": "#{#AddLoginWorkaroundPage.docRef}"
                    }
                ]
            }
        }
    }
]

However when executing the request it fails with this response:

[
	{
		"jsonrpc": "2.0",
		"id": "ListProjectBuffetConfigurator",
		"result": {
			"fullSize": 1,
			"page": {
				"offset": 0,
				"limit": 1
			},
			"entries": [
				{
					"docRef": "ProjectDM/7447b4b0-d913-4f58-ae8b-a66229e04b92",
					"documentModelName": "ProjectDM",
					"document": {
						"Info": {
							"Name": "Buffet Configurator"
						},
						"__meta": {
							"creator": "superUser",
							"modifier": "superUser",
							"createdAt": "2025-01-28T15:04:48",
							"modifiedAt": "2025-01-28T15:04:48",
							"modelReference": "ProjectDM",
							"docRef": "ProjectDM/7447b4b0-d913-4f58-ae8b-a66229e04b92",
							"modelVersion": null
						}
					}
				}
			]
		}
	},
	{
		"jsonrpc": "2.0",
		"id": "AddLoginWorkaroundPage",
		"result": {
			"docRef": "CustomPageDM/f62af1d4-a9d2-4594-bdd9-b7e2756801e8"
		}
	},
	{
		"jsonrpc": "2.0",
		"id": "AddLoginWorkaroundPageToProject",
		"error": {
			"code": -32002,
			"message": "JSON-RPC Request failedand rollback was performed",
			"data": {
				"level": "ERROR",
				"title": {
					"key": "rpc.operation.error",
					"default": "JSON-RPC Request failed and rollback was performed"
				},
				"description": {
					"key": "error.convert.json",
					"default": "SpEL evaluation error occurred!"
				},
				"details": {
					"code": "-32002",
					"subsystem": "GENERIC",
					"time": "2025-01-28T16:35:47.694+0100"
				},
				"source": "",
				"timestamp": "2025-01-28T15:35:47.705+0000",
				"logId": "8f374e43-d087-4c9c-85e8-a596f6c0073c"
			}
		}
	}
]

When debugging through the code evaluating the SpEL expression I see that only a variable AddLoginWorkaroundPage is defined. But not one named ListProjectBuffetConfigurator. It seems to me the context only keeps the last defined variable. The SpEL exception generated by spring complains about property entries not being present on null.

Is this intended behaviour?

And how should I refer to the single doc ref of the document returned in the ListProjectBuffetConfigurator operation? When I use the SpEL "docRef": "#{#ListProjectBuffetConfigurator.entries[0].docRef}" the SpEL evaluation doesn’t kick in (presumably because of the array index operator is in use).

I just noticed that the LIST_DOCUMENTS operation doesn’t make use of the operation context at all. So there is currently no way to make this work with using this operation.

I ended up implementing an RPC operation to achieve the effect I wanted:

@Component
@RequiredArgsConstructor
@RemoteOperation(name = "LIST_SINGLE_DOCUMENT")
public class ListSingleDocumentOperation {

	private final ListDocumentsOperation listDocumentsOperation;

	public DocumentSpec rpc(@NonNull @JsonRpcParam("documentModelName") String documentModelName,
			@JsonRpcParam("filter") FilterSpec filterSpec, @JsonRpcParam("sort") List<SortSpec> sortSpec,
			@JsonRpcParam("facets") Collection<? extends AbstractFacetQuery> facets) {
		PageSpec pageSpec = new PageSpec();
		pageSpec.setLimit(1);
		pageSpec.setOffset(0);
		ResultSet<DocumentSpec> result = listDocumentsOperation.rpc(documentModelName, filterSpec, sortSpec, pageSpec,
				facets);
		DocumentSpec foundDocument = result.getEntries()
				.stream()
				.findFirst()
				.orElseThrow(() -> new NotFoundException(ExceptionKeys.DOCUMENT_NOT_FOUND_ERROR_KEY,
						String.format("Not a single document found in search.")));
		OperationContextHolder.put(foundDocument);
		return foundDocument;
	}
}

This only returns the first document found and puts it into the operation context. By returning only the first element the array index in SpEL expressions is also avoided.

Moin @andreas-fresh-mesa,
please be aware that the ListDocumentsOperation is an internal Data Services service, meaning that it won’t be covered under the breaking change policy.

You should be able to use the DocumentQueryService which is public API and is also used by the ListDocumentsOperation under the hood.

Kind regards,
Jan