crnk-framework
crnk-framework copied to clipboard
Can't create a nested resource
I am using CrnkClient to work with nested resources (#831). The URI template is
/v0/channels/{channelId}/messages
I have the following resource structure:
@JsonApiResource(type = "messages", nested = true)
class Message {
@JsonApiId
MessageId id;
@JsonApiRelationId
@NotEmpty
String channelId;
@JsonApiRelation(mappedBy = "messages")
Channel channel;
}
@JsonApiResource(type = "channels")
class Channel {
@JsonApiId
String id;
@JsonApiRelation
List<Message> messages;
}
class MessageId implements Serializable {
@JsonApiId
String id;
@JsonApiRelationId
String channelId;
// ...
}
If I try to call messageRepository.create
without setting any IDs, then CrnkClient tries to POST to /v0/messages
(which it shouldn't, because it's nested!), so I tried setting the channelId
: Of course, since this is a new message resource, I don't have an ID for it.
def m = new Message(
id: new MessageId(channelId: CHANNEL),
channelId: CHANNEL,
text: "stuff"
)
messageRepository.create(m)
This throws in ResourceRegistryImpl.getResourcePath
with nested resources must have a non-null identifier
even though for a create POST resources shouldn't have a non-null ID.
Is there any way to create a nested resource?
is there a stack trace? just with channelId should be ok
I lost track of this and had worked around it with a hand-rolled request. As there has been no Crnk release, the problem is still present.
The stack trace is
java.lang.IllegalStateException: nested resources must have a non-null identifier
at io.crnk.core.engine.internal.utils.PreconditionUtil.fail(PreconditionUtil.java:50)
at io.crnk.core.engine.internal.utils.PreconditionUtil.verify(PreconditionUtil.java:131)
at io.crnk.core.engine.internal.registry.ResourceRegistryImpl.getResourcePath(ResourceRegistryImpl.java:187)
at io.crnk.core.engine.internal.registry.ResourceRegistryImpl.getResourceUrl(ResourceRegistryImpl.java:201)
at io.crnk.core.engine.internal.utils.JsonApiUrlBuilder.buildUrlInternal(JsonApiUrlBuilder.java:114)
at io.crnk.core.engine.internal.utils.JsonApiUrlBuilder.buildUrl(JsonApiUrlBuilder.java:86)
at io.crnk.core.engine.internal.utils.JsonApiUrlBuilder.buildUrl(JsonApiUrlBuilder.java:81)
at io.crnk.client.internal.ResourceRepositoryStubImpl.computeUrl(ResourceRepositoryStubImpl.java:135)
at io.crnk.client.internal.ResourceRepositoryStubImpl.modify(ResourceRepositoryStubImpl.java:122)
at io.crnk.client.internal.ResourceRepositoryStubImpl.create(ResourceRepositoryStubImpl.java:112)
It's quite clearly a semantic error: The getResourcePath
call requires that the nested-part of the ID be non-null, even though specifically on creation the ID should be null. The problem is that computeUrl
calls buildUrl
and then "trims off" the final path-part (even though there's no spec requirement that the path be constructed in the conventional manner), and buildUrl
requires a (meaningless) value. Using the empty string gets past that, but then there's another bug: The serializer writes the (bogus) ID into the POST body:
{
"data": {
"id": "46bef8f7-444c-4581-9abe-c6e3282a8f0c--",
"type": "messages",
...
}
}
(to which the server responds with 4xx because the id
must be null).