OData $expand Contains Incorrect Context Link - c#

Using an ASP.NET Web API 2 OData service I have exposed the the following endpoint:
/entity1('key')/entity2('key')/entity3('key')/entity4('key')
When I execute /entity1('key')/entity2('key')/entity3?$expand=entity4 I receive a JSON response with the correct "#odata.context":
"#odata.context": "https://host:port/$metadata#entity1('key')/entity2('ket')/entity3",
"value": [
{
...,
"entity4#odata.context": "https://host:port/$metadata#entity1('key')/entity2('key')/entity3('key')/entity4",
"entity4": [
{
...
}
]
},
...
When I execute /entity1('key')/entity2('key')/entity3('key')?$expand=entity4 however, I receive an incorrect "#odata.context" with two keys:
"#odata.context": "https://host:port/$metadata#entity1('key')/entity2('ket')/entity3",
"value": [
{
...,
"entity4#odata.context": "https://host:port/$metadata#entity1('key')/entity2('key')/entity3('key')('key')/entity4",
"entity4": [
{
...
}
]
}
This causes an exception when executing with Microsoft's OData Client:
"The context URL 'https://host:port/$metadata#entity1('key')/entity2('key')/entity3('key')('key')/entity4' is invalid."
Well no kidding. What could be causing this?
Update
I've logged an issue here. We'll see what happens.

I've found a workaround:
Using a $filter instead of a key: /entity1('key')/entity2('key')/entity3?$filter=Key eq 'key'&$expand=entity4 provides the correct response:
"#odata.context": "https://host:port/$metadata#entity1('key')/entity2('ket')/entity3",
"value": [
{
...,
"entity4#odata.context": "https://host:port/$metadata#entity1('key')/entity2('key')/entity3('key')/entity4" ,
"entity4": [
{
...
}
]
}

Related

Actions on Google smart home project can't parse QUERY response

I have the following problem. When I turn on/off my bulb in Google Home app it sends me EXECUTE request and then QUERY request for state check. That behaviour is okay, but the problem is that Actions on Google project can't parse my QUERY response.
For clarification: I don't have problems with C# JSON parsing and any of similar topics. I have problem with that the Actions on Google can't understand my QUERY response. I reponse in correct format according to Google's docs but in the web console I see that error occurs and I don't know what is wrong.
It shows following error in the Google Cloud Console:
{
"insertId": "166g06lg15lgekt",
"jsonPayload": {
"executionLog": {
"executionResults": [
{
"actionResults": [
{
"action": {
"actionType": "STATE_QUERY"
},
"device": {
"deviceType": "OUTLET"
},
"status": {
"externalDebugString": "JSON payload not an object.",
"isSuccess": false,
"statusType": "PARTNER_RESPONSE_INVALID_PAYLOAD"
}
}
],
"executionType": "PARTNER_CLOUD",
"latencyMsec": "191",
"requestId": "4734217757620110233"
}
]
},
"locale": "en-US"
},
"logName": "projects/smartlightproject-f47f4/logs/assistant_smarthome%2Fassistant_smarthome_logs",
"receiveTimestamp": "2022-07-13T13:23:04.088211105Z",
"resource": {
"labels": {
"project_id": "smartlightproject-f47f4"
},
"type": "assistant_action_project"
},
"severity": "ERROR",
"timestamp": "2022-07-13T13:23:04.088211105Z"
}
QUERY request which I receive is
{
"inputs": [
{
"intent": "action.devices.QUERY",
"payload": {
"devices": [
{
"customData": {
"barValue": true,
"bazValue": "foo",
"fooValue": 74
},
"id": "123"
}
]
}
}
],
"requestId": "5460596871498683621"
}
And my response to that QUERY request is
{
"requestId": "5460596871498683621",
"payload": {
"devices": {
"123": {
"on": true,
"online": true
}
}
}
}
I think that everything is as Google wants to in documentation but I can't find the solution nor cause of that issue. I appreciate your help, really.
If it matters I write my local fulfillment in ASP.NET.
That's how I send the QUERY response
var queryObj = JsonConvert.DeserializeObject<dynamic>("{\"requestId\":\"" + requestId + "\",\"payload\":{\"devices\":{\"123\":{\"on\":true,\"online\":true}}}}");
_logger.LogInformation("Odpowiedź na żądanie QUERY." + originalRequest + "\n\n" + JsonConvert.SerializeObject((object)queryObj));
return Ok((object)queryObj);

Google action doesn't provide user.profile.payload object after successful account linking

I followed this guide in order to create account linking in my app
https://developers.google.com/actions/identity/google-sign-in#json
I'm able to verify the user's jwt decoder and send back a response that the user is authorised. Then, according to the guide, in the next request, I should get the user's profile payload (user.profile.payload in the json structure) but It's missing from the next request. More than that, I get the tokenId for jwt verification again.
I think that what i miss here is in the possibleIntent object but I'm not sure, as I didn't see any documentation for that, because I work with asp.net server. There are SDKs with documentation for java and nodeJS only
this is the request provided for the sign in the contain the tokenId
{
"user": {
"locale": "en-US",
"lastSeen": "2019-07-11T14:18:10Z",
"idToken": "<tokenId>",
"userVerificationStatus": "VERIFIED"
},
"conversation": {
"conversationId": "ABwppHH9uZfcKj6pS6A6wItKC1dOXuZJ5oFYt2Og7cqrElSQYC9bv-aV7iQ5FDYaJPp-fa7tQNhc2yS0fw3QBu-M",
"type": "ACTIVE",
"conversationToken": "e0e78f40-a207-49c2-9050-50c6ed526c24"
},
"inputs": [
{
"intent": "actions.intent.SIGN_IN",
"rawInputs": [
{
"inputType": "KEYBOARD"
}
],
"arguments": [
{
"name": "SIGN_IN",
"extension": {
"#type": "type.googleapis.com/google.actions.v2.SignInValue",
"status": "OK"
}
},
{
"name": "text"
}
]
}
],
"surface": {
"capabilities": [
{
"name": "actions.capability.SCREEN_OUTPUT"
},
{
"name": "actions.capability.ACCOUNT_LINKING"
},
{
"name": "actions.capability.AUDIO_OUTPUT"
},
{
"name": "actions.capability.MEDIA_RESPONSE_AUDIO"
},
{
"name": "actions.capability.WEB_BROWSER"
}
]
},
"isInSandbox": true,
"requestType": "SIMULATOR"
}
this is the response that i provide after verifying the user.
I tried it with both intents actions.intent.TEXT and actions.intent.SIGN_IN but with no success. the next request is provided with the user.idToken property again instead of the user.profile (that should contain the payload)
{
"conversationToken": "b09d915e-6df9-496d-acde-b76858cd95b4",
"expectUserResponse": true,
"expectedInputs": [
{
"inputPrompt": {
"richInitialPrompt": {
"items": [
{
"simpleResponse": {
"textToSpeech": "Hi",
"displayText": "Hi"
}
}
],
"suggestions": []
}
},
"possibleIntents": [
{
"intent": "actions.intent.TEXT",
"inputValueData": {
"#type": "type.googleapis.com/google.actions.v2.SignInValue",
"status": "OK"
}
}
]
}
]
}
The user.profile attribute you're talking about is something that is provided via the actions-on-google library for JavaScript. It isn't in the JSON that you will receive. But...
You don't need it because the basic profile information (name, email, and Google ID) is encoded in the user.idToken. That string, which will be sent to you for every request, is just a JWT token which you can verify and decode. The profile will be in the "payload" section.
I don't know c#, but https://jwt.io/ contains a list of libraries which can decode the JWT string for you so you can read the "payload".
Keep in mind that you don't need to verify the token each time (although if you do it right, this shouldn't be expensive), but that you can decode it to get the information that you're looking for.
If you don't want to decode it, you can decode it when you first verify it, get the information you need, and store that information in the userStorage string (assuming you don't expect it to change).

Scrolling and Paging in Elastic/Nest 6+

Intro
I'm upgrading the elastic version to 6.3 (previously we were using 5.4.
Our app is written in C#, thus we use NEST.NET dll to talk with the Elastic server, so we are also updating it to the version 6.0.0.0.
The use case - Before
Until version 5, I was able to execute this query:
jsonStr ="
{
"from": 16224,
"size": 12,
"query": {
"bool": {
"filter": [
{
"bool": {
"must": [
{
"terms": {
"COMPANY": [
"AMP Services Ltd"
]
}
}
]
}
}
]
}
}
}
}"
Using this NEST/C# code:
Func<SearchRequestParameters, SearchRequestParameters> requestParameters = null;
requestParameters = a => a.Scroll(new TimeSpan(0, 1, 0));
response = Connection.Client.GetInstance().LowLevel.Search<dynamic>("myindex", new PostData<dynamic>(jsonStr), requestParameters);
And with that, I was able to fetch the data without problems,
The use case - NOW
Now, with version 6, I'm trying to execute this very same query:
jsonStr ="
{
"from": 16224,
"size": 12,
"query": {
"bool": {
"filter": [
{
"bool": {
"must": [
{
"terms": {
"COMPANY": [
"AMP Services Ltd"
]
}
}
]
}
}
]
}
}
}"
Using this NEST/C# code (as the previus method signatures are no longer available):
SearchRequestParameters searchRequest = new SearchRequestParameters();
searchRequest.Scroll = new TimeSpan(0, 1, 0);
response = Connection.Client.GetInstance().LowLevel.Search<StringResponse>("myindex", PostData.String(jsonStr), searchRequest);
And I'm getting this error: "Validation Failed: 1: using [from] is not allowed in a scroll context;"
Documentation
I could not find anything in here (https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-scroll.html) and here (https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/sliced-scroll-search-usage.html) to help me replace this logic. Nothing in the forums either.
Do you guys have any insights on this?
Thanks
It looks to be related to a validation change in Elasticsearch in 6.0.0; In 5.x, the from parameter was allowed for a scroll request but silently ignored. Now in 6.0.0, Elasticsearch is stricter and validates whether from is present for a scroll request and if it is, returns a bad response with an appropriate validation error.
Since a from parameter doesn't make sense for a scroll request, the solution to this is to do one of these two
Remove the from parameter when using the Scroll API
Continue to use the from parameter but do not use the Scroll API.
As an aside, If you are needing to scroll many documents, you may want to use ScrollAll() observable helper to do so.

Get all information about Facebook posts including reactions

I'm having a problem with the URL of the Facebook Graph API. Is there any possibility to get all the fields of a Facebook post including reactions? I use the following URL for the posts:
https://graph.facebook.com/{pageName}/feed?access_token={access_token}
Now I'm getting data like this (which is quite nice):
{
"data": [
{
"id": "someId",
"from": {
"Name": "Page name",
"category": "Sports Team",
"id": "someId"
},
"message": "Hello world!",
[...]
"shares": {
"count": 1
},
"likes": {
"data": [
{
"id": "someId",
"name": "Some person"
}
]
}
},
[...]
]
}
As for now I have to get the reactions (LOVE, WOW, HAHA, SAD, ANGRY and THANKFUL) by downloading the json from the following URL for every single post (and this is very time consuming):
https://graph.facebook.com/v2.9/{postId}?access_token={access_token}&fields=reactions
The only problem is that I can't get the reactions when using the "normal" URL (without &fields). Is there any chance to get all information including reactions without having to add all the fields to &fields=from,message,likes,shares,reactions?
From CBroe's comment:
I had to pass all the fields I wanted to save to my DB in my URL:
https://graph.facebook.com/v2.9/{pageName}/feed?access_token={access_token}&fields=id,from,message,name,[...],likes,comments,reactions,shares

Which JSON adapter enables Ember to propagate all necessary requests to the server to maintain model consistence?

I am using Andy Crum's EmberDataModelMaker.
Having punched in the following two classes
// app/models/server-item.js
export default DS.Model.extend({
hostName: DS.attr('string'),
syncServers: DS.hasMany('string'),
subscribers: DS.hasMany('string'),
mailHost: DS.attr('string'),
mailHostLogin: DS.hasMany('credentials')
});
// app/models/credentials.js
export default DS.Model.extend({
user: DS.attr('string'),
password: DS.attr('string'),
server: DS.belongsTo('serverItem')
});
It's showing the following three different expected JSON formats (a very nice feature btw.):
DS.RESTAdapter
"serverItems": [
{
"id": 1,
"hostName": "foo",
"syncServers": [
<stringids>
],
"subscribers": [
<stringids>
],
"mailHost": "foo",
"mailHostLogin": [
<Credentialsids>
]
}
],
"credentials": [
{
"id": 1,
"user": "foo",
"password": "foo",
"server": <ServerItemid>
}
]
DS.ActiveModelAdapter
"serverItems": [
{
"id": 1,
"host_name": "foo",
"sync_server_ids": [
<stringids>
],
"subscriber_ids": [
<stringids>
],
"mail_host": "foo",
"mail_host_login_ids": [
<Credentialsids>
]
}
],
"credentials": [
{
"id": 1,
"user": "foo",
"password": "foo",
"server_id": <ServerItemid>
}
]
DS.JSONAPIAdapter
{
"data": {
"type": "server-items",
"id": "1",
"attributes": {
"HostName": "foo",
"MailHost": "foo",
},
"relationships": {
"SyncServers": {
"data": {
"type": "SyncServers",
"id": <SyncServersid>
}
},
"Subscribers": {
"data": {
"type": "Subscribers",
"id": <Subscribersid>
}
},
"MailHostLogin": {
"data": {
"type": "MailHostLogin",
"id": <MailHostLoginid>
}
}
},
"included": [
{
<sideloadedrelationships>
]
}
}
}
{
"data": {
"type": "credentials",
"id": "1",
"attributes": {
"User": "foo",
"Password": "foo",
},
"relationships": {
"Server": {
"data": {
"type": "Server",
"id": <Serverid>
}
}
},
"included": [
{
<sideloadedrelationships>
]
}
}
}
I am going to implement (or rather change) some WebServices on the Server side (using C#, ASP.NET Web API). Currently, the WebService already creates a result that is pretty similar to the format expected with DS.RESTAdapter - obviously, it would be ideal if I could use it without compromising the Data Integrity - can I?
If yes, would it empower Ember Data to send all the requests necessary to maintain the data consistency on the server? Meaning, would the client send a DELETE request to the server not only for the ServerItem but also for the Credentials item that is referenced via the mailHostLogin property when the user wants to delete a ServerItem?
If not: are both of the other two adapters fulfilling the above mentioned consistency requirement? Which of the other two should I implement - any experiences/recommendations out there?
You should choose whichever Adapter closest fits your API data structure as a basis(sounds like DS.RESTAdapter in this case). You can extend the adapters and serializers that are a closest fit to make any necessary adjustments(this can be done both application wide or on a per model basis).
However, I don't think that the Ember Data model relationships(i.e. belongsTo and hasMany) are binding in such a way that will automatically result in the "data consistency" you are looking for. If your application requirements are to delete all associated Credentials records when a ServerItem is deleted, I would recommend doing that server side when handling the DELETE ServerItem API request. That would result in better performance(1 HTTP call instead of 2 or N depending if credentials can be deleted in bulk) and be much less error prone due to potential network or other failure of calls to delete Credentials after a ServerItem is deleted.
After a successful ServerItem delete, you could loop through it's credentials and unload the records from the client side store to keep it in sync with the new state on the server. Something like:
serverItemCredentials.forEach(function(id) {
if (this.store.recordIsLoaded('credential', id)) {
this.store.unloadRecord(this.store.peekRecord('credential', id));
}
});

Categories

Resources