Normalizr - Setting key of nested responses in results - redux

I see in the normalizer examples that they have a name property "users" being returned on the results object:
{
result: { users: [ 1, 2 ] },
entities: {
users: {
'1': { id: 1 },
'2': { id: 2 }
}
}
}
I can't seem to figure out how to do this with the nested api response I'm getting. I have both a user and an address reducer that I am trying to pass the results of the normalized response into.
I have a JSON response that looks like this:
[
{
id: 1
first_name: First,
last_name: Last,
address: {
data: [
{
id: 1,
address_one: '123 Street Ave',
address_two: '',
city: 'Some City',
state: 'CA',
zip: '1234'
}
]
}
},
{
id: 1
first_name: First,
last_name: Last,
address: {
data: [
{
id: 2,
address_one: '123 Street Ave',
address_two: '',
city: 'Some City',
state: 'CA',
zip: '1234'
},
{
id: 3,
address_one: '321 Avenue Road',
address_two: 'Suite 101',
city: 'Some City',
state: 'CA',
zip: '1234'
}
]
}
}
]
My schema looks like this:
import { schema } from 'normalizr'
/**
* Addresses
*/
const address = new schema.Entity('addresses');
const arrayOfAddresses = new schema.Array(address);
/**
* User
*/
export const user = new schema.Entity('users', {
addresses: arrayOfAddresses
});
user.define({
addresses: {
data: arrayOfAddresses
}
})
export const arrayOfUsers = new schema.Array(user)
Doing: let result = normalize(data, schema.arrayOfUseres)
returns:
{
entities: {
addresses: /* The address data */
users: /* The users with an array of the address ids attached to them*/
},
result: [1, 2]
}
What I would really like is the result object to have both the users and the addresses in it:
{
entities: {
addresses: /* The address data */
users: /* The users with an array of the address ids attached to them */
},
result: {
addresses: [1, 2, 3]
users: [1, 2]
}
}
Is this possible? I've tried several variations of
import * as schema from './schema'
normalize(data, { users: [schema.user], addresses: [schema.address] }
But this just errors out and returns my data back into the result object.

Is this possible?
No. The result value is always set to match the top structure of your input data per the schema. If your input data doesn't have a first-level key addresses, it won't be possible.

Related

DynamoDB result is not structured the way I want

I am doing a simple command to list all the items in my table. However, the data I am getting back is not structured the way I want. I want a simple JSON structure but DynamoDB is turning the results into nested objects.
DynamoDB gives me below response:
// What I am currently getting
[
{
id: { S: '8' },
lastName: { S: 'Perry' },
firstName: { S: 'Matthew' }
},
{
id: { S: '3' },
firstName: { S: 'Joan' },
lastName: { S: 'Peter' }
}
]
But I want this:
// What I want
[
{
id: 8
lastName: 'Perry' ,
firstName: 'Matthew'
},
{
id: 3,
firstName: 'Joan' ,
lastName: 'Peter'
}
]
How can I achieve the later result set. Below is my code:
const { ExecuteStatementCommand } = require('#aws-sdk/client-dynamodb')
const { ddbDocClient, memberTableName } = require('./client.js')
const selectAll = async () => {
const params = {
Statement: `SELECT * FROM ${memberTableName}`,
Parameters: [{ S: '3' }]
}
console.log(params)
return await ddbDocClient.send(new ExecuteStatementCommand(params));
}
selectAll()
.then(d => console.log(d.Items))
ddbDocClient was created like this:
const ddbDocClient = DynamoDBDocumentClient.from(ddbClient);
The command import is incorrect. To send and receive native JS types, import the ExecuteStatementCommand command from the #aws-sdk/lib-dynamodb "document client" package.
const { ExecuteStatementCommand } = require('#aws-sdk/lib-dynamodb');
You are importing the command from the "regular client" package #aws-sdk/client-dynamodb, i.e. the one that accepts and returns DynamoDB JSON.
Note: The Parameters: [{ S: '3' }] line is also wrong, but it's currently not causing trouble because your statement is scanning for all records. If you were to include a WHERE id=? phrase in the statement, make sure to change the parameters to Parameters: ['3']. You must pass JS types to the "document client" commands.
You need use simple lib https://www.npmjs.com/package/#aws-sdk/util-dynamodb
then use like this:
const { DynamoDB } = require("#aws-sdk/client-dynamodb");
const { marshall, unmarshall } = require("#aws-sdk/util-dynamodb");
const client = new DynamoDB(clientParams);
const params = {
TableName: "Table",
Key: marshall({
HashKey: "hashKey",
}),
};
const { Item } = await client.getItem(params);
unmarshall(Item);

Firestore: Query documents by property of object

I've a collection with contacts with a structure like:
name: 'XPTO Company',
emails: {
susan#xpto.com: { name: 'Susan', text: 'manager' },
fred#xpto.com: { name: 'Fred', text: 'marketing' }
}
How do I retrieve documents with email 'susan#xpto.com'
Something like:
firebase.firestore().collection('contacts')
.where(new firebase.firestore.FieldPath('emails', email), '==', true).get()
.then(snap => {
})
You may add a property id that holds the email address to your email object. and replace '.' in email object key with '-', e.g. susan#xpto-com, so that your data structure looks like this:
name: 'XPTO Company',
emails: {
susan#xpto-com: { id: 'susan#xpto.com', name: 'Susan', text: 'manager' },
fred#xpto-com: { id: 'fred#xpto.com', name: 'Fred', text: 'marketing' }
Then you can retrieve documents with email 'susan#xpto.com' in this way:
firebase.firestore().collection('contacts')
.where('emails.susan#xpto-com.id', '==', 'susan#xpto.com').get()
.then(snap => {
})

Apollo/GraphQL: Setting Up Resolver for String Fields?

In GraphiQL at http://localhost:8080/graphiql, I'm using this query:
{
instant_message(fromID: "1"){
fromID
toID
msgText
}
}
I'm getting this response:
{
"data": {
"instant_message": {
"fromID": null,
"toID": null,
"msgText": null
}
},
"errors": [
{
"message": "Resolve function for \"instant_message.fromID\" returned undefined",
"locations": [
{
"line": 3,
"column": 5
}
]
},
{
"message": "Resolve function for \"instant_message.toID\" returned undefined",
"locations": [
{
"line": 4,
"column": 5
}
]
},
{
"message": "Resolve function for \"instant_message.msgText\" returned undefined",
"locations": [
{
"line": 5,
"column": 5
}
]
}
]
}
I tried to set up my system according to the examples found here:
https://medium.com/apollo-stack/tutorial-building-a-graphql-server-cddaa023c035#.s7vjgjkb7
Looking at that article, it doesn't seem to be necessary to set up individual resolvers for string fields, but I must be missing something.
What is the correct way to update my resolvers so as to return results from string fields? Example code would be greatly appreciated!
Thanks very much in advance to all for any thoughts or info.
CONNECTORS
import Sequelize from 'sequelize';
//SQL CONNECTORS
const db = new Sequelize(Meteor.settings.postgres.current_dev_system.dbname, Meteor.settings.postgres.current_dev_system.dbuser, Meteor.settings.postgres.current_dev_system.dbpsd, {
host: 'localhost',
dialect: 'postgres',
});
db
.authenticate()
.then(function(err) {
console.log('Connection to Sequelize has been established successfully.');
})
.catch(function (err) {
console.log('Unable to connect to the Sequelize database:', err);
});
const IMModel = db.define('IM', {
id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true},
fromID: {type: Sequelize.STRING},
toID: {type: Sequelize.STRING},
msgText: {type: Sequelize.STRING}
});
IMModel.sync({force: true}).then(function () {
// Table created
return IMModel.create({
fromID: '1',
toID: '2',
msgText: 'msg set up via IMModel.create'
});
});
const IM = db.models.IM;
export {db, IM };
SCHEMA
const typeDefinitions = [`
type instant_message {
id: Int
fromID: String
toID: String
msgText: String
}
type Query {
instant_message(fromID: String, toID: String, msgText: String): instant_message
}
type RootMutation {
createInstant_message(
fromID: String!
toID: String!
msgText: String!
): instant_message
}
schema {
query: Query,
mutation: RootMutation
}
`];
export default typeDefinitions;
RESOLVERS
import * as connectors from './db-connectors';
import { Kind } from 'graphql/language';
const b = 100;
const resolvers = {
Query: {
instant_message(_, args) {
const a = 100;
return connectors.IM.find({ where: args });
}
},
RootMutation: {
createInstant_message: (__, args) => { return connectors.IM.create(args); },
},
};
export default resolvers;
When you define your GraphQLObjectTypes you need to provide a resolver for each of their fields.
You defined your instant_message with multiple fields but did not provide resolvers for each of these fields.
More over you defined the types of those field with regular typescript fields while you need to define it with GraphQL types (GraphQLInt, GraphQLString, GrapQLFloat etc..)
So defining your type should look something like this:
let instant_message = new GraphQLObjectType({
id: {
type: GraphQLInt,
resolve: (instantMsg)=> {return instantMsg.id}
}
fromID: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.fromID}
}
toID: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.toID}
}
msgText: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.msgText}
}
})
In addition, you will need to define your Query as follows:
let Query = new GraphQLObjectType({
name: "query",
description: "...",
fields: () => ({
instant_messages: {
type: new GraphQLList(instant_message),
args: {
id: {type: GraphQLInt}
},
resolve: (root, args) => {
connectors.IM.find({ where: args })
}
}
})
})
The issue is that the query does not expect an array,
Please fix it:
type Query {
instant_message(fromID: String, toID: String, msgText: String): [instant_message]
}
Then you should make sure the resolver returns Array of objects, if it doesnt work then the resolver is not returning an Array.

Redux and Normalizr placing single entity into array

I'm using Redux and Normalizr.
I have a state which looks like:
{
entities: {
users: {
1: {...},
2: {...}
}
},
data: {
users: [1,2]
}
}
One of my API endpoints returns an object that includes a single user and some other data:
data = {
user: {id: 3, ...}
otherstuff: {...}
}
const user = new Schema('users');
normalize(data, {user: user})
This returns
{
entities: {
users: {
3: {id: 3, ...}
}
},
result: {
user: 3
}
}
However my data reducer expects to merge in a users array (of ids):
function data(state = {}, action) {
if (action.response && action.response.result) {
return deepmerge(state, action.response.result)
}
return state
}
Ideally I'd address this issue in the normalisation process rather than changing the reducer. Is there an easy way to either get normalizr to parse {user: {id: 3}} into {result: {users: [3]}}? It already changes the entities key to users.
If not, is there a clean and generic reducer-level solution to having this problem across a variety of entity type names?

Using Firebase-util to resolve one to many relationship

I've been looking at the firebase util project at https://firebase.github.io/firebase-util/. I can't figure out if this is possible.
Say I have a data set like this:
{ clients:
{ client1: { groups: { group1: true, group2: true }, name: 'client1' },
client2: { groups: { group3: true, group4: true }, name: 'client2' } },
groups:
{ group1: { name: 'group1' },
group2: { name: 'group2' },
group3: { name: 'group3' },
group4: { name: 'group4' },
group5: { name: 'group5' } } }
I want to retrieve "client1" and resolve all the groups associated with client1, giving me back a dataset like this.
{ client1: { groups: { group1: { name: 'group1' }, group2: { name: 'group2' } }, name: 'client1' }
Is there a way to do this with Firebase-util?
There is an example on the website that is similar, where it is joining an account to a profile and then mapping the style to another reference point.
var ref = Firebase.util.join(
new Firebase('INSTANCE/account'),
{
ref: new Firebase('INSTANCE/profile'),
keyMap: {
name: 'name',
nick: 'nick',
style: new Firebase('INSTANCE/styles')
}
}
);
But, I'm not seeing how to do it with what I'm looking for.
Something like:
FirebaseUtil.join(
{ ref: new Firebase('INSTANCE/clients/client1'), keyMap: {name: 'name', groups: new Firebase('INSTANCE/groups')} }
)
Thanks for any hints!
You cannot use a nested list of groups as the keymap for clients/$client/groups. It must be a string pointing to exactly one reference.
The solution would be to retrieve the list separately from the meta data for the user:
var fb = new Firebase(URL);
getClients(fb.child('clients'), function(userId, data) {
console.log(userId, data);
});
function getGroupsForClient(clientId, callback) {
var indexRef = fb.child('clients/'+clientId+'/groups');
var groupsRef = fb.child('groups');
var intersectionRef = new Firebase.util.intersection(indexRef, groupsRef);
intersectionRef.once('value', callback);
}
function getClients(callback) {
fb.child('clients').on('child_added', function(snap) {
var userId = snap.name();
var userData = snap.val();
getGroupsForClient(userId, function(snap) {
userData.groups = snap.val();
callback(userId, userData);
});
});
}

Resources