In our quasar application we have a few boot files, some of them can run into an exception which should block the application from starting normally, instead an error should be displayed.
Is there some best-practice for such a case? I didn't find much in the documentation.
My approach was the following, if an error occurs forward/redirect to an error page and display the error.
I need to somehow pass the error to the error-page:
passing the message as GET parameter would be a bad idea => XSS
I also cannot pass an error ID/Key because the error may come from a server with various messages
Storing the error in a vuex store does not work because the store is reset after reject() is called.
So I was thinking maybe there is a better way of doing handling errors in boot files?
Stop current booting and goto error-page...
Here is an reduced example which will end in the /error page but with the store.error empty, reject() will reboot the application reset the store.
export default ({ app, store, urlPath }) => {
// required to prevent invinite loop, the reject(url) will reboot application
if (urlPath.indexOf("error") !== -1) {
// TODO be more specific then indexOf("error")
return;
}
return new Promise((resolve, reject) => {
doSomeServerCommunication()
.then((result) => {
resolve();
})
.catch((e) => {
store.commit("appStore/updateError", e);
reject({ url: window.location.origin + "/app/error" });
return;
});
});
};
Related
I have a set of integration tests which run in Karma. Unfortunately, they call out to an external, production API endpoint. I do not want integration tests to call out and am exploring my options.
I am wondering if service workers are a viable solution. My assumption is that they do not work because https://github.com/w3c/ServiceWorker/issues/1188 makes it clear that cross-origin fetch is not supported and localhost is not the same origin as a production API endpoint.
For clarity, here is some code I am running:
try {
const { scope, installing, waiting, active } = await navigator.serviceWorker.register('./base/htdocs/test/imageMock.sw.js');
console.log('ServiceWorker registration successful with scope: ', scope, installing, waiting, active);
(installing || waiting || active).addEventListener('statechange', (e) => {
console.log('state', e.target.state);
});
} catch (error) {
console.error('ServiceWorker registration failed: ', error);
}
and the service worker
// imageMock.sw.js
if (typeof self.skipWaiting === 'function') {
console.log('self.skipWaiting() is supported.');
self.addEventListener('install', (e) => {
// See https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-global-scope-skipwaiting
e.waitUntil(self.skipWaiting());
});
} else {
console.log('self.skipWaiting() is not supported.');
}
if (self.clients && (typeof self.clients.claim === 'function')) {
console.log('self.clients.claim() is supported.');
self.addEventListener('activate', (e) => {
// See https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#clients-claim-method
e.waitUntil(self.clients.claim());
});
} else {
console.log('self.clients.claim() is not supported.');
}
self.addEventListener('fetch', (event) => {
console.log('fetching resource', event);
if (/\.jpg$/.test(event.request.url)) {
const response = new Response('<p>This is a response that comes from your service worker!</p>', {
headers: { 'Content-Type': 'text/html' },
});
event.respondWith(response);
}
});
and when this code is ran I see in the console
ServiceWorker registration successful with scope: http://localhost:9876/base/htdocs/test/ null null ServiceWorker
and then requests to https://<productionServer>.com/image.php are not intercepted by the fetch handler.
Is it correct that there is no way to intercept in this scenario?
You can use a service worker to intercept requests made by a browser as part of a test suite. As long a service worker is in control of a web page, it can intercept cross-origin requests and generate any response you'd like.
(The issue you link to about "foreign fetch" is something different; think of it as the remote production server deploying its own service worker. This was abandoned.)
"Stop mocking fetch" is a comprehensive article covering how to use the msw service worker library within the context of a test suite.
I can't say off the top of my head exactly why your current setup isn't working, but from past experience, I can say that the most important thing to remember when doing this is that you need to delay making any requests from a client test page until the page itself is being controlled by an active service worker. Otherwise, there's a race condition in which you might end up firing off a request that needs to trigger a fetch handler, but won't if the service worker isn't yet in control.
You can wait for this to happen with logic like:
const controlledPromise = new Promise((resolve) => {
// Resolve right away if this page is already controlled.
if (navigator.serviceWorker.controller) {
resolve();
} else {
navigator.serviceWorker.addEventListener('controllerchange', () => {
resolve();
});
}
});
await controlledPromise;
// At this point, the page will be controlled by a service worker.
// You can start making requests at this point.
Note that for this use case, await navigator.serviceWorker.ready will not give you the behavior you need, as there can be a gap in time between when the navigator.serviceWorker.ready promise resolves and when the newly activated service worker takes control of the current page. You don't want that gap of time to lead to flaky tests.
So I'm trying to make a database, couple of function snippets that read, write or create X.json files. The way I imagined it to be is a DB folder, then within that folder bunch of username folders, and there, a bunch of files, like account.json, level.json, and so on... So each folder would keep users data, now, this is the code I managed to write so far, and it works.
But the problem is, on the FS docs, it says that using fs.stat to check for the existence of the file before reading/writing to it is a bad idea. I don't understand why tho, as that seems like the only way to do it before I keep asking questions, I'd like to paste my code here:
socket.on('play', (data) => {
fs.stat(`db/${data.username}/account.json`, (error, result) => {
if(!error) {
fs.readFile(`db/${data.username}/account.json`, (error, result) => {
if(error) {
throw error;
} else {
const rawResult = JSON.parse(result);
if(data.password == rawResult.password) {
socket.emit('playResponse', {
success: true,
msg: 'Login Succesfull'
});
} else {
socket.emit('playResponse', {
success: false,
msg: 'Wrong Password!'
});
}
}
});
} else if(error.code == 'ENOENT') {
socket.emit('playResponse', {
success: false,
msg: 'Account not found'
});
}
});
});
I haven't written a general function that does this for me, because I figured that the code above is a mess right now. So, why is it a bad practice to check for the existence of the file (fs.stat) before writing/reading from them? I guess I could do something with the error I get from the readFile function and omit the fs.stat function, but whenever readFile function comes across a folder that doesn't exist, my server just crashes.
I'm not very experienced with Node, so the code above is probably absolute nonsense. That's why I'm here!
How can I make my server not crash if the readFile comes across a non-existent folder, but instead just emit the "Account not Found" through socket.io? If I put that emit code there, my server just crashes anyway.
I would just go with MongoDB or something, but I have a loooot of free time and doing stuff like this is very fun for me. > Is using a DB like mongo like more secure, or do people do it so they don't have to waste time writing their own DB?
Thanks for your help!
But the problem is, on the FS docs, it says that using fs.stat to check for the existence of the file before reading / writing to it is bad idea. I don't understand why tho
The reason is mentioned on the deprecated fs.exists docs:
Using fs.exists() to check for the existence of a file before calling fs.open(), fs.readFile() or fs.writeFile() is not recommended. Doing so introduces a race condition, since other processes may change the file's state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file does not exist.
How can I make my server not crash if the readFile comes across a non existent folder, but instead just emit the "Account not Found" through socket.io?
You don't handle the errors properly. As an example you throw an error in your .readFile callback but your code leaves the error unhandled, that will "crash" your application. You could either wrap your code with a try/catch block or use promises. Promises provide nice APIs to handle errors in your application. Node.js v10.0.0 introduces promise-wrapped APIs for fs module APIs.
const fs = require('fs');
const fsPromises = fs.promises;
fsPromises.readFile(`db/${data.username}/account.json`).then(error => {
// the file exists and readFile could read it successfully!
// you can throw an error and the next `catch` handle catches the error
}).catch(error => {
// there was an error
});
You can also use the APIs with try/catch and await:
try {
const content = await fsPromises.readFile(`db/${data.username}/account.json`);
// the file exists and readFile could read it successfully!
} catch(error) {
// handle the possible error
}
If using node v10.0.0 is not an option you can use a npm package that provides promised-wrapped fs APIs like fs-extra or draxt:
// using draxt
const $ = require('draxt');
const File = $.File;
const file = new File(`db/${data.username}/account.json`);
file.read('utf8').then(contents => {
// the file exists and readFile could read it successfully!
}).catch(error => {
// handle the possible error
});
The problem is that the file could be deleted betweent the time where you did got response the stat call and the readFile call.
The recommend way is to do call the readFile and the check for the error code in the callback of the readFile. For the callback based version this would look that way:
socket.on('play', (data) => {
fs.readFile(`db/${data.username}/account.json`, (error, result) => {
if (error) {
if (error.code === 'ENOENT') {
socket.emit('playResponse', {
success: false,
msg: 'Account not found'
})
} else {
// throw the error if you really want to exit the application for unknown errors
throw error
}
} else {
const rawResult = JSON.parse(result)
if (data.password === rawResult.password) {
socket.emit('playResponse', {
success: true,
msg: 'Login Succesfull'
})
} else {
socket.emit('playResponse', {
success: false,
msg: 'Wrong Password!'
})
}
}
})
})
In my react native app, the server is specified by the user. Although I put some control on the "url" field, sometimes the user put an url which simply does not exist.
Then, I got a TypeError:
Network request failed
How can I handle these case in my code?
Thank you
You should handle the error case in .then for fetch API.
For example:
fetch('...URL')
.then(
(response) => {
console.log('success',response)
},
(err) => {
console.log('error',err)
}
);
We're using the enterprise server side row model to fetch data from the server. We've implemented the IServerSideDatasource and, if the server errors, we call params.failCallback as recommended.
However, nothing happens on the grid. The loading spinner still is visible and there's no notification to the user about anything going wrong.
The 'onRowDataChanged' event fires, but it has no information about the status of the event.
Is there a recommended way to notify the user about the failure? Ideally I'd like to deal with this through ag-grid events rather than throw my own errors from the IServerSideDatasource or even the http client.
Is this possible?
I'm using a custom eventListener to catch failCallback calls and it works pretty well
In my main class:
onGridReady = params => {
this.gridApi = params.api;
this.gridApi.addEventListener('failCallback', this.onServerFailCallback);
this.gridApi.setServerSideDatasource(MyRemoteDataSource);
};
onServerFailCallback = params => {
console.error('onServerFailCallback', params);
}
In MyRemoteDatasource:
class MyRemoteDatasource{
getRows(params) {
fetchData(params).then(
response => {
params.successCallback(response.data);
},
error => {
params.failCallback();
params.parentNode.gridApi.dispatchEvent({
type: 'failCallback',
api: params.parentNode.gridApi,
columnApi: params.parentNode.columnApi,
error: error
});
});
}
}
output:
onServerFailCallback,
{type: "failCallback", api: GridApi, columnApi: ColumnApi, error: Error: Error inside fetchData() at stack traceā¦}
I'm trying to make an alert window saying there's an error, When trying to post a message offline. But the catch doesn't seem to ever work, Maybe it just works in other cases?
here's my code :
return (dispatch) => {
dispatch({
type: POST_TO_DB,
});
firebase.database().ref(locationInDB).push(object)
.then((data) => {
dispatch({
type: POST_TRADE_TO_DB_SUCCESS,
}); // success
})
.catch((err) => {
console.log("failed to post...")
dispatch({
type: POST_TRADE_TO_DB_FAILED,
}); // failed
});
};
Is there an alternative? Or am I doing something wrong?
When there is no network connection, the Firebase client will keep the pending write in memory until the network connection is restored, at which point it will complete the write.
The catch() clause is triggered if the write fails on the server, not when it can't complete.
Also see:
To detect if the client is connected to the Firebase backend, see Detect if Firebase connection is lost/regained
Firebase synchronisation of locally-modified data: handling errors & global status