Recently I had to build a solution for a customer. One part was to create order documents in a Cosmos DB using a Logic App. Sounds easy, however, I struggled a bit because of a simple thing and thought that this could help others. So here we go.
The Logic App will be HTTP-triggered. The request body will contain the order details. For this blog post I simplified the procedure (less order details, no schema validation etc.). After the trigger I use a “Create or update document” step, connect to the Cosmos DB and use the body to create the document
Once prepared, I use a simple PowerShell script to test the Logic App. Here you can also see how the body is constructed.
$uri = https://prod-35.westeurope.logic.azure.com:443/workflows/58a………”
$id = [guid]::NewGuid()
$product = “Office 2003 Professional”
$orderdate = get-date -format s
$person = “Marcel Zehner”
$country = “Switzerland”
$Status = “Ordered”
$body = @{
“id” = $id;
“product” = $product;
“orderdate” = $orderdate;
“person” = $person;
“country” = $country;
“status” = $status
} | ConvertTo-Json
Invoke-WebRequest -Uri $uri -Body $body -Method POST -ContentType application/json
The Logic App triggers, but later fails. The request body looks OK, but the document has not been created. Let’s have a look at the error message.
{
“code”: “BadRequest”,
“message”: “Message: {\”Errors\”:[\”The partition key supplied in x-ms-partitionkey header has fewer components than defined in the the collection.\”]}\r\nActivityId: 397f8bfc-b4d6-461d-934b-ef834182eb3a, Request URI: /apps/36834417-c2b8-4524-871e-e3247817af32/services/cad59f1d-0db5-448a-b8ce-ab8e45f2f3d9/partitions/2e968e2a-42b1-40fe-9b91-1c294c301efc/replicas/131934563705646138p/, RequestStats: \r\nRequestStartTime: 2019-02-05T17:25:00.7030395Z, Number of regions attempted: 1\r\n, SDK: Microsoft.Azure.Documents.Common/2.1.0.0″
}
So there seems to be an issue with the Cosmos DB partition key. This key is used for scaling, so let’s have a look at the collection configuration. The “orders” collection uses a partition key “country”.
Now back in the Logic App designer you will discover that you can add a partition key value to the step. Let’s do that. For now I just hardcode the value to see if this works. But nope, after firing the PowerShell script the Logic App fails again. But this time with a different error message.
{
“code”: “BadRequest”,
“message”: “Partition key [Switzerland] is invalid.\r\nActivityId: 31c27f0b-e75a-4fa7-9aac-9a4d777e0529, \r\nRequestStartTime: 2019-02-05T16:07:12.1086065Z, Number of regions attempted: 1\r\n, Microsoft.Azure.Documents.Common/2.1.0.0″
}
One interesting thing is, that the value is shown in brackets which means that it is interpreted as an array. Back at the drawing board I changed the hardcoded value to something different and added quotation marks. Again, another error message. This time it’s a bit clearer. The partition key value obviously MUST be in quotation marks and it MUST match the value for the same key in the payload.
{
“code”: “BadRequest”,
“message”: “Message: {\”Errors\”:[\”PartitionKey extracted from document doesn’t match the one specified in the header\”]}\r\nActivityId: 3c500c0c-c2ed-4587-b16f-4565877d87b6, Request URI: /apps/36834417-c2b8-4524-871e-e3247817af32/services/cad59f1d-0db5-448a-b8ce-ab8e45f2f3d9/partitions/2e968e2a-42b1-40fe-9b91-1c294c301efc/replicas/131934563705646138p/, RequestStats: \r\nRequestStartTime: 2019-02-05T16:10:24.7924046Z, Number of regions attempted: 1\r\n, SDK: Microsoft.Azure.Documents.Common/2.1.0.0″
}
After changing everything properly, it worked like a charm.
– Partition key in quotation mark
– “Country” value identical to the one submitted in the request body
The last step was bringing the value for the partition key dynamically to the field. I use a “Parse JSON” step for that. In this step, the request body is parsed, so that afterwards all individual key-value pairs can easily be accessed.
{
“type”: “object”,
“properties”: {
“id”: {
“type”: “string”
},
“person”: {
“type”: “string”
},
“status”: {
“type”: “string”
},
“product”: {
“type”: “string”
},
“orderdate”: {
“type”: “string”
},
“country”: {
“type”: “string”
}
}
}
Finally, I use the dynamic “country” value from the Parse JSON output in the partition key property. And don’t miss the quotation marks!
Firing the script again now does exactly what I need.
I found this a bit confusing and also discovered that this is/was an issue for many folks out there – not only for the ones using Logic App but trying to create new Cosmos DB documents in general. I hope this blog post can help out a bit …
Cheers
Marcel
I did the same. But I still get error “PartitionKey extracted from document doesn’t match the one specified in the header”. The Partition Key Value in the Input section has correct value in quote, match the document. Some people suggest it has to be an array?
Are you sure you have the exact same property in the body? Then it should definitely work.
yeah me too facing the same issue even after exactly following the steps.
the issue was the mismatch in partition key..turns out the partition key i gave while creating the db was never present in the json payload. It was a typo with camel case and pascal case each. Now it is working as a charm after I corrected it.
Thanks for the update. As mentioned, the partition key is needed in the JSON payload. Without it will not work.
Cheers, Marcel
In the code view make sure the header section of the CosmosDB Action looks like what I have below:
“headers”: {
“x-ms-documentdb-partitionkey”: “@{body(‘Parse_JSON’).country}”
},
Notice: when trying to use the “Add New Parameter” it formats it as x-ms-documentdb-RAW-partitionkey, which causes errors, and as Marcel mentioned you need it in your body which your screenshot shows.
I think it’s the logic app connector bug mentioned above, where it add’s “RAW” to the parameter declaration.
The PartitionKey should always point to the value of the field you are partitioning on, so it should be the value of “country” that is passed in the root of the document body, and it of course needs to match what the CosmosDB Collection expects as the partition key (as far as it’s naming in the document body) I.E. if the CosmosDB Collection used “nation” instead of “country” than your document body would need to declare it as “nation” instead of “country” to set it.
Elliott’s code-view headers section is very useful. Thanks.
Thanks for the article, it saved me some time!
Thank you all for the above post and replies. I was trying to load-in a couple of JSON items via PowerShell into a CosmosDB collection and was specifying the partition key definition rather than the actual value of the key for the item. Reading through the above it was an easy fix;
# Insert docs into Collection
$json = Get-Content “C:\Users\*****\OneDrive – Onomi\GitHub\DP-200-Implementing-an-Azure-Data-Solution\Labfiles\Starter\DP-200.4\ClothingItems.json” | Out-String | ConvertFrom-Json
$cosmosDbContext = New-CosmosDbContext -Account $accountName -Database $databaseName -ResourceGroup $resourceGroupName
foreach($i in $json){
$document = $i | ConvertTo-Json | Out-String
New-CosmosDbDocument -Context $cosmosDbContext -CollectionId $containerName -DocumentBody $document -PartitionKey $i.productId
}
Thanks for this article that really help me.
Hello, we have non-partition collection for our cosmos DB, And I am using Android SDK to create a document using databaseId and collectionId. Also, i have taken the partition key as property and set as null. still getting the error “partition key supplied in x-ms-partitionkey header has fewer components than defined in the collection”
can anyone help me to come out from this ?
kudos man, thanks for this tip. saved me loads of time needless troubleshooting! Documentation could be better for soure.
Great to hear that post helped!
Thank you. It helped me fix the issue.
This one really did save some time for me today! Hope they will also introduce Native Mongo API support for the Logic Apps
Thank you for this post Marcel. Saved me a lot of time! I encountered the same problem and I am equally confused by the solution… Why??
Thank you.
By the way, for create or update, the parameter IsUpsert most be set to true.
Thanks for your Input!