How to update the cache, after creating new data?
Error message from Apollo
Store error: the application attempted to write an object with no provided id but the store already contains an id of UsersPermissionsUser:1 for this object. The selectionSet that was trying to be written is:
{
"kind": "Field",
"name": { "kind": "Name", "value": "user" },
"arguments": [],
"directives": [],
"selectionSet": {
"kind": "SelectionSet",
"selections": [
{ "kind": "Field", "name": { "kind": "Name", "value": "username" }, "arguments": [], "directives": [] },
{ "kind": "Field", "name": { "kind": "Name", "value": "__typename" } }
]
}
}
Nativescript-vue Front-end Details
1- Watch Book Mobile app in action on YouTube: https://youtu.be/sBM-ErjXWuw
2- Watch Question video for details on YouTube: https://youtu.be/wqvrcBRQpZg
{N}-vue AddBook.vue file
apolloClient
.mutate({
// Query
mutation: mutations.CREATE_BOOK,
// Parameters
variables: {
name: this.book.name,
year: this.book.year,
},
// HOW TO UPDATE
update: (store, { data }) => {
console.log("data ::::>> ", data.createBook.book);
const bookQuery = {
query: queries.ALL_BOOKS,
};
// TypeScript detail: instead of creating an interface
// I used any type access books property without compile errors.
const bookData:any = store.readQuery(bookQuery);
console.log('bookData :>> ', bookData);
// I pin-pointed data objects
// Instead of push(createBook) I've pushed data.createBook.book
bookData.books.push(data.createBook.book);
store.writeQuery({ ...bookQuery, data: bookData })
},
})
.then((data) => {
// I can even see ID in Result
console.log("new data.data id ::::: :>> ", data.data.createBook.book.id);
this.$navigateTo(App);
})
.catch((error) => {
// Error
console.error(error);
});
What are these "Book:9": { lines in the cache?
console.log store turns out:
"Book:9": {
"id": "9",
"name": "Hadi",
"year": "255",
"__typename": "Book"
},
"$ROOT_MUTATION.createBook({\"input\":{\"data\":{\"name\":\"Hadi\",\"year\":\"255\"}}})": {
You can see all front-end GitHub repo here
Download Android apk file
Our goal is to update the cache. Add Book Method is in here:
https://github.com/kaanguru/mutate-question/blob/c199f8dcc8e80e83abdbcde4811770b766befcb5/nativescript-vue/app/components/AddBook.vue#L39
Back-end details
However, this is a frontend question a running Strapi GraphQL Server is here: https://polar-badlands-01357.herokuapp.com/admin/
GraphQL Playground
USER: admin
PASSWORD: passw123
You can see GraphQL documentation
I have so much simple Strapi GrapQL Scheme:
If you want to test it using postman or insomnia you can use;
POST GraphQL Query URL: https://polar-badlands-01357.herokuapp.com/graphql
Bearer Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTkwODI3MzE0LCJleHAiOjE1OTM0MTkzMTR9.WIK-f4dkwVAyIlP20v1PFoflpwGmRYgRrsQiRFgGdqg
NOTE: Don't get confused with $navigateTo() it's just a custom method of nativescript-vue.
It turns out;
all code was correct accept bookData.push(createBook);
// HOW TO UPDATE
update: (store, { data }) => {
console.log("data ::::>> ", data.createBook.book);
const bookQuery = {
query: queries.ALL_BOOKS,
};
// TypeScript detail: instead of creating an interface
// I used any type access books property without compile errors.
const bookData:any = store.readQuery(bookQuery);
console.log('bookData :>> ', bookData);
// I pin-pointed data objects
// Instead of push(createBook) I've pushed data.createBook.book
bookData.books.push(data.createBook.book);
store.writeQuery({ ...bookQuery, data: bookData })
},
})
Typescipt was helping
The point is; I shouldn't trust TypeScript errors, or at least I should read more about what it really says.
Typescript just asked me to be more specific while saying: Property 'push' does not exist on type 'unknown'
TypeScript was trying to tell me I need to be more specific while calling ROOT_MUTATION data. It said: Cannot find name 'createBook' But again I ignored it.
Solution Github Branch
https://github.com/kaanguru/mutate-question/tree/solution
Sources
how to update cache
Create interface for object Typescript
Related
How to delete all products via admin api?
To achieve the goal i try to use the Bulk Payloads | Deleting entities
The doc says:
[...] To delete entities, the payload of an operation contains the IDs. [...]
Questions:
to delete all products i have to read first all product.id's?
or is there a alternative way with a type of "wildcard"?
My current request body (using Postman) ...:
{
"delete-product": {
"entity": "product",
"action": "delete",
"payload": []
}
}
... response with (products remains in db):
{
"extensions": [],
"success": true,
"data": {
"delete-product": {
"extensions": [],
"result": []
}
},
"deleted": [],
"notFound": []
}
EDIT #1
With id's provided...:
...
const obj = {
"delete-products": {
"entity": "product",
"action": "delete",
"payload": [
{"id": "73af65014974440b95450f471b3afed8"},
{"id": "784f25a29e034fad9a416923f964ba8a"}
]
}
}
apiClient.request({
"url": "/_action/sync",
"method": "POST",
obj
})
...
... the request fails in class Symfony\\Component\\Serializer\\Encoder\\JsonDecode with message:
detail: "Syntax error"
Debugging the request, payload is missing (empty content):
What is wrong with the configuration of the /api/_action/sync call?
Indeed, what it means is that you will need a low impacting query to get all product id's, store it into a variable & delete them. Use includes:["id"] filter to just get the ID's.
Here is an example of me deleting some products in Postman.
Request body:
{
"delete-product": {
"entity": "product",
"action": "delete",
"payload": {{gen_dynamic_products}}
}
}
Pre-request script (you'll need to adjust this sightly to get your ID's):
const map = new Array(30).fill(0).map((val, index) => {
return { id: pm.environment.get('gen_product_list_sub_' + index) };
});
pm.variables.set('gen_dynamic_products', JSON.stringify(map));
to delete all products i have to read first all product.id's?
Yes, that is what you'll have to do. This is necessary to maintain the extendibility of the platform. The core or other plugins may react to the deletion of products by subscribing to an entity lifecycle event. This event includes the id of the deleted entity. Hence why it is necessary to explicitly provide the ids of the entities in the first place.
I am setting properties when adding to cart.
Ex:
var formdata=[
"items":{
id:123456,
quantity:1
properties:{'flag':true}
}
];
added using api : /cart/add.js
Details I get from cart.js without refresh
response from : /cart/add.js and cart.js
[
{
"id": 32423423423423,
"properties": {
"flag": true
},
"quantity": 1,
"variant_id": 42705234345345,
}
]
The above items get added successfully to cart. After adding I again fetch the cart details and It has this properties value.But when I refresh the page cart items properties does not have any value.
Ex Currently I am getting this only when page is refreshed:
response from cart.js after page refresh
properties:{
Ref: 0
}
What this properties is?
Why is this happening? If worked, will this properties be available on order create webhook? It only disappears when refreshed. Moreover main reason to add this properties is to receive this properties in order-create webhook to distinguish from normal order. If anyone having other alternative please suggest.
Adding a product to the cart like so:
fetch('/cart/add.js', {
method: "post",
headers: {
"content-type": "application/json"
},
body: JSON.stringify({
items: [
{
quantity: 1,
id: 33116507373620,
properties: {
'flag': true
}
}
]
})
})
And getting the cart.js like so:
fetch('/cart/add.js').then(res => res.json()).then(res => console.log(res))
Will get you result like so:
{
...
"items": [
{
"id": 33116507373620,
"properties": {
"flag": true
},
"quantity": 1,
...
}
],
...
}
From there on what you are doing to not get this result I'm not sure, since this is working/tested and there is no issue.
Please double check if you are targeting the correct object once you get the cart.js response. (there is no properties direct object, it's under items[0].properties in this case)
I am new in React-native and Redux, I am trying to do CRUD operation. How to update the state of store when we add data by api. I am calling Get Business Api in may action. and I store it into Store.
business action:
export const getBusinessByAliases = (aliases) => {
return (dispatch) => {
dispatch(getBusinessByAliasesData(aliases))
//API CALL
getBusiness(aliases)
.then(response=>{
//HERE I GET WHOLE BUSINESS DATA
dispatch(getBusinessByAliasesSuccess(response.data.business))
})
.catch(err => {
console.log("err",err)
dispatch(getBusinessByAliasesFailure(err))
})
}
}
business data is:
business:[
"id": "17bfdde3-bc04-4a9c-87e7-7530ded1b929",
"inserted_at": "2019-07-10T09:47:41",
"name": "Business2",
"employees": [
{
"nickname": "xyz",
"settings": null,
"user": {
"email": null,
"first_name": null,
"id": "582f5d07-146e-4a81-a6c0-7dd5208b43b2",
"image_id": null,
"inserted_at": "2019-07-02T13:41:06",
"last_name": null,
"phone": "+911234567890"
}
}
],
"services": [
{
"id": "34bd8c80-41e1-459a-bc09-d88a6894bd42",
"name": "Gsgsha",
"settings": {
"color": "#039BE5",
"duration": 4,
"price": 6
}
}
],
]
Now i am adding customer by calling create employee api in another action
employee action :
export const createNewEmployee = (businessName,data) => {
//console.log("whole data", data)
return (dispatch) => {
dispatch(createEmployee(businessName,data))
// API CALL
createEmployees(businessName,data)
.then(response=>{
//IN RESPONSE I GET SUCCESS MESSAGE WITH TRANSACTION ID
dispatch(createEmployeeSuccess(response.data.transaction_id))
})
.catch(err => {
console.log("errEmp",err)
dispatch(createEmployeeFailure(err))
})
}
}
Now, How do I update by the business state which contains all data with my new added employee entry?
I think the best solution is that your endpoint should response the new business object with all employes or only the new list of employes.
Keep in mind that your server can be modified by a lot of app clients, if you need to keep the information updated, you need to implement a Firebase protocol to update your store every time.
How to create an external API on Loopback?
I want to get the external API data and use it on my loopback application, and also pass the input from my loopback to external API and return result or response.
Loopback has the concept of non-database connectors, including a REST connector. From the docs:
LoopBack supports a number of connectors to backend systems beyond
databases.
These types of connectors often implement specific methods depending
on the underlying system. For example, the REST connector delegates
calls to REST APIs while the Push connector integrates with iOS and
Android push notification services.
If you post details on the API call(s) you want to call then I can add some more specific code samples for you. In the mean time, this is also from the documentation:
datasources.json
MyModel": {
"name": "MyModel",
"connector": "rest",
"debug": false,
"options": {
"headers": {
"accept": "application/json",
"content-type": "application/json"
},
"strictSSL": false
},
"operations": [
{
"template": {
"method": "GET",
"url": "http://maps.googleapis.com/maps/api/geocode/{format=json}",
"query": {
"address": "{street},{city},{zipcode}",
"sensor": "{sensor=false}"
},
"options": {
"strictSSL": true,
"useQuerystring": true
},
"responsePath": "$.results[0].geometry.location"
},
"functions": {
"geocode": ["street", "city", "zipcode"]
}
}
]
}
You could then call this api from code with:
app.dataSources.MyModel.geocode('107 S B St', 'San Mateo', '94401', processResponse);
You gonna need https module for calling external module inside loopback.
Suppose you want to use the external API with any model script file. Let the model name be Customer
Inside your loopback folder. Type this command and install https module.
$npm install https --save
common/models/customer.js
var https = require('https');
Customer.externalApiProcessing = function(number, callback){
var data = "https://rest.xyz.com/api/1";
https.get(
data,
function(res) {
res.on('data', function(data) {
// all done! handle the data as you need to
/*
DO SOME PROCESSING ON THE `DATA` HERE
*/
enter code here
//Finally return the data. the return type should be an object.
callback(null, data);
});
}
).on('error', function(err) {
console.log("Error getting data from the server.");
// handle errors somewhow
callback(err, null);
});
}
//Now registering the method
Customer.remoteMethod(
'extenalApiProcessing',
{
accepts: {arg: 'number', type: 'string', required:true},
returns: {arg: 'myResponse', type: 'object'},
description: "A test for processing on external Api and then sending back the response to /externalApiProcessing route"
}
)
common/models/customer.json
....
....
//Now add this line in the ACL property.
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW",
"property": "extenalApiProcessing"
}
]
Now explore the api at /api/modelName/extenalApiProcessing
By default its a post method.
For more info. Loopback Remote Methods
I'm attempting to customize the oauth2orize all-grants example for my use. I can run the all-grants as-is and everything works (as you would expect), but when I run my customized version, I always end up with this error:
Error: Unable to issue redirect for OAuth 2.0 transaction
at Object.response [as handle] (C:\Dev\Expy\api\node_modules\oauth2orize\lib\grant\code.js:122:41)
I've been digging into this a bit and it seems there is a property of the txn variable within that function that should be named redirectURI and should be populated with the redirect_uri from the query string of the initial request to the /dialog/authorize page. For some reason this doesn't happen on my example app. Is this caused by an express version difference? That is the biggest difference that I see between the example code and my customizations. The all-grants uses express 2.* and my app will use express 4.*.
If it isn't an express version issue, where should I start looking in my code for the issue?
For reference, this is what I see in my app for the txn object:
txn: {
"transactionID": "evlUd2q4",
"client": { ... },
"req": {
"type": "code",
"clientID": "5C3B4438-433F-11E5-A532-74653C701F13"
},
"user": { ... },
"res": {
"allow": true
}
}
and this is what I see in that same object with the example (note the presence of the redirectURI in req and in the txn itself):
txn: {
"transactionID": "EEcYp3Uj",
"client": { ... },
"redirectURI": "http://localhost:3000/api/userinfo",
"req": {
"type": "code",
"clientID": "abc123",
"redirectURI": "http://localhost:3000/api/userinfo"
},
"user": { ... },
"res": {
"allow": true
}
}