Redux - updating store based on async api calls - redux

I have a use case to render page using redux with graphql api calls.
On first component will call default action to fetch data from graphql, and stores in redux state as below
state = { films: {
totalCount: 6,
films: [
{
created: '2014-12-10T14:23:31.880000Z',
id: 'ZmlsbXM6MQ==',
director: 'George Lucas',
title: 'A New Hope'
},
{
created: '2014-12-12T11:26:24.656000Z',
id: 'ZmlsbXM6Mg==',
director: 'Irvin Kershner',
title: 'The Empire Strikes Back'
},
{
created: '2014-12-18T10:39:33.255000Z',
id: 'ZmlsbXM6Mw==',
director: 'Richard Marquand',
title: 'Return of the Jedi'
}
]
}
}
and will show UI like below
Films demo app UI screenshot
Once placed in individual component (Film), i have to make service call to get film details by id ( this calls will be async to fetch data and has to store in state.
Will get data as below
{ "data": {
"film": {
"id": "ZmlsbXM6NQ==",
"title": "Attack of the Clones",
"created": "2014-12-20T10:57:57.886000Z",
"director": "George Lucas",
"releaseDate": "2002-05-16",
"episodeID": 2,
"openingCrawl": "There is unrest in the Galactic\r\nSenate. Several thousand solar\r\nsystems have declared their\r\nintentions to leave the Republic.\r\n\r\nSenator Amidala, the former\r\nQueen of Naboo, is returning\r\nto the Galactic Senate to vote\r\non the critical issue of creating\r\nan ARMY OF THE REPUBLIC\r\nto assist the overwhelmed\r\nJedi....",
"producers": [
"Rick McCallum"
]
}
}
}
Now i have to update my state like below, so that i can show all film data in individual (Film) component
state = { films: {
totalCount: 6,
films: [
{
"id": "ZmlsbXM6NQ==",
"title": "Attack of the Clones",
"created": "2014-12-20T10:57:57.886000Z",
"director": "George Lucas",
"releaseDate": "2002-05-16",
"episodeID": 2,
"openingCrawl": "There is unrest in the Galactic\r\nSenate. Several thousand solar\r\nsystems have declared their\r\nintentions to leave the Republic.\r\n\r\nSenator Amidala, the former\r\nQueen of Naboo, is returning\r\nto the Galactic Senate to vote\r\non the critical issue of creating\r\nan ARMY OF THE REPUBLIC\r\nto assist the overwhelmed\r\nJedi....",
"producers": [
"Rick McCallum"
]
},
{
"id": "ZmlsbXM6Mg==",
"title": "The Empire Strikes Back",
"created": "2014-12-12T11:26:24.656000Z",
"director": "Irvin Kershner",
"releaseDate": "2002-05-16",
"episodeID": 2,
"openingCrawl": "There is unrest in the Galactic\r\nSenate. Several thousand solar\r\nsystems have declared their\r\nintentions to leave the Republic.\r\n\r\nSenator Amidala, the former\r\nQueen of Naboo, is returning\r\nto the Galactic Senate to vote\r\non the critical issue of creating\r\nan ARMY OF THE REPUBLIC\r\nto assist the overwhelmed\r\nJedi....",
"producers": [
"Rick McCallum"
]
},
{
"id": "ZmlsbXM6Mw==",
"title": "Return of the Jedi",
"created": "2014-12-18T10:39:33.255000Z",
"director": "Richard Marquand",
"releaseDate": "2002-05-16",
"episodeID": 2,
"openingCrawl": "There is unrest in the Galactic\r\nSenate. Several thousand solar\r\nsystems have declared their\r\nintentions to leave the Republic.\r\n\r\nSenator Amidala, the former\r\nQueen of Naboo, is returning\r\nto the Galactic Senate to vote\r\non the critical issue of creating\r\nan ARMY OF THE REPUBLIC\r\nto assist the overwhelmed\r\nJedi....",
"producers": [
"Rick McCallum"
]
}
]
}
}
When i am trying action to fetch Film by id (async calls using api middleware) in Film component, its calling and trying to update but all actions are looping and not working properly.
Please help me to understand and use redux actions properly.
App Codesandbox link https://codesandbox.io/s/adoring-jang-bf2f8m
Verify the console logs, can see actions looping....
Thanks.
Update::
Updated the above app to #redux/toolkit, below is the ref url
https://codesandbox.io/s/react-rtk-with-graphql-9bl8q7

You are using a highly outdated style of Redux here that will make you write 4 times the code at no benefit - modern Redux does not have ACTION_TYPE constants, switch..case reducers, hand-written middleware (at least in a case like yours), immutable reducer logic or hand-written action creators - all that since 2019. The tutorial you are following is highly outdated and the problems you are facing right now will not be problems for you if you go with the modern style.
Please do yourself a favor and go follow the official Redux tutorial. It also covers getting data from apis in chapters 5, 7 and 8.

Related

How to handle Polymorphic endpoints on Pact?

I have an app where I can search for Books and Movies. These 2 entities have different properties, so their JSON structures are different.
I also have a GET /favorites endpoint which should return both Books and Movies.
GET /favorites
{
"favorites": [
{
"type": "book",
"title": "Foo",
"author": "John"
},
{
"type": "movie",
"name": "Bar",
"producers": [
{
"firstName": "Mary"
}
]
}
]
}
I searched for for docs on this case but I can't find anything. How can I write a Pact contract for this use case?
I would write two separate test cases for this, and use Provider States to differentiate the two payloads.
For example:
When there are books
When there are movies
Or something to that effect. See [1] for related background on this.
[1] https://docs.pact.io/faq#why-is-there-no-support-for-specifying-optional-attributes

How can I retrieve a RingCentral call recording from a monitored incoming call?

I'm monitoring incoming calls on RingCentral by listening for the Call Session Notifications (CSN) telephony/sessions event filter:
/restapi/v1.0/account/~/extension/~/telephony/sessions
From this, I will receive events like the following. The recordings property will appear to indicate a recording is available. How can I retrieve this recording?
{
"uuid":"12345678901234567890",
"event":"/restapi/v1.0/account/11111111/extension/22222222/telephony/sessions",
"timestamp":"2019-03-08T22:30:40.059Z",
"subscriptionId":"11112222-3333-4444-5555-666677778888",
"ownerId":"33333333",
"body":{
"sequence":7,
"sessionId":"1234567890",
"telephonySessionId":"1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"serverId":"10.11.12.13.TAM",
"eventTime":"2019-03-08T22:30:39.938Z",
"parties":[
{
"accountId":"11111111",
"extensionId":"22222222",
"id":"cs12345678901234567890-2",
"direction":"Inbound",
"to":{
"phoneNumber":"+16505550100",
"name":"Jane Doe",
"extensionId":"22222222"
},
"from":{
"phoneNumber":"+14155550100",
"name":"John Smith"
},
"recordings":[
{
"id":"44444444",
"active":false
}
],
"status":{
"code":"Answered",
"rcc":false
},
"missedCall":false,
"standAlone":false,
"muted":false
}
],
"origin":{
"type":"Call"
}
}
}
There are two ways to retrieve the recording using information in the Call Session Notification (CSN) event, specifically the recordings[0].id property and the sessionID property.
retrieving a full media URL by calling the call-log endpoint with the sessionId property
manually creating recording media URL using the recordings[0].id property.
Note 1: While the call is ongoing, the recording will not be available for retrieval, even when the recording id is present in the Call Session Notification event. The recording will be available to be retrieved shortly after the call concludes.
Note 2: Call recordings can be in MP3 or WAV format determined by the company. To distinguish check the response Content-Type header for the MIME type when retrieving the recording media file.
1) Retrieving Full Medial URL via Call Log API
Making an intermediate API call to the call-log API has the dual benefits of being the official approach for receiving a media URL an providing more metadata for the call. In this approach, the recording.id in the call-log record will match the recordings[0].id property in the Call Session Notification event.
Both the company account and user extension call-log APIs can be called with the sessionId parameter from the event as shown:
GET /restapi/v1.0/account/~/call-log?sessionId={sessionId}
GET /restapi/v1.0/account/~/extension/~/call-log?sessionId={sessionId}
In this example, the sessionId is 1234567890 so you would have a Company Call Log API URL as follows
GET /restapi/v1.0/account/~/call-log?sessionId=1234567890
The response object will have a recording property that provides hypermedia links to get the media file. The file can be WAV or MP3 format which is communicated in the response Content-Type header.
{
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/extension/22222222/call-log?view=Simple&sessionId=1234567890&page=1&perPage=100",
"records": [
{
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/extension/22222222/call-log/1234567890ABCDEFGabcdefgh?view=Simple",
"id": "1234567890ABCDEFGabcdefgh",
"sessionId": "1234567890",
"startTime": "2019-03-08T22:30:29.505Z",
"duration": 35,
"type": "Voice",
"direction": "Inbound",
"action": "Phone Call",
"result": "Accepted",
"to": {
"phoneNumber": "+16505550100",
"name": "Jane Doe"
},
"from": {
"phoneNumber": "+14155550100",
"name": "John Smith",
"location": "San Francisco, CA"
},
"recording": {
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/recording/44444444",
"id": "44444444",
"type": "OnDemand",
"contentUri": "https://media.ringcentral.com/restapi/v1.0/account/111111111/recording/44444444/content"
},
"extension": {
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/111111111/extension/22222222",
"id": 22222222
},
"reason": "Accepted",
"reasonDescription": "The call connected to and was accepted by this number."
}
],
"paging": {
"page": 1,
"perPage": 100,
"pageStart": 0,
"pageEnd": 0
},
"navigation": {
"firstPage": {
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/extension/22222222/call-log?view=Simple&sessionId=1234567890&page=1&perPage=100"
},
"lastPage": {
"uri": "https://platform.ringcentral.com/restapi/v1.0/account/11111111/extension/22222222/call-log?view=Simple&sessionId=1234567890&page=1&perPage=100"
}
}
}
2) Manually Creating Media URL
You can call the Recording API endpoint and retrieve the media directly by manually constructing the recording URL as follows:
https://media.ringcentral.com/restapi/v1.0/account/{accountId}/recording/{recordingId}/content
In this example, the accountId is 11111111 and the recordingId is 44444444 for the following:
https://media.ringcentral.com/restapi/v1.0/account/11111111/recording/44444444/content
The accountId in the URL path can be set to the currently authorized user's account using ~. Alternately, it can be set explicitly by extracting the accountId from the event property or using the accountId property in the relevant party object. Using ~ is the recommended way to set accountId.
Note: This this approach can be quick, it may be error prone as RingCentral has changed the media hostname once in the past. While there are no anticipated, future changes, calling the call-log API and retrieving the full media URL from the response is the safer and recommended approach. See below for this approach. This is only included as some people will try this and potentially run into issues later.
3) Hybrid Approach
The first approach of calling the call-log end point is the recommended approach, however, it involves an extra API call and most of the time the second approach should work fine.
A hybrid approach is to construct the URL as in approach 2 and then fall back to approach 1 if approach 2 returns a 404 or other error.

Context size limit inside a node in Watson Conversation

My Watson Conversation bots typically have a node where I load some data into context. This usually contains all possible answers, strings, various other data.
So one of my first nodes in any bot looks like this:
{
"type": "standard",
"title": "Load Messages",
"output": {
"text": {
"values": [
""
],
"selection_policy": "sequential"
}
},
"context": {
// A whole bunch of data here
}
...
Is there a limit on how much data I can put there? Currently I have around 70 kilobytes, but potentially I can put a few megabytes there just for the convenience of running the logic inside Conversation. (Yes I am aware that this entire data will be sent back to the client, which is not very efficient)
There is no documented limit. You are more likely to hit network issues before Watson Assistant has any issues.
But storing your whole applications logic in the context object is considered an anti-pattern.
Your context object should only store what is required in Watson Assistant, and then if possible only for the related portion of the conversation.
For one time context values you can store them in the output object.
{
"context": {
},
"output": {
...
"one_time_var": "abc"
}
}
This will be discarded on your next call.
If you have a large volume of data that could be used at different times, then one pattern to use is a context request object.
For example:
"context": {
"request": "name,address,id"
}
Your next response from the application layer would send this:
"context": {
"name" : "Bob",
"address": "123 street",
"id": "1234"
}
You have your returning response update those variables, then clear the context variables again. If you have other context variables that need to stay, then store those in an object and erase just that object.

Wrong intent in Alexa Skill Request when using the simulator

I set up my intents using this intent schema:
{
"intents": [
{
"intent": "StartIntend"
},
{
"intent": "AMAZON.YesIntent"
},
{
"intent": "AMAZON.NoIntent"
}
]
}
My sample utterances look like this (it's german):
StartIntend Hallo
StartIntend Moin
StartIntend Guten Tag
Why does the Amazon Developer Console generate the following request, when I use the utterance "Yes" or "Ja"?
{
"session": {
"sessionId": "SessionId...",
"application": {
"applicationId": "amzn1.ask.skill...."
},
"attributes": {},
"user": {
"userId": "amzn1.ask.account...."
},
"new": true
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId...",
"locale": "de-DE",
"timestamp": "2017-02-17T21:07:59Z",
"intent": {
"name": "StartIntend",
"slots": {}
}
},
"version": "1.0"
}
Whatever I enter, it always is using the intend StartIntend.
Why is that? What have I forgotten / what have I done wrong?
The schema and utterance look correct.
I tried duplicating what you are seeing by performing the following steps:
Copied them as-is into a new skill on my account
Selected the North America region on the Configuration page.
Set the lambda to point to an existing lambda that I have. For testing purposes, I just need a valid ARN. I'm going to ignore the response anyways.
Then entered "Yes" into the service simulator
It indeed sent the Lambda the AMAZON.YesIntent.
So I conclude that there's nothing with the data you posted.
I tried entering Ja which resulted in the StartIntend, but I guess I would expect that since Ja is not "Yes" in North America.
Have you set the region to Europe, and entered a Lambda for the Europe region?
I talked about it with the Amazon Support. After some experiments it turned out, you have to write "ja" in lowercase. It seems to be a bug in the simulator itself.
When creating the skill in the Alexa Skills Kit, you need to choose the correct language i.e. German, see screenshot below.
Everything else seems to be correct.

Watson conversation API returns empty output for yes and no input

I've set up a very simple script and part of it requires a yes or no response from the user.
When I test the script through the script builder at ibmwatsonconversation.com the script works fine.
But when I'm testing it through Postman making HTTP POST requests when I get to the part that requires a yes or no answer the output node is always
"output": {
"log_messages": [],
"text": [],
"nodes_visited": [
"node_25_1480610375023"
]},
The previous two nodes in the conversation work fine.
I have set up intents for yes and no, see images below:
The dialog is as follows:
Here's the chain of requests / responses:
{"input": {"text": "hello"}}
"output": {"log_messages": [],"text": ["Welcome to the KMBC IT help desk.How can I help you?"],"nodes_visited": ["node_1_1480509441272"]},
then
{"input": {"text": "my laptop is broken"}}
"output": {
"log_messages": [],
"text": [
"I'm sorry to hear that your laptop isn't working. \n\nI need you to check a couple of things for me, is that ok?"
],
"nodes_visited": [
"node_3_1480509642420",
"node_19_1480518011225"
]},
finally
{"input": {"text": "yes"}}
"output": {
"log_messages": [],
"text": [],
"nodes_visited": [
"node_25_1480610375023"
]},
Works fine inside the "Try it out" panel within the workspace:
Full JSON request / response:
{"input": {"text": "hello"}}
{"intents": [{"intent": "greetings","confidence": 1}],"entities": [],"input": {"text": "hello"},"output": {"log_messages": [],"text": ["Welcome to the KMBC IT help desk. How can I help you?"],"nodes_visited": ["node_1_1480509441272"]},"context": {"conversation_id": "4b5b1858-ae4e-4907-a3ab-c49abf601fd3","system": {
"dialog_stack": [
{
"dialog_node": "root"
}
],
"dialog_turn_counter": 1,
"dialog_request_counter": 1
}}}
{"input": {"text": "laptop broken"}}
{"intents": [{"intent": "complaint","confidence": 0.989692384334575}],"entities": [
{"entity": "hardware",
"location": [
0,
6
],
"value": "laptop"
}],"input": {"text": "laptop broken"},"output": {"log_messages": [],"text": ["I'm sorry to hear that your laptop isn't working. \n\nI need you to check a couple of things for me, is that ok?"],"nodes_visited": ["node_3_1480509642420",
"node_19_1480518011225"]},"context": {
"conversation_id": "b53dff12-9252-4b7e-abe8-7b45f561d394",
"system": {"dialog_stack": [{"dialog_node": "node_19_1480518011225"}],
"dialog_turn_counter": 1,
"dialog_request_counter": 1}}}
{"input": {"text": "yes"}}
{"intents": [{"intent": "yes","confidence": 1}],"entities": [],"input": {"text": "yes"},"output": {"log_messages": [],"text": [],"nodes_visited": ["node_25_1480610375023"]},"context": {"conversation_id": "b9ddc5b0-5f3c-423f-9bbe-5a1ef013c175","system": {"dialog_stack": [{"dialog_node": "root"}],"dialog_turn_counter": 1,"dialog_request_counter": 1}}}
Based on the full JSON request/response, your issue is here (actually it's also in the previous call, but that works by concidence):
{"input": {"text": "yes"}}
Conversation is stateless, so you need to send back in the context object you received previously. Otherwise the system doesn't know where to continue. The following request object should look like this:
{
"input": {"text": "yes"},
"context": {
"conversation_id": "b53dff12-9252-4b7e-abe8-7b45f561d394",
"system": {
"dialog_stack": [{"dialog_node": "node_19_1480518011225"}],
"dialog_turn_counter": 1,
"dialog_request_counter": 1
}
}
I would recommend to use the Watson Developer Cloud SDK to manage this for you.
https://github.com/watson-developer-cloud
Have you actually created a yes and no intent ?
There is a lot of debate about the best process to handle yes and no responses. But I have found that by creating a yes and no intent, with example "yes" and "No" responses works well.
Your example questions for these intents could include responses like "ok", "yess", "on no","yes please" etc.

Resources