JMS Serializer nested objects policy - symfony

I have a similar question as JMS Serializer serialize object in object with diffrent view, but I can't get it work like in the accepted answer.
I have a User model that has many Reviews, but the Reviews owner is another User. I have a serialization policy that outputs the following:
{
"id": "1",
"name": "John Doe",
"reviews": [
{
"id": "1",
"rate": "5",
"evaluator": {
"id": "2",
"name": "Alice",
"reviews": [...]
}
}, ...
]
}
The behavior makes sense since the associated (Review) owner model is the same as the parent model and therefore it's using the same serialization policy. But how could I define a custom serialization policy for the nested model so outputs me the following:
{
"id": "1",
"name": "John Doe",
"reviews": [
{
"id": "1"
"rate": "5",
"evaluator": "Alice"
}, ...
]
}

Related

Json serializer reference handling asp.net

When fetching data from a database, we have a lot of recursive and circular references. To handle this we configure the (default) json serializer to give the objects ids and refs, e.g..
{
"$id": "1",
"Children":
[
{ "$id": "2", "Children": null, "Name": "Bob Smith", "Parent": { "$ref": "1" }},
{ "$id": "3", "Children": null, "Name": "Suzy Smith", "Parent": { "$ref": "1" }}
],
"Name": "John Smith",
"Parent": null
},
{
"$ref": "2"
},
{
"$ref": "3"
}
]
My question regards the behaviour of the $id and $ref numbers when there are a lot of requests. We handle a decent amount of traffic and they would overflow fairly quickly.
We configure it as
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
});

Create a document in Cosmos DB via Logic App and PartitionKey mismatch in document & header

So I'm trying to create an Azure Logic App which will create a new record inside a Cosmos DB collection to store review results.
I've created a Cosmos DB database (called icecream) + collection (called reviews). The PartitionKey of the reviews collection is /flavorIdentifier.
In my Logic App I have a step which inserts a new document.
The code looks like this.
"Create_review": {
"inputs": {
"body": {
"flavor": "#{body('JSON_parseren_2')?['flavor']}",
"flavorIdentifier": "#{body('JSON_parseren')?['itemOrdered']}",
"id": "#{guid()}",
"review": "#{body('E-mail_met_opties_verzenden')?['SelectedOption']}"
},
"headers": {
"x-ms-documentdb-raw-partitionkey": "#body('JSON_parseren')?['itemOrdered']"
},
"host": {
"connection": {
"name": "#parameters('$connections')['documentdb']['connectionId']"
}
},
"method": "post",
"path": "/dbs/#{encodeURIComponent('icecream')}/colls/#{encodeURIComponent('reviews')}/docs"
},
"runAfter": {
"E-mail_met_opties_verzenden": [
"Succeeded"
]
},
"type": "ApiConnection"
},
So, I'm posting the message to Cosmos DB
{
"flavor": "My flavor",
"flavorIdentifier": "3",
"id": "4927042a-faa1-4e09-9c6d-d038ef659b25",
"review": "Very satisfied"
}
As you can see, I also specified the partition key to the same value as flavorIdentifier.
My guess is this should just work. But it doesn't
The error I'm receiving states
PartitionKey extracted from document doesn't match the one specified in the header
Which is strange, as both have the same value, also when checking the raw input and output. Is there anything I'm overlooking?
For completeness, the full input and output of this step.
The raw input of the failed step.
{
"host": {
"connection": {
"name": "/subscriptions/3b3729b4-021a-48b5-a2eb-47be0c7e7f44/resourceGroups/developerday-workshop/providers/Microsoft.Web/connections/documentdb"
}
},
"method": "post",
"path": "/dbs/icecream/colls/reviews/docs",
"headers": {
"x-ms-documentdb-raw-partitionkey": "3"
},
"body": {
"flavor": "My flavor",
"flavorIdentifier": "3",
"id": "4927042a-faa1-4e09-9c6d-d038ef659b25",
"review": "Very satisfied"
}
}
The raw output of the failed step.
{
"statusCode": 400,
"headers": {
"x-ms-last-state-change-utc": "Wed,27 Mar 2019 05:03:54.568 GMT",
"lsn": "1",
"x-ms-schemaversion": "1.7",
"x-ms-quorum-acked-lsn": "1",
"x-ms-substatus": "1001",
"x-ms-current-write-quorum": "3",
"x-ms-current-replica-set-size": "4",
"x-ms-xp-role": "1",
"x-ms-global-Committed-lsn": "1",
"x-ms-number-of-read-regions": "0",
"x-ms-transport-request-id": "1",
"x-ms-cosmos-llsn": "1",
"x-ms-cosmos-quorum-acked-llsn": "1",
"x-ms-session-token": "1",
"x-ms-request-charge": "1.24",
"x-ms-serviceversion": "version=2.2.0.0",
"x-ms-activity-id": "bd428162-ec1a-4f50-879a-04b8ca0817a1",
"Strict-Transport-Security": "max-age=31536000",
"x-ms-gatewayversion": "version=2.2.0.0",
"Timing-Allow-Origin": "*",
"x-ms-apihub-cached-response": "false",
"Date": "Wed, 27 Mar 2019 11:39:57 GMT",
"Content-Length": "1149",
"Content-Type": "application/json"
},
"body": {
"code": "BadRequest",
"message": "Message: {\"Errors\":[\"PartitionKey extracted from document doesn't match the one specified in the header\"]}\r\nActivityId: bd428162-ec1a-4f50-879a-04b8ca0817a1, Request URI: /apps/9bc5d0cc-9b7c-4b1d-9be2-0fa2654271c4/services/9d84c048-383b-498e-8472-1f57da72135d/partitions/0f29761a-9ffc-4560-94ce-0328c3c79f92/replicas/131981366274575376p/, RequestStats: \r\nRequestStartTime: 2019-03-27T11:39:57.0795308Z, RequestEndTime: 2019-03-27T11:39:57.0995601Z, Number of regions attempted: 1\r\nResponseTime: 2019-03-27T11:39:57.0995601Z, StoreResult: StorePhysicalAddress: rntbd://cdb-ms-prod-westeurope1-fd21.documents.azure.com:16833/apps/9bc5d0cc-9b7c-4b1d-9be2-0fa2654271c4/services/9d84c048-383b-498e-8472-1f57da72135d/partitions/0f29761a-9ffc-4560-94ce-0328c3c79f92/replicas/131981366274575376p/, LSN: 1, GlobalCommittedLsn: 1, PartitionKeyRangeId: 0, IsValid: True, StatusCode: 400, SubStatusCode: 1001, RequestCharge: 1.24, ItemLSN: -1, SessionToken: 1, UsingLocalLSN: False, TransportException: null, ResourceType: Document, OperationType: Create\r\n, SDK: Microsoft.Azure.Documents.Common/2.2.0.0"
}
}
Any thoughts?
Please add double quotation marks "" around your partition key and it works fine for me.
View the code:

How to associate nested relationships with attributes for a POST in JSON API

According to the spec, resource identifier objects do not hold attributes.
I want to do a POST to create a new resource which includes other nested resource.
These are the basic resources: club (with name) and many positions (type). Think a football club with positions like goalkeeper, goalkeeper, striker, striker, etc.
When I do this association, I want to set some attributes like is the position required for this particular team. For example I only need 1 goalkeeper but I want to have a team with many reserve goalkeepers. When I model these entities in the DB I'll set the required attribute in a linkage table.
This is not compliant with JSON API:
{
"data": {
"type": "club",
"attributes": {
"name": "Backyard Football Club"
},
"relationships": {
"positions": {
"data": [{
"id": "1",
"type": "position",
"attributes": {
"required": "true"
}
}, {
"id": "1",
"type": "position",
"attributes": {
"required": "false"
}
}
]
}
}
}
}
This is also not valid:
{
"data": {
"type": "club",
"attributes": {
"name": "Backyard Football Club",
"positions": [{
"position_id": "1",
"required": "true"
},
{
"position_id": "1",
"required": "false"
}]
}
}
}
So how is the best way to approach this association?
The best approach here will be to create a separate resource for club_position
Creating a club will return a url to a create club_positions, you will then post club_positions to that url with a relationship identifier to the position and club resource.
Added benefit to this is that club_positions creation can be parallelized.

How to create compound documents?

I'm thinking of using the JSONAPI standard for the design of our API. One thing this API must be able to do, is accept a compound document (several layers deep) and create it. The root object owns all descendants ('to-many' relationships) which the server knows nothing about at that point, so it's not possible for the client to provide an id.
Is this supported by the specification or does the client have to issue http requests for every object in the document in order?
from http://jsonapi.org/format/#document-compound-documents
Compound documents require "full linkage", meaning that every included
resource MUST be identified by at least one resource identifier object
in the same document. These resource identifier objects could either
be primary data or represent resource linkage contained within primary
or included resources. The only exception to the full linkage
requirement is when relationship fields that would otherwise contain
linkage data are excluded via sparse fieldsets.
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"links": {
"self": "http://example.com/articles/1"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
},
"comments": {
"links": {
"self": "http://example.com/articles/1/relationships/comments",
"related": "http://example.com/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}, {
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"links": {
"self": "http://example.com/comments/12"
}
}]
}

No self href in embedded HAL resource collection

I use the self href from my HAL resources on the client side to find the right path for the CRUD operations. In single(ton) resources this is working fine (see the address resource below, the _links containing the self href is included in the embedded resource) but when it comes down to collections this is a different story. The _links of a collection are not rendered when the collection is in _embedded.
Earlier I worked around this problem by reading the url form the first child. But this is not sufficient. In case the collection is empty I only have
an empty array with no possibility to extract the url like that. If I want to create a new item in the collection I would like that my client knows where to send data using POST by reading the self href from _links. Is it a good idea to include the _links to my collection like this:
{
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1"
}
},
"_embedded": {
"contacts": {
Now I have access to the self href here:
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1/contacts"
}
},
"_embedded": {
"contacts": [
{
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1/contacts/2"
}
},
"id": "2",
"name": "John Smith"
},
{
"_links": {
"self": {
"href": "http://example.org/api/v1/users/1/contacts/3"
}
},
"id": "3",
"name": "Jane Doe"
}
],
}
},
"address": {
"_links": {
"self": {
"href": "http://example.com/api/v1/addresses/1"
}
},
"street": "Bakerstreet 11",
"postal code": "123456",
"city": "Some city",
"country": "Some country",
}
},
"id": "1",
"name": "John Doe"
}
EDIT (one year later)
In the end I solved this by always adding the links of embedded resources to the parent resource. So in the example above my response object would look like this:
{
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1"
},
"contacts": {
"href": "http://example.com/api/v1/users/1/contacts"
},
"address": {
"href": "http://example.com/api/v1/addresses/1"
}
},
"_embedded": {
"contacts": [
{
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1/contacts/2"
}
},
"id": "2",
"name": "John Smith"
},
{
"_links": {
"self": {
"href": "http://example.org/api/v1/users/1/contacts/3"
}
},
"id": "3",
"name": "Jane Doe"
},
],
"address": {
"_links": {
"self": {
"href": "http://example.org/api/v1/addresses/1"
}
},
"street": "Bakerstreet 11",
"postal code": "123456",
"city": "Some city",
"country": "Some country",
}
},
"id": "1",
"name": "John Doe"
}
So no matter whether I embedded the resources or not I always know where they are located. And for the contacts collection I will have the link to my collection endpoint in the _links array and the contacts themselves in _embedded.
Yes. It's not only good practice to do so; it's recommended by the HAL spec:
Each Resource Object SHOULD contain a 'self' link that corresponds with the IANA registered 'self' relation (as defined by RFC5988) whose target is the resource's URI.
A collection of resources is itself a resource, don't forget.

Resources