How to use schemaAttribute as a string for Array in normalizr - normalizr

I was looking into the docs about array schema and under the schemaAttribute option it says that we can pass a string and it is optional for one schema.
I've tried lots of combinations but I can't get it done, I get errors or my data is not normalized correctly.
For example:
const adminSchema = new schema.Entity('admins');
const myArray = new schema.Array({
new: adminSchema
}, 'new');
or
const myArray = new schema.Array(adminSchema, 'new');
How can I achieve this?
Beside that, what would be the reason of it?

After looking at the source code I realised how I can use it
const data = [ { id: 1, type: 'admins' }, { the_ID: 2, type: 'users' } ];
const userSchema = new schema.Entity('users', {}, {
idAttribute: 'the_ID'
});
const adminSchema = new schema.Entity('admins');
const myArray = new schema.Array({
admins: adminSchema, users: userSchema
}, 'type');

Related

Optimistic response not working when adding items to list

My data model is a list with items. Very simple:
{
_id: 1,
name: "List 1",
items: [
{ _id: 2, text: "Item text 1" },
{ _id: 3, text: "Item text 2" }
]
}
Adding a new list with optimistic response works perfectly:
const [addListMutation] = useAddListMutation({
update: (cache, { data }) => {
const cachedLists =
(cache.readQuery<GetAllListsQuery>({
query: GetAllListsDocument,
})?.lists as TList[]) ?? [];
if (data) {
cache.writeQuery({
query: GetAllListsDocument,
data: {
lists: [...cachedLists, data?.list as TList],
},
});
}
},
});
const addList = async (name: string) => {
const list = {
_id: ..new id here,
name,
items: [],
};
const variables: AddListMutationVariables = {
data: list,
};
await addListMutation({
variables,
optimisticResponse: {
list,
},
});
};
This gets reflected immediately in my component using const { loading, data } = useGetAllListsQuery();. data is updated twice; first with the optimistic response and then after the mutation is done. Just like expected.
Now I'm trying to add an item to the list this way:
const [updateListMutation] = useUpdateListMutation({
update: (cache, { data }) => {
const cachedLists =
(cache.readQuery<GetAllListsQuery>(
{
query: GetAllListsDocument,
},
)?.lists as TList[]) ?? [];
if (data?.list) {
// Find existing list to update
const updatedList = data?.list as TList;
const updatedListIndex = cachedLists.findIndex(
(list: TList) => list._id === updatedList._id,
);
// Create a copy of cached lists and replace entire list
// with new list from { data }.
const updatedLists = [...cachedLists];
updatedLists[updatedListIndex] = { ...updatedList };
cache.writeQuery({
query: GetAllListsDocument,
data: {
lists: updatedLists,
},
});
}
}
});
const updateList = async (updatedList: TList) => {
const variables: UpdateListMutationVariables = {
query: {
_id: updatedList._id,
},
set: updatedList,
};
await updateListMutation({
variables,
optimisticResponse: {
list: updatedList,
},
});
};
const addListItem = async (list: TList, text: string) => {
const updatedList = R.clone(list);
updatedList.items.push({
_id: ...new item id here,
text: 'My new list item',
});
await updateList(updatedList);
};
The problem is is in my component and the const { loading, data } = useGetAllListsQuery(); not returning what I expect. When data first changes with the optimistic response it contains an empty list item:
{
_id: 1,
name: "List 1",
items: [{}]
}
And only after the mutation response returns, it populates the items array with the item with text 'My new list item'. So my component first updates when the mutation is finished and not with the optimistic response because it can't figure out to update the array. Don't know why?
(and I have checked that the updatedLists array in writeQuery correctly contains the new item with text 'My new list item' so I'm trying to write the correct data).
Please let me know if you have any hints or solutions.
I've tried playing around with the cache (right now it's just initialized default like new InMemoryCache({})). I can see the cache is normalized with a bunch of List:1, List:2, ... and ListItem:3, ListItem:4, ...
Tried to disable normalization so I only have List:{id} entries. Didn't help. Also tried to add __typename: 'ListItem' to item added, but that only caused the { data } in the update: ... for the optimistic response to be undefined. I have used hours on this now. It should be a fairly simple and common use case what I'm trying to do :).
package.json
"#apollo/client": "^3.3.4",
"graphql": "^15.4.0",
"#graphql-codegen/typescript": "^1.19.0",

How can you base a Joi `when` condition on a value from the context?

Joi's ref documentation suggests that a Reference created using Joi.ref can refer to a value from the context object. Joi's any.when documentation suggests that the condition parameter accepts a Reference. However, I can't get the following simple usage to work:
import Joi from "joi";
const schema = Joi.object({
year: Joi.number().when(Joi.ref(`$flag`), {
is: Joi.boolean().truthy,
then: Joi.number().min(2000),
}),
});
const value = {
year: 1999,
};
const context = {
flag: true
};
const result = schema.validate(value, { context });
This code results in a passing validation. However, I would expect it to fail. What am I missing?
You need to use boolean().valid(true) so only the boolean value true passes!
boolean().truthy() accepts a list of additional values to be considered valid booleans. E.g. boolean().truthy('YES', 'Y'). Details in the Documentation
joi version: 17.2.1
const Joi = require('joi');
const schema = Joi.object({
year: Joi.number().when(
Joi.ref('$flag'), {
is: Joi.boolean().valid(true), then: Joi.number().min(2000),
},
),
});
const value = {
year: 1999,
};
// fails
const context = {
flag: true
};
// passes
const context = {
flag: false
};
const result = schema.validate(value, { context });
console.log(result.error);

How to make sequalize createTable support underscore?

I was trying to use sequalize seed to initialize the table rows, but I got error return:
column "createdAt" of relation "users" does not exist
It actually is "created_at" as the field, so my question is that how can I seed the table with snake or underscore option?
My seed code as following:
"use strict";
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert(
"users",
[
{
firstname: "John",
lastname: "Doe",
email: "demo#demo.com",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete("users", null, {});
}
};
You pass those information while defining your configurations for database connection like below.
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
config.define = {
"timestamps":true,
"createdAt":"created_at",
"updatedAt":"updated_at"
}
sequelize = new Sequelize(config.database, config.username, config.password,config );
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
Here config is your db configuration. You add other configuration on a separate define
As you can see all in a documentation queryInterface methods work with tables and not models so you should use real column names instead of model props.

Issue while updating store from updater function of commitMutation

I have a mutation
mutation createQuoteLineMutation {
createQuoteLine {
quoteLine {
name
price
product {
name
}
}
}
}
My updater function is as below.
updater: (store) => {
const payload = store.getRootField('createQuoteLine');
const newQuoteLine = payload.getLinkedRecord('quoteLine');
const quote = store.getRoot().getLinkedRecord('getQuote');
const quoteLines = quote.getLinkedRecords('quoteLines') || [];
const newQuoteLines = [...quoteLines, newQuoteLine];
quote.setLinkedRecords(newQuoteLines, 'quoteLines');
}
This works fine for the first time, but the consequent mutations all the previously added quoteLines change to new one I'm assuming this is because newQuoteLine points to same object all the time.
adding below line at the end of updater function unlink quoteLine from createQuoteLine also does not work.
payload.setValue(null, 'quoteLine');
Any help in this regard is highly appreciated.
I have seen a quite similar problem, but I am not sure if it's the same. Try to pass an clientMutationId to the mutation, and increment it along.
const commit = (
input,
onCompleted: (response) => void,
) => {
const variables = {
input: {
...input,
clientMutationId: temp++,
},
};
commitMutation(Environment, {
mutation,
variables,
onCompleted,
onError: null,
updater: store => {
// ....
},
});
};
Try something like this and let me know if it fixes :).

hi, is there a way to get pdf document thru apollo2angular and graphql?

following is the response info:
"%PDF-1.4\n%����\n2 0 obj\n<>stream\nx��X�s�F\u0010�\u0019����$\u0010JaF\u000f\t\t�*:Iw�:�h������-ڀg/}]�yk��Z\u0002��0Vڈ��&��\n��ɐֳ�L\u0012?�4\u000bi���\u001b�D\u0017\tPقYl���\u0019p\u0010:\-g���Oǜ�F�\u0019�L�&�_c6�'\u0016��cv���fhm�ҏ?;�ǴK�U��d\u0007�!����qG{,k�M��\u001e۬�)-�E�GU¢\u0019�\u0003(�����q2R�\u001c%K�vC���[�;�j\u0004�E�PO�hH��dk\u0011\fN����騈���\u000fs=8���9\u001fY�\u00185t\u001d��A0Sa��!\u000e��i\u000f�(1R�q����>�\u0017�i��\u0017<�$��\u0015�;�|/^�;���v̺H\u0019�1�\t�#��m�?�\u00137q�\u0004�\\u0010ְ'M鴨\u000b���0�9B1�ێ���=�K,Z\"'���\u0019���3��W\u0010q\u0000A�D��� �Hh���\u0017�c�k��̉�i�W\u000b�Mph�P�#��0W:ʥ\u0003�*ӕ�9���\u0015OG��\u000e�$A)>�\u0018H*�R�1 7��~ch\u001a�CUfQ�j�9��+��K��Џ�\u001a{�G��)\f�D\u0012\f���(C\u0005��?ݗ��m�������c��0Ϩ�v#*#Hp�\u0019�Y�!��7�-�\u001b�a�w�3��&�T0q=�K�-ؚ�\u0018\u000e�)���]N�P\u0019ZB.�$w\u0015�\u0006n�&6|
Hey yes there is but it takes a little custom work!
Here is how you can do it to work with scaphold.io but you could extend this to your own implementation as well!
Basically what is happening is you attach the file at the root level of a multipart/form-data request and then from the input in your GraphQL variables you point to the key of the file in the request. Here is some code to show how you can retrofit and ApolloClient instance with a file-ready network interface that you can then feed into Angular2Apollo.
import { createNetworkInterface } from 'apollo-client';
// Create a file-ready ApolloClient instance.
export function makeClientApolloFileHandler() {
const graphqlUrl = `https://scaphold.io/graphql/my-app`;
const networkInterface = createNetworkInterface(graphqlUrl);
networkInterface.query = (request) => {
const formData = new FormData();
// Parse out the file and append at root level of form data.
const varDefs = request.query.definitions[0].variableDefinitions;
let vars = [];
if (varDefs && varDefs.length) {
vars = varDefs.map(def => def.variable.name.value);
}
const activeVars = vars.map(v => request.variables[v]);
const fname = find(activeVars, 'blobFieldName');
if (fname) {
const blobFieldName = fname.blobFieldName;
formData.append(blobFieldName, request.variables[blobFieldName]);
}
formData.append('query', printGraphQL(request.query));
formData.append('variables', JSON.stringify(request.variables || {}));
formData.append('operationName', JSON.stringify(request.operationName || ''));
const headers = { Accept: '*/*' };
if (localStorage.getItem('scaphold_user_token')) {
headers.Authorization = `Bearer ${localStorage.getItem('scaphold_user_token')}`;
}
return fetch(graphqlUrl, {
headers,
body: formData,
method: 'POST',
}).then(result => result.json());
};
const clientFileGraphql = new ApolloClient({
networkInterface,
initialState: {},
});
return clientFileGraphql;
}
After this function does its work on the request you might have FormData that looks like this (if it were encoded in JSON):
{
"query": "mutation CreateFile($input:CreateFileInput) { createFile(input: $input) { ... }",
"variables": {
"input": {
"blobFieldName": "myFile",
"name": "MyFileName!"
}
},
// Note how the blobFieldName points to this key
"myFile": <Buffer of file data in multipart/form-data>
}
The server would then need to understand how to interpret this in order to look for the file in the payload and associate it with the correct object etc.
Hope this helps!

Resources