Hi,
we are building a custom back-end operation to implement a business use-case.
In the end it just creates some new document, modifies existing document and created links between new and old documents.
We want to the operation to be transactional so we don’t end up with things we have to clean up manually.
I use the JsonRpc2RequestBuilder to build a rpc batch and then execute it at the end.
I build a small wrapper that gives me the operationId of a create document operation so i can later use it to create a link.
Here a have a problem i have to put a SPEL expression into the RelationshipRoleSpec of the linkDescriptor (com.mgmtp.a12.dataservices.relationship.spec.LinkDescriptor)
but it can only accept DocumentReference that enforces the MODEL/ID pattern so i can not just put a SPEL into it.
requestBuilderFactory.newJsonRpc2RequestBuilder()
.addMethodCall(CoreOperationConstants.ADD_LINK_OPERATION)
.putParameter(LINK_DESCRIPTOR_PARAM, linkDescriptor)
.putParameter(LINK_DOCUMENT_PARAM, linkDocument)
.id(id)
I don’t want to copy RelationshipRoleSpec because it’s part of the API.
Is there any other class that defines the API in a more “primitive” way?
Any ideas how to do this in a good way?
Thx Julian
For now we use a special DocumentReference that is marshaled as SPeL.
But we would like to have a more native solution.
/**
* A special DocumentReference that can contain a spring expression language string.
* <p>
* This is needed so we can create an ADD_LINK operation with an operation id as docRef.
* A plain DocumentReference can not be used because the <MODEL>/<ID> is enforced.
*
*/
@EqualsAndHashCode(callSuper = false)
public class OperationDocumentReference extends DocumentReference {
@Getter
private final OperationId operationId;
@Serial
private static final long serialVersionUID = 1L;
public OperationDocumentReference(@NonNull OperationId operationId) {
super(operationId.getDocumentModelId(), operationId.getId());
this.operationId = operationId;
}
@NotNull
@Override
public String toString() {
return operationId.asSpEL();
}
}
/**
* An id of an A12 JSON-RPC operation.
*/
@Getter
@ToString
@EqualsAndHashCode
public class OperationId implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private final String id;
private final String documentModelId;
public OperationId(@NonNull String documentModelId) {
this.documentModelId = documentModelId;
this.id = buildId();
}
/**
* @return the operation id as a spring expression language string.
* @link <a href="https://geta12.com/#/docs/2025.06/ext1/data_services/dataservices-documentation-src%23_spel_example">A12 SpEL</a>
*/
public String asSpEL() {
return "#{#%s.metadata.docRef}".formatted(id);
}
private static String buildId() {
// A valid A12 operation id starts with a letter and can only contain [0-9a-zA-Z._]
// @see com.mgmtp.a12.dataservices.rpc.internal.JsonNodeSpelProcessor.PATTERN_FOR_REPLACEMENT_WITH_OBJECT
return "batch_operation_" + UUID.randomUUID().toString().replace('-', '_');
}
}
Hi @julian-typed-spruce, I’m a bit confused here. First you’re talking about your custom operation, which is server stuff, but then you continue talking about an RPC request builder, which is a client part. So I’m not sure what the real issue is.
If the only problem is docref with SpEL, then you can use com.mgmtp.a12.dataservices.client.document.SpelAwareDocumentReference.
Hi it might be a bit special setup.
We make JSON RPC calls from the backend to our self to create documents and add links.
We were counseled to use the rpc endpoints instead of the internal api due to memory leaks and caching problems.
So one operation is creating a batch of basic A12 operations.
SpelAwareDocumentReference sound what i need i will try it out.
Hello @julian-typed-spruce,
Did you have the time to try it out, and did it satisfy your needs?
Yes we used it to build a batch builder that can create document and link between documents and operations and then executes the full batch.