Properly chaining reducers when using combining them may not be appropriate - flux

In my application, the state tree has this shape:
{
...otherReducers,
rooms: Map({
// room being the room's id
room: Map({
id: '',
name: '',
users: Map({
// user being the user's id
user: Map({
id: '',
name: '',
rank: ' '
})
})
})
})
}
In order to update a user's state, I've been writing my reducers as follows:
function roomsReducer(state = Map(), action) {
case USER_RENAME:
return selectRoom(state, action);
default:
return state;
}
function selectRoom(state, action) {
let room = state.get(action.payload.id);
return state.set(action.payload.id, roomReducer(room, action));
}
// and so on and so forth until I reach the user I want to update
Is there a way I an combine the users reducer with room, despite it having id and name properties? If not, is there a better way I could be managing my state to make reducing more manageable?

This is a problem with how you structure your state. Editing users would be much easier if it was normalized like so:
{
...otherReducers,
rooms: Map({
5: Map({
id: '5',
name: 'some name',
users: [1,4,52]
})
}),
users: Map({
1: Map({
id: '1',
name: 'Charles',
range: 'Master'
}),
4: Map({
id: '4',
name: 'Sally',
range: 'Master'
}),
52: Map({
id: '52',
name: 'Harry',
range: 'Novice'
})
})
}

Related

Strapi how query relationship in CRON

I'm trying to write a CRON in strapi to query product that is post_status: "Draft" and username blocked is false. If true, it should not show and results.
The username has a one to many relations with the product.
"use strict";
module.exports = {
"*/1 * * * *": async () => {
const draftProductToPublishToFacebook = await strapi.api.product.services.product.find(
{
post_status: "Draft",
published_at_lt: new Date(),
username: {
blocked: false,
},
}
);
},
};
Sample data
{
post_status: 'Draft',
_id: 5eef02af7761f1425dd0ccec,
stock_status: 'test',
product_status: 'publish',
__v: 0,
username: {
confirmed: true,
blocked: false,
_id: 5eef05985f864742725ab8e1,
username: 'test2',
email: 'test2#test.com',
provider: 'local',
createdAt: 2020-06-21T07:00:40.996Z,
updatedAt: 2020-07-17T01:24:58.918Z,
__v: 0,
role: 5ee30d5424f89d5253877e90,
id: '5eef05985f864742725ab8e1'
},
published_at: 2020-08-02T00:20:00.000Z,
id: '5eef02af7761f1425dd0ccec'
}
The error I'm getting
(node:3652) UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value "{ blocked: false }" at path "username" for model "product"
Thanks
'username.blocked': false
should work.

Is there a way to add conversation history when connecting to direct line?

We are using botframework-webchat v4. Is there any way to provide history that will be shown up in the chat?
This is currently what I have, but its not working, not sure what format should be for activities in store.
const store = window.WebChat.createStore(
{
activities: ['{"type":"message",...}']
},
({ dispatch }: { dispatch: any }) => (next: any) => (action: any) => {
if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
const { activity } = action.payload;
if (activity.type === 'event' && activity.name === 'sample:backchannel') {
alert(JSON.stringify(activity, null, 2));
}
}
return next(action);
}
)
window.WebChat.renderWebChat(
{
directLine: this.directLine,
userID: this.userId,
styleOptions,
store
},
this.botWindowElement.nativeElement
);
Thanks in advance!!
You're solution above will, technically, work. Although, it's not very scalable in the long run. I would recommend you look over this BotFramework-WebChat experimental sample, Conversation History. It utilizes the sendConversationHistory API. This sample is a bit on the complex side, but will do precisely what you are wanting, i.e. load a previous user's conversation when a new session is started.
If you are wanting to reconnect a previous conversation (meaning continue a conversation using the same conversationId) then you should be aware that the Direct Line service has certain limitations. Reconnecting will only work for up to 14 days after the last activity to that conversation and only 24 hours if activities are present.
Hope of help!
#StevenKanberg Thanks for the help!
I found the answer in source code of BotFramework-WebChat.
Here is the sample,
test('absolute timestamp', async () => {
const activities = [
{
type: 'message',
id: '6266x5ZXhXkBfuIH0fNx0h-o|0000000',
timestamp: '2019-08-08T16:41:12.9397263Z',
from: {
id: 'dl_654b35e09ab4149595a70aa6f1af6f50',
name: '',
role: 'user'
},
textFormat: 'plain',
text: 'echo "Hello, World!"'
},
{
type: 'message',
id: '6266x5ZXhXkBfuIH0fNx0h-o|0000001',
timestamp: '2019-08-08T16:41:13.1835518Z',
from: {
id: 'webchat-mockbot',
name: 'webchat-mockbot',
role: 'bot'
},
text: 'Echoing back in a separate activity.'
},
{
type: 'message',
id: '6266x5ZXhXkBfuIH0fNx0h-o|0000002',
timestamp: '2019-08-08T16:41:13.3963019Z',
from: {
id: 'webchat-mockbot',
name: 'webchat-mockbot',
role: 'bot'
},
text: 'Hello, World!'
}
];
const styleOptions = { timestampFormat: 'absolute' };
const { driver } = await setupWebDriver({ storeInitialState: { activities }, props: { styleOptions } });

Why Doesn't This Bit of Code Work Without My Hack?

I have a situation where I'm making an API call to a Laravel back-end and eager-loading some data. The data is a collection of locations, each of the form:
location = {
id: ..
name: ...
...
docks: [
{
id: ...
name: ...
},
...
]
}
The end result of this epic should be to dispatch 2 actions, one with a list of the locations, and another with the list of all the associated docks
Taking it from the response to the api call, the following throws a Typescript error and won't compile:
.mergeMap(({ response }) => {
if (response.messages) {
return Observable.of(GetLocationsForReceiverFailedAction(response.messages))
}
const locations = response.locations.map((locn: any) => ({
id: locn.id,
receiver_id: locn.receiver_id,
name: locn.name,
address: locn.address,
city: locn.city,
state: locn.state,
zip: locn.zip
}))
const location$ = Observable.of(GetLocationsForReceiverSuccessAction(locations))
const docks: any[] = []
response.locations.forEach((locn: any) => {
locn.docks.forEach((dk: any) => {
docks.push(dk)
})
})
const docks$ = Observable.of(GetDocksForReceiverSuccessAction(docks))
return Observable.merge(location$, docks$)
})
.catch(() =>
Observable.of(
GetLocationsForReceiverFailedAction([
'Something went wrong trying to reach the server'
])
)
)
The error reads:
"Argument of type '({ response }: AjaxResponse) => Observable<{ type: ActionTypes; messages: string[]; }> | Observab...' is not assignable to parameter of type '(value: AjaxResponse, index: number) => ObservableInput<{ type: ActionTypes; messages: string[]; }>'."
If, however, I change the line:
return Observable.merge(location$, docks$)
to:
return Observable.merge(location$, Observable.empty(), docks$)
(ie, separating by 2 observables by an empty observable in the merge (or concat, which works the same way here), it compiles and runs.
What am I not understanding here?

GraphQL : Implementing windowed pagination for regular list

I'm trying to implement a windowed pagination using a "List". I don't need the cursor based solution with connections, because I need to show numbered pages to the user.
There are "User" and "Post" objects."User" has one-to-many relation to "Post".
Using graphql-js for schema,
here is my schema for userType and postType:
var userType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: globalIdField('User'),
posts: {
type: new GraphQLList(postType),
args: {
page:{
type: GraphQLInt,
defaultValue: 0
}
},
resolve: (_, args) => {
//code to return relevant result set
},
},
totalPosts:{
type: GraphQLInt,
resolve: () => {
//code to return total count
}
},
}),
interfaces: [nodeInterface],
});
var postType = new GraphQLObjectType({
name: 'Post',
fields: () => ({
id: globalIdField('Post'),
name: {type: GraphQLString},
//other fields
}),
interfaces: [nodeInterface],
});
Please notice the "totalPosts" field in "userType". Since there is going to be other Lists for the user,with the same paging needs, I'm going to end up maintaining lot of "total{Type}" variables in the fragment. This can be solved if I can send the totalCount within the List result somehow.
https://github.com/facebook/graphql/issues/4 this issue talks about implementing a wrapper over the List to include the totalCount in the result set.
I tried creating a wrapper like this:
var postList = new GraphQLObjectType({
name: 'PostList',
fields:()=>({
count: {
type: GraphQLInt,
resolve: ()=>getPosts().length //this is total count
},
edges: {
type: new GraphQLList(postType),
resolve: () => {
return getPosts() ; // this is results for the page, though I don't know how to use 'page' argument here
},
}
}),
interfaces: [nodeInterface],
});
but how should I connect this to the userType's posts field? And how can I use a 'page' argument on this wrapper, like I have in original userType?
how should I connect this to the userType's posts field? And how can I use a 'page' argument on this wrapper, like I have in original userType?
One simple way to implement what you're trying to do is to define a dumb wrapper type postList like this:
var postList = new GraphQLObjectType({
name: 'PostList',
fields:()=>({
count: { type: GraphQLInt },
edges: { type: new GraphQLList(postType) }
// Consider renaming 'edges'. In your case, it's a list, not a
// connection. So, it can cause confusion in the long run.
}),
});
Then in the userType definition, add a field of that wrapper type and define its resolve function like below. As for argument page, just describe it while defining the field type posts.
posts: {
type: postList,
args: {
page:{
type: GraphQLInt,
defaultValue: 0
},
...otherArgs
},
resolve: async (_, {page, ...otherArgs}) => {
// Get posts for the given page number.
const posts = await db.getPosts(page);
// Prepare a server-side object, which corresponds to GraphQL
// object type postList.
const postListObj = {
count: posts.length,
edges: posts
};
// Consider renaming 'edges'. In your case, it's a list, not a
// connection. So, it can cause confusion in the long run.
},
},

sails waterline sort on number not working(sails-redis)

I am trying to do sort id
Modal:
module.exports = {
autoPK: false,
attributes: {
id: {
type: 'integer',
autoIncrement:true,
primaryKey: true
},
}
}
Query:
mymodal.find().sort({id: 'asc'}).exec(function (err, res) {
console.log(res)
});
Data:
[ { id: '2', },{ id: '1'},{ id: '11' } ]
Actual:
[ { id: '1', },{ id: '11'},{ id: '2' } ]
**Expected:
[ { id: '1', },{ id: '2'},{ id: '11' } ]**
Can anybody help me out. Please..
Sorting on string working, but on number(interger).
Is there any with my query or issue with sails waterline criteria sort
First you should do query find and later sort()
Modal.find()
.sort({id: 'asc'})
.exec(function(err, res) {
console.log(res)
});
I went through the sails-redis adapter index and schema, few things are missing. while parsing the input data like '11' the method not worried about the data type.
Data like { id: 1 } and { id: '1'} is consider to be different even though in the model
type: 'integer
specified.
sails-redis/lib/database/schema.js
changes code:
Schema.prototype.parse
.
.
case 'integer':
values[key] = parseInt(values[key]);
break;
case 'float':
values[key] = parseFloat(values[key]);
break;
}
}
.
.
changes working for me. Make sure the values[key] is not null
The documentation isn't great on this, but it looks like you might need to use binary notation if you pass an object to sort(). Easier to just pass a string:
Model.find().sort('id asc').exec(function (err, res) {
console.log(res)
});

Resources