Shopify Storage Redis Issue with Node React App - redis

I have added session storage in serve.js as follows :-
import SessionHandler from "./SessionHandler";
const sessionStorage = new SessionHandler();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SCOPES.split(","),
HOST_NAME: process.env.HOST.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.October21,
IS_EMBEDDED_APP: false,
// This should be replaced with your preferred storage strategy
//SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
sessionStorage.storeCallback,
sessionStorage.loadCallback,
sessionStorage.deleteCallback
),
});
My router get function is
router.get("(.*)", async (ctx) => {
const shop = ctx.query.shop;
let documentQuery = { shop: shop };
let data = await SessionStorage.findOne(documentQuery); //this finds the store in the session table
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
if (data == null) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
} else {
await handleRequest(ctx);
}
});
and than in the SessionHandler file added code as attached in file ,
but when I run install the app it goes to the storeCallback , loadcallback and deletecallback function multiple times
StoreCallback Function Code
Load and delete callback function code

sorry I have edited my answer as I think its incorrect . all I can say for now is to look at this example:https://github.com/Shopify/shopify-api-node/blob/main/docs/usage/customsessions.md
if you havent already..

Related

Infinite loop with a graphql and mongoose backend

I have a backend made in express and mongoose:
all my mutations and queries work perfectly except one mutation sends me an infinite loader
updateVehicleVerification: async (_, { id, updateVehicleVerification }) => {
const vehicleVeri = await VehicleVerification.findById(id);
if (!vehicleVeri) {
throw new Error(ErrorMessage + ' : Verification de Vehicule');
}
await VehicleVerification.findByIdAndUpdate(
id,
updateVehicleVerification
);
const veri = await VehicleVerification.findById(id);
return veri;
},
and the query I use here:
export const UPDATE_CONTROL_VEHICLE = gqlmutation updateVehicleVerification( $id: String! $updateVehicleVerification: VerificationVehicleInput ) { updateVehicleVerification( id: $id updateVehicleVerification: $updateVehicleVerification ) { honk { state image comment } mileage dateVerification stateVehicle { damaged good missing } } };
enter code here
I I solved the problem !
I did not manage the case where the data reached me by the request which keyed an infinite loop.

Apollo/graphql request result buffered in vuejs

In a Vue component controlling users subsciption to newsletters, I have the fellowing code:
async newSubscriber(event) {
// Validate email
//---------------
if (!this.isEmailValid(this.subscriber_email))
this.subscribeResult = "Email not valid";
else {
// If valid, check if email is not already recorded
//-------------------------------------------------
let alreadyRecorded = false;
let recordedEmails = await this.$apollo.query({ query: gql`query { newslettersEmails { email } }` });
console.log('length ' + recordedEmails.data.newslettersEmails.length);
console.log(recordedEmails.data.newslettersEmails);
for (let i = 0; !alreadyRecorded && i < recordedEmails.data.newslettersEmails.length; i++)
alreadyRecorded = this.subscriber_email === recordedEmails.data.newslettersEmails[i].email;
if (alreadyRecorded)
this.subscribeResult = "Email already recorded";
else {
// If not, record it and warn the user
//------------------------------------
this.$apollo.mutate({
mutation: gql`mutation ($subscriber_email: String!){
createNewslettersEmail(input: { data: { email: $subscriber_email } }) {
newslettersEmail {
email
}
}
}`,
variables: {
subscriber_email: this.subscriber_email,
}
})
.then((data) => { this.subscribeResult = "Email recorded"; })
.catch((error) => { this.subscribeResult = "Error recording the email: " + error.graphQLErrors[0].message; });
}
}
}
At the very first email subscription test, $apollo.query returns me the correct number of emails already recorded (let's say, 10) and record the new subscriber email. But if I try to record a second email without hard refreshing (F5) the browser, $apollo.query returns me the exact same result than the first time (10), EVEN IF the first test email has been correctly recorded by strapi (graphql palyground showns me the added email with the very same query!). Even if I add ten emails, apollo will always return me what it got during its first call (10 recorded emails), as if it uses a buffered result. Of course, that allows Vue to record several times the same email, which I obviously want to avoid!
Does it speaks to anyone ?
After a lot of Google digging (giving the desired results by simply changing in my requests, at the end, "buffering" by "caching" !), I understood that Apollo cache its queries by default (at least, in the configuration of the Vue project I received). To solve the problem I just added "fetchPolicy: 'network-only'" to the query I make:
let recordedEmails = await this.$apollo.query({
query: gql`query { newslettersEmails { email } }`,
});
became
let recordedEmails = await this.$apollo.query({
query: gql`query { newslettersEmails { email } }`,
fetchPolicy: 'network-only'
});
And problem solved ^^

Async data fetching not updating reactive data property

Ok, guys, I´m having a little issue today, all day long, trying to solve, the deal goes like this...
I´m fetching some data from firebase to render on the html template with asynchronous functions
I have a fetchList Method that is like this:
async mounted() {
let ret = await this.fetchJobRequireList()
console.log('fetchjoblist' , ret)
async fetchJobRequireList() {
// debugger
let services = JSON.parse(sessionStorage.getItem('required_services'))
services != null ? this.required_services = services : null
let docs_ = []
let result = []
if (!services) {
// this.required_services = []
// get required services per user id
let collections = this.$options.firebase.functions().httpsCallable('getRequiredServices')
let docs = await this.$options.firebase.firestore().collection('required_services').get()
// console.log('required services docs', docs)
let _ = this
for (let doc of docs.docs) {
result[doc.id] =
await collections({doc_id: doc.id}).then( async r => {
// debugger
let collections_ = r.data.cols
docs_ = []
_.required_services[doc.id] = []
for (let collection of collections_) {
let path = collection._referencePath.segments
// let documents =
let __ = _
await this.$options.firebase.firestore().collection(path[0])
.doc(path[1]).collection(path[2]).get()
.then(async documents => {
// console.log('__documents__', documents)
for (let doc_ of documents.docs) {
doc_ = await documents.docs[0].ref.get()
doc_ = {
id: doc_.id,
path: doc_.ref.path,
data: doc_.data()
}
// debugger
__.required_services[doc.id].push(doc_)
console.log("this?", this.required_services[doc.id], '__??', __.required_services)
docs_.push(doc_)
}
})
}
console.log('__docs__', docs_)
return docs_
}).catch(err => console.error(err))
// console.log('this.required_services', this.required_services)
}
}
// console.log('object entries', Object.entries(result))
// console.log('__this.required_services__', Object.entries(this.required_services))
// sessionStorage.setItem('required_services', JSON.stringify(this.required_services))
return result
}
The expected result would be for the data function properties to be update after the firebase response came, but no update is happening.
If anyone, have any clues, of what could be happening... some people told me that asynchrounous functions could cause problems... but there is no alternative for them, I guess...
This line
_.required_services[doc.id] = []
is not reactive. See the first point in the docs
So as pointed by #StephenThomas, there is some limitations in array change detection capabilities in reactive property usage.
So after loading the content from firebase, try to push it like this.joblist.push(doc) vue property will not react properly and make some confusion in the head of someone that doesn´t know about this limitation in detecting this kind of array mutation (https://v2.vuejs.org/v2/guide/list.html#Caveats)...
By using this line, now is possible to see the changes in property inside the Vue dev tools
_.joblist.splice(0,0, local_doc)
Thanks #SthephenThomas, for pointing this out!!

CKEditor 5 Image Upload Issues

I'm using ckeditor5 into my project. I have to support image upload so I have search and followed this stackoverflow article.
I have created an uploadAdapter which is:
class UploadAdapter {
constructor( loader, url, t ) {
this.loader = loader;
this.url = url;
this.t = t;
}
upload() {
return new Promise( ( resolve, reject ) => {
this._initRequest();
this._initListeners( resolve, reject );
this._sendRequest();
} );
}
abort() {
if ( this.xhr ) {
this.xhr.abort();
}
}
_initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
xhr.open( 'POST', this.url, true );
xhr.responseType = 'json';
}
_initListeners( resolve, reject ) {
const xhr = this.xhr;
const loader = this.loader;
const t = this.t;
const genericError = t( 'Cannot upload file:' ) + ` ${ loader.file.name }.`;
xhr.addEventListener( 'error', () => reject( genericError ) );
xhr.addEventListener( 'abort', () => reject() );
xhr.addEventListener( 'load', () => {
const response = xhr.response;
if ( !response || !response.uploaded ) {
return reject( response && response.error && response.error.message ? response.error.message : genericError );
}
resolve( {
default: response.url
} );
} );
if ( xhr.upload ) {
xhr.upload.addEventListener( 'progress', evt => {
if ( evt.lengthComputable ) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
} );
}
}
_sendRequest() {
const data = new FormData();
data.append( 'upload', this.loader.file );
this.xhr.send( data );
}
}
import Plugin from '#ckeditor/ckeditor5-core/src/plugin';
import FileRepository from '#ckeditor/ckeditor5-upload/src/filerepository';
export default class GappUploadAdapter extends Plugin {
static get requires() {
return [ FileRepository ];
}
static get pluginName() {
return 'GappUploadAdapter';
}
init() {
const url = this.editor.config.get( 'gapp.uploadUrl' );
if ( !url ) {
return;
}
this.editor.plugins.get( FileRepository ).createUploadAdapter = loader => new UploadAdapter( loader, url, this.editor.t );
}
}
Now this is explained. I have 2 issues.
Once uploaded ( my upload on server is working fine and returning a valid url in format {default: url}, why is my image content inserted as data-uri and not in url as for easy image demo here. I want my image to be url like.
I would like to listen for a kind of success upload image ( with image id retrieved from upload server call ) to insert some content in my page. How to proceed ?
Thanks for help.
PS: I'm building ckeditor with command 'npm run build' from git repo cloned from https://github.com/ckeditor/ckeditor5-build-classic
EDIT:
Thanks to accepted response, I saw that I was wrong in returned data. I was not returning any URL in my uploader front end which was causing editor image to stay in img-data way. Once valid URL was returned, it was parsed automatically and my editor image was containing a valid url.
If the data-uri is still used after successful upload I would assume that server response was not processed correctly and the received url could not be retrieved. I have tested adapter code you provided and it works fine (with CKFinder on server side). I would check how the upload server response looks and if it can be correctly parsed.
When using CKFinder you will see:
and a parsed JSON response:
You could check if response is processed correctly in your adapter in:
xhr.addEventListener( 'load', () => {
const response = xhr.response;
...
}
Listening to successful image upload may be tricky as there is no event directly related to it. Depending on what exactly you are trying to achieve you may try to extend you custom loader so when successful response is received (and resolve() called) you may execute some code. However, in this state the image element is still not updated (in model, view and DOM) with new URL and UploadAdapter lacks a direct access to editor instance so it may be hard to do anything complex.
Better way may be to listen to model changes, the similar way it is done in ImageUploadEditing plugin (see code here) checking the image uploadStatus attribute change:
editor.model.document.on( 'change', () => {
const changes = doc.differ.getChanges();
for ( const entry of changes ) {
const uploaded = entry.type === 'attribute' && entry.attributeNewValue === 'complete' && entry.attributeOldValue === 'uploading';
console.log( entry );
}
} );
If it changes from uploading to complete it means the images was successfully uploaded:
You may also take a look at another answer, which shows how to hook into FileRepository API to track entire upload process - https://github.com/ckeditor/ckeditor5-image/issues/243#issuecomment-442393578.

Whatsapp Web - how to access data now?

It used to be possible to access http://web.whatsapp.com/ with the Store object in JavaScript. A few hours ago, this stopped working. How does it update chat data now? It must save the data somewhere.
I'm using this to get the Store again:
setTimeout(function() {
// Returns promise that resolves to all installed modules
function getAllModules() {
return new Promise((resolve) => {
const id = _.uniqueId("fakeModule_");
window["webpackJsonp"](
[],
{
[id]: function(module, exports, __webpack_require__) {
resolve(__webpack_require__.c);
}
},
[id]
);
});
}
var modules = getAllModules()._value;
// Automatically locate modules
for (var key in modules) {
if (modules[key].exports) {
if (modules[key].exports.default) {
if (modules[key].exports.default.Wap) {
store_id = modules[key].id.replace(/"/g, '"');
}
}
}
}
}, 5000);
function _requireById(id) {
return webpackJsonp([], null, [id]);
}
// Module IDs
var store_id = 0;
var Store = {};
function init() {
Store = _requireById(store_id).default;
console.log("Store is ready" + Store);
}
setTimeout(function() {
init();
}, 7000);
Just copy&paste on the console and wait for the message "Store is ready".
Enjoy!
To explain Pablo's answer in detail, initially we load all the Webpack modules using code based on this How do I require() from the console using webpack?.
Essentially, the getAllModules() returns a promise with all the installed modules in Webpack. Each module can be required by ID using the _requireById(id) which uses the webpackJsonp(...) function that is exposed by Webpack.
Once the modules are loaded, we need to identify which id corresponds to the Store. We search for a module containing exports.default.Wap and assign it's id as the Store ID.
You can find more details on my github wiki here
A faster method:
I grab the source of the "app" and find the store object then
I save it in ZStore global variable. :D
!function(){for(var t of document.getElementsByTagName("script"))t.src.indexOf("/app.")>0&&fetch(t.src,{method:"get"}).then(function(t){return t.text().then(function(t){var e=t.indexOf('var a={};t["default"]')-89;window.ZStore=window.webpackJsonp([],null,JSON.stringify(t.substr(e,10))).default})})}();
window.ZStore will contain the object.
Non minified version:
(function() {
function getStore(url) {
fetch(url, {
"method": 'get'
}).then(function(response) {
return response.text().then(function(data) {
var offset = data.indexOf('var a={};t["default"]') - 89;
window.ZStore = window.webpackJsonp([], null, JSON.stringify(data.substr(offset, 10))).default
});
});
}
for (var e of document.getElementsByTagName("script")) {
if (e.src.indexOf("/app.") > 0) getStore(e.src);
}
})();