Why does Parse LiveQuery not always trigger update/create/enter event? - parse-server

I am trying to achieve a board that enables real-time editing for cooperating users. I am running a parse server using Sashido, where I have LiveQueries enabled for among other things 'Sticky'. For the frontend I have the following code:
const query = new Parse.Query(Sticky);
... some query constraints
this.subscription = await query.subscribe();
this.subscription.on('open', () => {
console.log('SUBSCRIPTION: opened');
}
this.subscription.on('create', (sticky) => {
console.log('SUBSCRIPTION: created');
}
this.subscription.on('update', (sticky) => {
console.log('SUBSCRIPTION: updated');
}
this.subscription.on('enter', (sticky) => {
console.log('SUBSCRIPTION: entered');
}
this.stickies = await query.find();
When I open my application in two different browser tabs, I get the 'SUBSCRIPTION: opened'. When I edit or create Sticky instances, I expect to get the corresponding events and see changes in the Sashido database.
However, I always see the changes in the database, but half of the times when I create or edit Sticky instances, I do not get the update/create/enter events. Note: Sometimes they do get triggered, but I have not found a sequence of events that leads to them being triggered or not, it seems to happen at random.
Can someone see what I'm doing wrong?

Related

how to make cypress wait for a async search with multiple result complete without causing the test to fail

I have a typical search where the user types some text in an input, async work is done and a table with the results is properly updated.
I have tests that must wait for this search step and then assert business rules regarding the results, like if the table records are eligible for edit.
Every time I ran a complete test battery (something like 80 test files), one or two of the tests involving that search inevitably fail. But if immediately after that, I run the same test alone, the test passes. It's excruciating and makes e2e testing in CI/CD pointless for the project.
I've read the Cypress documentation about flaky tests and searched questions in StackOverflow and GitHub with only complete failure. It's a drama.
Here is one of the tests:
import { searchList } from '../helpers';
import { createFluxoIniciado, randomFluxoNome } from './common';
import { fluxoSelectors } from './selectors';
describe('fluxos finish', () => {
it('can manually finish a fluxo INICIADO', () => {
// feed data to be searched
const fluxoNome = randomFluxoNome();
createFluxoIniciado({ fluxoNome });
// search
searchList(fluxoNome);
// do something with search results
fluxoSelectors.fluxos.view().click();
fluxoSelectors.finish().click();
fluxoSelectors.confirm().click();
// serach again
searchList(fluxoNome);
cy.contains('FINALIZADO');
});
});
The code in searchList is where trouble emerge sometimes. It uses the callback strategy recommended here. The code attempts to cause retries if not all rows have the searched text.
export function searchList (text) {
cy.get('#searchText')
.scrollIntoView()
.type(text)
.blur();
cy.get('tbody tr').should($trs => {
$trs.each((i, $tr) => {
expect($tr).to.contain(text);
});
}, { timeout: 15000 });
}
Here is an example of a test failure inside a run all test execution:
The problem is obviously caused by the async fetch between .blur() and testing the rows.
You are correctly trying to use Cypress retry with .should(callback), but if the callback is complex or there are multiple steps it may not retry the element that is changing (the table rows).
Ideally you want to keep the cy.get(...).should(...) as simple as possible, and start by testing that the table loading has completed.
// wait for expected number of rows
cy.get('tbody tr', {timeout: 15000}).should('have.length', 5)
cy.get('tbody tr').each($tr => {
expect($tr).to.contain(text);
})
But you have a randomizer there, so maybe it's not possible to test explicitly the row count.
Another approach, test the whole table for text (.contains() checks child text also)
// wait for text to appear somewhere
cy.get('tbody tr', {timeout: 15000}).should('contain', text)
cy.get('tbody tr').each($tr => {
expect($tr).to.contain(text);
})
You can also add an intercept between start and end of api call
export function searchList (text) {
cy.intercept('search/api/endpoint').as('search')
cy.get('#searchText')
.scrollIntoView()
.type(text)
.blur();
cy.wait('#search') // wait for api response
cy.get('tbody tr', {timeout: 15000}).should('contain', text)
cy.get('tbody tr').each($tr => {
expect($tr).to.contain(text);
})
}
I just noticed you have the {timeout} option on .should(), but that's the wrong place,
see Timeouts
cy.get('input', { timeout: 10000 }).should('have.value', '10')
// timeout here will be passed down to the '.should()'
// and it will retry for up to 10 secs
This may be successful
cy.get('tbody tr', { timeout: 15000 })
.should($trs => {
$trs.each((i, $tr) => {
expect($tr).to.contain(text);
});
})

ExpressJS - Small curiosity

My thing is a small project.
In main what it does is that the "server" will get a call from the link directly what will run some functions that will update the database and the data that has to be shown.
I will show what I mean:
function updateData(){
connection.query(`SELECT * FROM muzica WHERE melodie = "${updateList()}"`, function (error, rezultat, fields) {
if (error) {console.log('err la selectare')};
//express output
let data = {
melodie: rezultat[0].melodie,
likes: rezultat[0].likes
}
console.log(data.likes);
app.get('/like', (req,res) =>{
res.json(`${data.likes}`);
});
}
setInterval(()=>{
updateData();
}, 20000)
Uhh, how to explain it, I'm so bad at this...
So, in main, I'm new to back-end work, everything that I did was based on their Documentation as I learn way faster by my needs than some guides and so on.
So, when I or someone does my http://website/like it should show just data.likes, cause that is all that I need, don't count data.melodie (i will clean that later on) after I finish all the code.
Anyway, whenever I do website/like data.likes is not updating to the new database data.likes.
For example, data.likes before were 5, in a few minutes it can be 2 but whenever I call website/like show "5" than its new value 2.
Don't be hash on me, I'm new and I want to learn as much as I can, but I can't understand the above case, by my logic it should ALWAYS show what its in database when it refreshes each 10 seconds(I run this in localhost so I will not stress any online server).
But if there is any better way to check for databases update than "setInterval" please notice me.
It's hard to learn alone without a mentor or someone else to talk about this domain.
Thank you for your time!
Kind regards,
Pulsy
You have things a bit inside out. A request handler such as app.get('/like', ...) goes at the top level and you only ever call it once. What that statement does is register an event handler for any incoming requests with the /like path. When the server receives an incoming request for /like, it will then call the function for this route handler.
You then put inside that route handler the code that you want to run to generate the response and send the response back to the client.
app.get('/like', (req, res) => {
connection.query(`SELECT * FROM muzica WHERE melodie = "${updateList()}"`, function (error, rezultat, fields) {
if (error) {
console.log(error);
res.sendStatus(500);
} else {
//express output
let data = {
melodie: rezultat[0].melodie,
likes: rezultat[0].likes
}
res.json(data);
}
});
});
The endpoints need to be outside of any functions in express.
For example, if you look at the express "hello world" example here, you will see that they have a basic app that only has a single GET endpoint defined which is "/" so you would access it by running "localhost/" or "127.0.0.1/".
In your case, you want your endpoint to be "/like", so you must define something like:
const express = require('express')
const app = express()
const port = 3000
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
app.get('/like', (req, res) => {
// do database stuff and assign data variable
// res.json(data);
}

How to solve duplicate page in ion-infinite-scroll ionic 4

I am using ionic 4 and I am doing pagination using ion-infinite-scroll. My problem is I always get the duplicate page problem. Can I know how to solve this duplicate problem? Here is my code in home.page.ts:
doInfinite(event) {
this.userService.getData().then(res => {
event.target.complete();
});
}
loadData(event) {
console.log('Load more data');
this.userService.getData().then(res => {
event.target.complete();
});
}
Here is home.html
<ion-infinite-scroll (ionInfinite)="loadData($event)">
<ion-infinite-scroll-content
loadingSpinner="bubbles"
loadingText="loading ...">
</ion-infinite-scroll-content>
</ion-infinite-scroll>
It depends what your userService.getData() looks like.
It doesn't look like you are telling it to start at an offset.
Each time you pull data down, you should assign that list data to some local on-page variable, let's say dataList.
Then use this.dataList.length as the starting index for your next data request.
So some pseudo-code for how this might work would be:
let dataFeed = [];
let startAtRecord = 0;
constructor() {
this.userService.getData(startAtRecord).then(res => {
this.dataFeed = res;
this.startAtRecord = this.dataService.length;
});
}
loadData(event) {
// ask for a batch of records, starting at `startAtRecord`
this.userService.getData(startAtRecord).then(res => {
// add the new res data to the existing dataFeed
this.dataFeed = [...this.dataFeed, ...res];
// keep track of the number of records loaded
this.startAtRecord = this.dataService.length;
event.target.complete();
});
}
Do you see what I'm saying? The data service has to load the next page of data so you don't get the same one back, so it needs to track where its starting the list from.

How to refresh v-data-table after REST API Patch call?

I have a data table in Vuetify that is populated via a REST get request, using a function "getData" that is called when the app is mounted. The <td>'s in the table have buttons that the user can hit to "lock" the period (the row/column intersection).
When they hit the button, they get a popup confirmation dialog. When they hit "OK", there is a save method called to write the current date back to the db via a REST PATCH request (see below).
My problem is, the grid is not updating with the results of the patch request. I have to manually refresh the page to see the result. What is the common pattern here? Should i pull down the data again via getData to refresh the table? Should i update the array that the data-table sits on directly?
getData method:
getData() {
var self = this;
return axios
.get("http://127.0.0.1:5000/api/estimatefinal/periods?dataset=capital")
.then(function(response) {
self.periods = response.data;
})
.catch(function(error) {
alert(error);
});
},
Save method:
save(item) {
var self = this;
axios
.patch("http://localhost:5000/api/estimatefinal/period/" + self.id, {
date: moment(self.selected_date, "YYYY-MM-DD").format(
"YYYY-MM-DDTH:m:s"
)
})
.then(function() {
this.getData(); // ????
})
.catch(function(error) {
alert(error)
});
this.getData(); // ????
this.close();
}
If your PATCH changes only one row in DB, means has visually effect on only one row on your v-data-table, then you can change the data locally when you get "success" response from back-end.
If, in other hand, your PATCH changes many other things in DB (also in v-data-table) your best option is probably to getData() after you get PATCH response.
Point is to keep that same "picture" of values in DB and on screen v-data-table.

How to use store.filter / store.find with Ember-Data to implement infinite scrolling?

This was originally posted on discuss.emberjs.com. See:
http://discuss.emberjs.com/t/what-is-the-proper-use-of-store-filter-store-find-for-infinite-scrolling/3798/2
but that site seems to get worse and worse as far as quality of content these days so I'm hoping StackOverflow can rescue me.
Intent: Build a page in ember with ember-data implementing infinite scrolling.
Background Knowledge: Based on the emberjs.com api docs on ember-data, specifically the store.filter and store.find methods ( see: http://emberjs.com/api/data/classes/DS.Store.html#method_filter ) I should be able to set the model hook of a route to the promise of a store filter operation. The response of the promise should be a filtered record array which is a an array of items from the store filtered by a filter function which is suppose to be constantly updated whenever new items are pushed into the store. By combining this with the store.find method which will push items into the store, the filteredRecordArray should automatically update with the new items thus updating the model and resulting in new items showing on the page.
For instance, assume we have a Questions Route, Controller and a model of type Question.
App.QuestionsRoute = Ember.Route.extend({
model: function (urlParams) {
return this.get('store').filter('question', function (q) {
return true;
});
}
});
Then we have a controller with some method that will call store.find, this could be triggered by some event/action whether it be detecting scroll events or the user explicitly clicking to load more, regardless this method would be called to load more questions.
Example:
App.QuestionsController = Ember.ArrayController.extend({
...
loadMore: function (offset) {
return this.get('store').find('question', { skip: currentOffset});
}
...
});
And the template to render the items:
...
{{#each question in controller}}
{{question.title}}
{{/each}}
...
Notice, that with this method we do NOT have to add a function to the store.find promise which explicitly calls this.get('model').pushObjects(questions); In fact, trying to do that once you have already returned a filter record array to the model does not work. Either we manage the content of the model manually, or we let ember-data do the work and I would very much like to let Ember-data do the work.
This is is a very clean API; however, it does not seem to work they way I've written it. Based on the documentation I cannot see anything wrong.
Using the Ember-Inspector tool from chrome I can see that the new questions from the second find call are loaded into the store under the 'question' type but the page does not refresh until I change routes and come back. It seems like the is simply a problem with observers, which made me think that this would be a bug in Ember-Data, but I didn't want to jump to conclusions like that until I asked to see if I'm using Ember-Data as intended.
If someone doesn't know exactly what is wrong but knows how to use store.push/pushMany to recreate this scenario in a jsbin that would also help too. I'm just not familiar with how to use the lower level methods on the store.
Help is much appreciated.
I just made this pattern work for myself, but in the "traditional" way, i.e. without using store.filter().
I managed the "loadMore" part in the router itself :
actions: {
loadMore: function () {
var model = this.controller.get('model'), route = this;
if (!this.get('loading')) {
this.set('loading', true);
this.store.find('question', {offset: model.get('length')}).then(function (records) {
model.addObjects(records);
route.set('loading', false);
});
}
}
}
Since you already tried the traditional way (from what I see in your post on discuss), it seems that the key part is to use addObjects() instead of pushObjects() as you did.
For the records, here is the relevant part of my view to trigger the loadMore action:
didInsertElement: function() {
var controller = this.get('controller');
$(window).on('scroll', function() {
if ($(window).scrollTop() > $(document).height() - ($(window).height()*2)) {
controller.send('loadMore');
}
});
},
willDestroyElement: function() {
$(window).off('scroll');
}
I am now looking to move the loading property to the controller so that I get a nice loader for the user.