OData Dynamic Model Self-Referencing Navigation Property "Max Level" Expansion - asp.net-core-webapi

I am developing an OData Web API with dynamic models (no CLR-based types of any kind). Note that this concept is heavily predicated upon the ODataDynamicModel found on GitHub (https://github.com/OData/AspNetCoreOData/tree/main/sample/ODataDynamicModel). Contained within the dynamic model is a self-referencing navigation property named "Items". The OData CSDL is shown below.
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="ns">
<EntityType Name="Item">
<Key>
<PropertyRef Name="ID"/>
</Key>
<Property Name="ID" Type="Edm.Int32"/>
<Property Name="Name" Type="Edm.String"/>
<NavigationProperty Name="Detail" Type="ns.Detail" ContainsTarget="true"/>
**<NavigationProperty Name="Items" Type="Collection(ns.Item)" ContainsTarget="true"/>**
</EntityType>
<EntityType Name="Detail">
<Key>
<PropertyRef Name="ID"/>
</Key>
<Property Name="ID" Type="Edm.Int32"/>
<Property Name="Description" Type="Edm.String"/>
</EntityType>
<EntityContainer Name="Default">
<EntitySet Name="Items" EntityType="ns.Item"/>
<EntitySet Name="Details" EntityType="ns.Detail"/>
</EntityContainer>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
For testing purposes, my API is serving up a static data source so as to eliminate unnecessary complexities such as ORM, etc. I am able to query the data source via OData in its entirety when providing an explicit, fully-defined query string as shown below.
[GET]
http://localhost:4527/odata/ns/Items?$expand=Detail,Items($expand=Detail,Items($expand=Detail,Items($expand=Detail,Items($expand=Detail,Items($expand=Detail,Items)))))
Response:
{
"#odata.context": "http://localhost:4527/odata/ns/$metadata#Items(Detail(),Items(Detail(),Items(Detail(),Items(Detail(),Items(Detail(),Items(Detail(),Items()))))))",
"value": [
{
"ID": 1,
"Name": "Item 1",
"Detail": {
"ID": 1,
"Description": "Detail 1"
},
"Items": [
{
"ID": 2,
"Name": "Item 2",
"Detail": {
"ID": 2,
"Description": "Detail 2"
},
"Items": [
{
"ID": 3,
"Name": "Item 3",
"Detail": {
"ID": 3,
"Description": "Detail 3"
},
"Items": [
{
"ID": 4,
"Name": "Item 4",
"Detail": {
"ID": 4,
"Description": "Detail 4"
},
"Items": []
}
]
}
]
}
]
}
]
}
Retrieval of the same response payload fails when attempting to take advantage of OData's "levels" capabilities as shown below.
[GET]
http://localhost:4527/odata/ns/Items?$expand=Items($levels=10;$expand=Detail),Detail
Response:
{
"#odata.context": "http://localhost:4527/odata/ns/$metadata#Items(Items(Detail()),Detail())",
"value": [
{
"ID": 1,
"Name": "Item 1",
"Items": [
{
"ID": 2,
"Name": "Item 2",
"Detail": {
"ID": 2,
"Description": "Detail 2"
}
}
],
"Detail": {
"ID": 1,
"Description": "Detail 1"
}
}
]
}
I have tried many things without success and am currently stumped to say the least. I posted to the discussion board on Git (https://github.com/OData/AspNetCoreOData/discussions/789) but haven't received a response, which is to be expected this time of year. Attached to that discussion is a small zip file containing a working solution demonstrating the current approach. I'm hopeful that someone out there better versed in the ways of OData can point me in the proper direction. Any and all suggestions are appreciated!

Related

Marketo - How to save complex object in Lead Database?

I was asked to store leads like below in Marketo's lead database through rest api "POST /rest/v1/leads.json".
{
"action": "createOnly",
"lookupField": "email",
"input": [{
"email": "kjashaedd-1#klooblept.com",
"firstName": "Kataldar-1",
"postalCode": "04828",
"property": [{
"type": "land",
"status": "available"
},
{
"type": "car",
"status": "sold out"
},
{
"type": "bike",
"status": "sold out"
},
{
"type": "laptops",
"status": "available"
}
]
},
{
"email": "kjashaedd-2#klooblept.com",
"firstName": "Kataldar-2",
"postalCode": "04828",
"property": [{
"type": "land",
"status": "sold out"
},
{
"type": "car",
"status": "available"
},
{
"type": "bike",
"status": "sold out"
},
{
"type": "laptops",
"status": "available"
}
]
}
]
}
Input field is not in flat json structure. What could be the best approach? Do I really need to use custom objects in this case? Can I dump "property" object as it is in the lead database and use velocity script to parse it ?
If 'Property' is a custom object, you'll want to call that separately for the record and associated the record with that object via the custom object API
So you can push (create) the record first and then associate (add) the custom object to the record.
You can create a List<Dictionary<string,object>> in C# and retrieve the data and then store it using Entity Framework.
I am considering .Net framework in this case

CosmosDB SQL to query "any" child field

Given a document structure like below, where "variants" has N id based sub-entries, I would like to filter on the inner "sku" field. Something akin to this:
SELECT * FROM c WHERE c.variants.?.sku = "some_sku_1"
Here "some_id_1" and "some_id_2" are id values, data driven, and cannot be part of the query.
Is this possible with Cosmos DB and if so, how?
{
"id": "45144",
"variants": {
"some_id_1": {
"sku": "some_sku_1",
"title": "some title 1"
},
"some_id_2": {
"sku": "some_sku_2",
"title": "some title 2"
}
}
}
You can't do that with that schema without using a UDF/SPROC, but if you change the schema slightly, you can do it.
Schema:
{
"id": "45144",
"variants": [
{
"id": "some_id_1",
"sku": "some_sku_1",
"title": "some title 1"
},
{
"id": "some_id_2"
"sku": "some_sku_2",
"title": "some title 2"
}
]
}
Query:
SELECT * FROM c IN Item.variants WHERE c.sku == "some_sku_1"
Check out this article to get a good idea of what's possible with that "IN' statement, which allows you to iterate over objects. https://learn.microsoft.com/en-us/azure/cosmos-db/sql-api-sql-query#Advanced

Swagger Schema error should NOT have additional properties

I am trying to create swagger json and trying to check it's validity in
http://editor.swagger.io
Upon validating the json, the above mentioned editor gives the following error:
Schema error should NOT have additional properties
additionalProperty: components
Jump to line 0
If for some reason I can't define an element named components at root level where i am going to have some sort of json schema, what is the right way to do a $ref on the schema for requestBody for an API operation as I intend to do as seen in my example below. Also, do you see anything wrong with my swagger json ?
My swagger json for swagger2.0 look like this.
{
"swagger": "2.0",
"info": {
"version": "1.0",
"title": "My swagger API",
"contact": {
"name": "myName",
"email": "abc#gmail.com"
}
},
"host": "localhost:1234",
"basePath": "/",
"tags": [{
"name": "someTagName",
"description": "this is a try"
}],
"components":{"schemas": {"myEndPoint": ""}},
"paths": {
"/myEndPoint": {
"post": {
"tags": ["some-tag"],
"summary": "myEndPoint endpoint will give you something",
"description": "some description will go here",
"operationId": "getMyEndPoint",
"consumes": ["application/json"],
"produces": ["application/json"],
"parameters": [{
"in": "body",
"name": "somePayload",
"description": "somePayload is what this is",
"required": true,
"schema": {
"$ref": "#components/schemas/myEndPoint"
}
},
{
"in": "header",
"name": "Authorization",
"description": "auth token goes here",
"required": true,
"type": "string"
}],
"responses": {
"200": {
"description": "Success",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request"
}
}
}
}
}
}
You are mixing up OpenAPI 3.0 and 2.0 syntax. The components keyword is used in OpenAPI 3.0. In OpenAPI/Swagger 2.0, reusable schemas live under definitions:
"definitions": {
"myEndPoint": {
...
}
}
Make sure to also change the $ref to
"$ref": "#/definitions/myEndPoint"

Hubo - How to get flow / thread users

How to get the users list from the current flow / thread with Hubot on Flowdock? I'd like to create plugin showing messages with the usage of current flow user names.
I have found this: robot.brain.data.users
but it returns the whole list of users from organization rather than from the current flow.
The list of a flow’s users can be fetched using the Flows resource. You'll need to know the Organization name and Flow name. See the 'Get a Flow' section here:
https://www.flowdock.com/api/flows
The format is:
GET /flows/:organization/:flow
and returns:
{
"id": "deadbeefdeadbeef",
"name": "My flow",
"parameterized_name": "my-flow",
"organization": {
"id": 8,
"name": "Acme",
"parameterized_name": "acme",
"user_limit": 0,
"user_count": 5,
"active": true,
"url": "https://api.flowdock.com/organizations/acme"
}
"unread_mentions": 0,
"open": true,
"url": "https://api.flowdock.com/flows/acme/my-flow",
"web_url": "https://www.flowdock.com/app/acme/my-flow",
"join_url": "https://www.flowdock.com/invitations/eedd2bf0643f75c14be9099272429351c7132a71-my-flow",
"access_mode": "link",
"users": [
{
"id": 9,
"nick": "Joe",
"name": "Joe Smith",
"email": "joe#example.com",
"avatar": "https://d2cxspbh1aoie1.cloudfront.net/avatars/f5b8fb60c6116331da07c65b96a8a1d1/",
"status": "Testing API",
"disabled": false,
"last_activity": 1328016726423000,
"last_ping": 1328017690004000
},
{
"id": 42,
"nick": "Stevie",
"name": "Stevie Johnson",
"email": "stevie#example.com",
"avatar": "https://d2cxspbh1aoie1.cloudfront.net/5bdd089a099acc56fc7120f6325a5d5c/",
"status": null,
"disabled": false,
"last_activity": 1328016712345000,
"last_ping": 1328017612345000
}
]
}

xml data lost in JSON conversion

I am getting the following result from converting xml to JSON, using more than one conversion library. As you can see, the property name attributes are lost, as are the Item name attributes. Why?
Does anyone have recommendations on how I might change my XML to make it more conversion friendly?
<Asset name="xyz">
<Property name="p1">Value 1</Property>
<Property name="p2">Value 2</Property>
<TimeSeries name="TimeSeries Name 1">
<Item name="30 Apr 2009">97.47219</Item>
<Item name="01 May 2009">97.16496</Item>
<Item name="05 May 2009">97.34606</Item>
</TimeSeries>
</Asset>
Returns:
{
"Asset": {
"#attributes": {
"name": "xyz"
},
"Property": ["Value 1", "Value 2"],
"TimeSeries": {
"#attributes": {
"name": "TimeSeries Name 1"
},
"Item": ["97.47219", "97.16496", "97.34606"]
}
}
}
I have tried the following, but both the XML and JSON are a lot more verbose:
<Asset name="xyz">
<Property><name>p1</name><value>Value 1</value></Property>
<Property><name>p2</name><value>Value 2</value></Property>
<TimeSeries name="TimeSeries Name 1">
<Item><date>30 Apr 2009</date><value>97.47219</value></Item>
<Item><date>01 May 2009</date><value>97.16496</value></Item>
<Item><date>05 May 2009</date><value>97.34606</value></Item>
</TimeSeries>
</Asset>
resulting in...
{
"Asset": {
"#attributes": {
"name": "xyz"
},
"Property": [{
"name": "p1",
"value": "Value 1"
}, {
"name": "p2",
"value": "Value 2"
}],
"TimeSeries": {
"#attributes": {
"name": "TimeSeries Name 1"
},
"Item": [{
"date": "30 Apr 2009",
"value": "97.47219"
},
{
"date": "01 May 2009",
"value": "97.16496"
}, {
"date": "05 May 2009",
"value": "97.34606"
}
]
}
}
}
Probably you should never use attributes in the source XML file if you use this conversion tool.
You main problem I see is that you don't design the data yourself and try usage of a strange tool. If you use ASP.NET on the server side, it is much better to design your C# classes, initialize an instance of the classes with any test data and use JSON serialization like DataContractJsonSerializer or use a simple Web Service. Look at Can I return JSON from an .asmx Web Service if the ContentType is not JSON? or How do I build a JSON object to send to an AJAX WebService? as examples.

Resources