How to update clients when database is edited outside of feathersjs app - javascript

I have two server-side applications editing the same database.
One server is a feathersjs app and another is a simple nodejs app. A frontend app connects to the feathersjs app via feathersjs client.
How, when the nodejs app edits the database, can I update clients connected to the feathersjs app? As currently any changes made outside the featherjs app aren't reflected on the feathersjs clients.
Can I trigger the patched event somehow and force the clients to pull down the updated data?

if you are using mongodb with WiredTiger storageEngine you can use the collection.watch() function and add a monitor in your feathers app something like this
//src/services/monitor.js
module.exports = function(app){
//get mongo client
const mongoClient = app.get('mongoClient');
//connect db
mongoClient.then(db => {
//collection to watch
const collection = db.collection('your_collection_name')
//watch collection
const changeStream = collection.watch({ fullDocument: 'updateLookup' });
//on some data changed
changeStream.on('change', data => {
console.log ( 'something is changed in your collection' , data )
//your service.emit
});
})
}
Then I added this simple monitor in the /src/services/index.js (maybe not the right way but it works)
//src/services/index.js
...
const monitor = require('./monitor.js');
module.exports = function (app) {
...
app.configure(monitor);
...
};
Data returned on every change on the collection
{ _id:
{ _data:
'825C7F03230000001C29295A100490DEF2C65812410FABF0DE46F9C89D7246645F696400645C1AC097B189CBD5D4A24D330004' },
operationType: 'replace',
clusterTime:
Timestamp { _bsontype: 'Timestamp', low_: 28, high_: 1551827747 },
fullDocument:
{ _id: 5c1ac097b189cbd5d4a24d33,
id: '12',
language: 'it-IT',
category: 'some data',
slug: '',
description: 'some data',
src:'',
color: 'card',
status: true,
home: true,
order_int: '3',
visual: 'card' },
ns: { db: 'mydb', coll: 'mycollection' },
documentKey: { _id: 5c1ac097b189cbd5d4a24d33 } }
More info here https://docs.mongodb.com/manual/reference/method/db.collection.watch/

As you pointed out, only changes made through the Feathers API will be reflected but on the server you can always emit the event you need via service.emit:
dbConnection.on('someDatabaseUpdate', data => {
app.service('messages').emit('patched', data);
app.service('messages').emit('updated', data);
});
Things to note here (also discussed in this issue):
There will be no user or any other information about the method call
data will not be run through any service hooks

Related

why messaging().sendtodevice is not working sometimes?

I'm using the following code to send a notification from one device to another using FCM. Everything works fine until before return admin.messaging().sendToDevice(...). The 'Token ID: ' log displays token ID of the receiver, but when I set the variable token_id to the sendToDevice function, the notification is not called, therefore the notification is not sent. Can someone tell me what's wrong?
var firebase = require("firebase-admin");
var serviceAccount = require("./julla-tutorial.json");
console.log("enter in then Firebase Api");
const firebaseToken = [
'e0T6j1AiRjaa7IXweJniJq:APA91bHNznSHSIey08s-C-c3gchci6wepvhP1QxQyYbmZ8LySI3wnu64iW7Q23GhA6VCdc4yodZoCFOgynfAb5C8O8VE81OcSv_LL-K3ET1IKGZ_6h35n-_q5EKFtfJWlzOqZr4IvpiB',
'dNWnSqyCQbufzv1JutNEWr:APA91bFcI9FDyRxHRBEcdw4791X0e-V0k1FjXcSstUA67l94hSojMRCd6LWr2b57azNEt3z_XLwLljMX4u2mc9cZDrAVm55Mw9CHGyue-09KofWnnHNR9XWBibc4T76xOV_DWX7T2RvW',
'cq65rtuaTCKGk5lHk7UabN:APA91bFR3kAArg6lhuBq7ktNuBk7Z9MXXk3PskqhYa8CgNaEl6MX4TQ5lo35d6XhnCQ4fEkCkyZ_j08evxE9Y4oVCRTEdqsrkccCVTE8Di47lfmDR3i1NdoL3re9oLw6F_uNsnvRoQcq'
]
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount)
})
const payload = {
notification: {
title: 'Demo 2345',
body: 'dfghj',
sound: 'default',
color: 'yellow',
android_channel_id: 'default',
channel_id: 'default'
},
data: { id: 'broadcast', channelId: 'default' }
}
const options = {
priority: 'high',
timeToLive: 60 * 60 * 24, // 1 day
};
console.log('------payload---',payload);
console.log('-----TOKEN_Array----',firebaseToken);
console.log('-------options-----',options);
firebase.messaging().sendToDevice(firebaseToken, payload, options).then(function (response) {
console.log('--------response',response);
}) .catch(function (error) {
console.log('-------rejet',reject);
});
It looks like you did not change the code from this tutorial:
https://medium.com/#jullainc/firebase-push-notifications-to-mobile-devices-using-nodejs-7d514e10dd4
you will need to change the 2nd line of code:
var serviceAccount = require("./julla-tutorial.json");
to actually point to your own firebase-push-admin.json file which holds your private keys registering your backend app with the firebase cloud messaging api. you can download this file from the firebase console as mentioned in the above article.
I recommend hiding this file from your git history by adding it to .gitignore so you dont accidentally push your private keys to a public repo.
I will link you another resource in addition to above link which helped me implement firebase push notifications in a nodeJS backend app.
https://izaanjahangir.medium.com/setting-schedule-push-notification-using-node-js-and-mongodb-95f73c00fc2e
https://github.com/izaanjahangir/schedule-push-notification-nodejs
Further I will also link you another repo where I am currently working on a fully functional firebase push notification implementation. Maybe it helps to actually see some example code.
https://gitlab.com/fiehra/plants-backend

Apollo Server and MySQL: How to get data direct from the database?

currently I'm writing a GraphQL API with ApolloServer/NodeJS. I have a MySQL Database. Because I'm new at this topic, I dont have any idea how can I get data directly from my database. My api sends article information to the client (this is the only purpose of my api). The client can search a specific article via ID (every article/products has a specific ID in the database. The id which the client is typing in should be compared with the id in the database. So if the id = 1 then the api should look after the article which has the id = 1 in the DATABASE and sends the requested information about THIS article to the client. (I know that this might be better with PHP, but I have some trouble with PHP). But I don't know how I can write this into my resolver.
Here is my resolver:
const models = require('../models');
module.exports = {
Query: {
article: (parent, {id}) => {
return models.articledata.find(
(c) => c.id == id
);
}
},
Node: {
__resolveType(node) {
if(node.toObject().name){
return 'Article';
}
}
}
}
Here is my models:
let articledata = [{
id: 1,
name: 'title',
description: 'Beispiel',
imgs: 'www.test.de/img',
btnLink: 'shoplink',
headline: 'Spicy das neue Spiel',
introduction: 'Beispiel',
definition: 'Beispiel',
tutorial: 'Beispiel',
specsText: 'Beispiel',
endText: 'Beispiel',
age: 12,
duration: 30,
players: 12,
category: 'Beispiel',
designer: 'Beispiel',
language: 'Deutsch',
releaseDate: 2020,
topic: 'Beispiel',
}];
module.exports = { articledata };
And here is my index.js:
const { ApolloServer } = require('apollo-server');
const schema = require('./schema');
const resolvers = require('./resolvers');
const server = new ApolloServer({
typeDefs: schema,
resolvers,
dataSources: () => {
articleAPI: new ArticleAPI()
}
})
//The listen-method launches a web server
server.listen().then(({ url }) => {
console.log('Server ready at ${url}');
});
Of course I searched on the web, but nothing seems to work for me. I'm very glad about your help!!
Did you check out the Apollo Server documentation? There is a lot of good information of connecting data sources to your server here.
There's a section about SQL connections here. The documentation notes that Apollo does not provide dedicated SQL connections right now, but shows you how to create one by working through an example.

Websockets in Vue/Vuex (how to receive emissions from server)

So until now I just socket.io-client to do communication to a WebSocket in my Vue component.
Now I am adding Vuex to the project and declared a Websocket like this
Vue.use(new VueSocketIO({
debug: true,
connection: 'http://192.168.0.38:5000',
}));
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
1) Should i have stuff like emitting some messages in the component themselves now or in the store?
2) Before I introduced the changes I could do something like this:
socket.on('connect', function () {
console.error('connected to webSocket');
socket.emit('my event', { data: 'I\'m connected!' });
});
socket.on('my response', function(data){
console.log('got response');
console.log(data.data);
});
When sending the "my event", the flask server would respond with "my response". Now I am trying the same thing from a component after the changes like this.
this.$socket.emit('my_event', { data: 'I\'m connected!' });
console.error('send to websocket ');
this.$options.sockets.my_event = (data) => {
console.error('received answer ');
console.error(data);
};
The my_event reaches my flask server however I don't get the response receiving to work. What am I doing wrong?
Also because I was asking about whether I should put this in the component or the store, I found stuff like this for the store:
SOCKET_MESSAGECHANNEL(state, message) {
state.socketMessage = message
}
The explanation was "So, for example, if your channel is called messageChannel, the corresponding Vuex mutation would be SOCKET_MESSAGECHANNEL" and it is from this site https://alligator.io/vuejs/vue-socketio/.
I think I don't really get what a channel is at this point. Is the my_response I emit from the flask server also a channel?
Thanks for your help in advance!
EDIT: So now I am trying to listen and emit to a websocket from my store. For this I tried the following: In main.js I have this part:
Vue.use(new VueSocketIO({
debug: true,
connection: SocketIO('http://192.168.0.38:5000'),
vuex: {
store,
actionPrefix: 'SOCKET_',
mutationPrefix: 'SOCKET_',
},
}));
Then in my store.js I have the following:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0,
title: 'title from vuex store',
isConnected: false,
},
mutations: {
increment(state) {
state.count += 1;
},
emitSth(state) {
this.sockets.emit('my_event', { data: 'I\'m connected!' });
console.log(state.count);
},
SOCKET_my_response(state) {
state.isConnected = true;
alert(state.isConnected);
},
SOCKET_connect(state) {
state.isConnected = true;
alert(state.isConnected);
},
},
});
And in my component I have this script:
export default {
name: 'ControlCenter',
data() {
return {
devices: [{ ip: 'yet unknown' }], // placeholder so line 12 does not throw error before actual device info fetched
thisDeviceIndex: 0,
currentLayoutIndex: 0,
layouts: [],
};
},
computed: mapState([
'title',
'count',
]),
components: {
DNDAssign,
FirstPage,
},
methods: {
// mapMutation helper let's us use mutation from store via this instead of this.$store
...mapMutations([
'increment',
'emitSth',
]),
incrementMutation() {
this.increment();
},
emitEvent() {
this.emitSth();
},
// some other stuff here
},
created() {
// inital fetching of layouts
console.log('fetching layouts from backend');
this.getAllLayouts();
console.log(this.$socket);
},
};
I also have a button for the triggering of the emit which is
<b-button
type="button"
variant="success"
v-on:click="emitEvent()"
>
emit event
</b-button>
The connected in the store gets triggered, however I get the following errors for the emitting:
"TypeError: Cannot read property 'emit' of undefined"
"Cannot read property 'emit' of undefined"
Also I am not sure about the naming in the mutations. If I have this mutationPrefix, shouldn't it be enough to just use connect instead of SOCKET_connect?
First of all, if you are using Vue-Socket.io version 3.0.5>, uninstall it and install version 3.0.5
npm uninstall vue-socket.io
npm install vue-socket.io#3.0.5
then lock the version in packege.json: "vue-socket.io": "3.0.5", latest update seems to breaks the library, read more here
Now to receive events from socket.io server, you can do:
this.sockets.subscribe("my response", (data) => {
console.log(data);
});
or if want put listener on component level, you need to add sockets object on the component export, for example:
export default {
...
sockets: {
"my response": function (data) {
console.log(data);
}
}
...
}
Since you are not using Vuex Integration on the VueSocketIO, you dont need to put additional function in store mutation. If you want to use Vuex integration on VueSocketIO, you need to add vuex object when declaring the VueSocketIO class.
Here's the basic example for main.js
// Set Vue to use Vuex
Vue.use(Vuex);
// Create store
const store = new Vuex.Store({
state: {
someData: null
},
getters: {},
actions: {
"SOCKET_my response"(context, data) {
// Received `my response`, do something with the data, in this case we are going to call mutation "setData"
context.commit("setData", data);
}
}
mutations: {
["setData"](state, data) {
state.someData = data; // Set it to state
}
}
});
// Set Vue to use VueSocketIO with Vuex integration
Vue.use(new VueSocketIO({
debug: true,
connection: 'http://192.168.0.38:5000',
vuex: {
store,
actionPrefix: "SOCKET_"
}
}));
new Vue({
router,
store
render: h => h(App)
}).$mount("#app");
If you need example on Vuex Integration, you can check my example app that uses Vue and Vue-Socket.io with Vuex integration.te

Is it possible to do push notifications with Gatbsy?

I am intrigued by Gatsby and my initial experiences with it have been very positive.
It's unclear how the static CDN-hosted model would dovetail with push notification functionality, and I would be appreciative of any guidance. Searching the web was to no avail.
I managed to add push notifications, following the Mozilla guide: https://developer.mozilla.org/es/docs/Web/API/ServiceWorkerRegistration/showNotification#Examples
In your gatsby-browser.js file, you can use onServiceWorkerUpdateFound to listen to updates and trigger a push notification, see code below
export const onServiceWorkerUpdateFound = () => {
const showNotification = () => {
Notification.requestPermission(result => {
if (result === 'granted') {
navigator.serviceWorker.ready.then(registration => {
registration.showNotification('Update', {
body: 'New content is available!',
icon: 'link-to-your-icon',
vibrate: [200, 100, 200, 100, 200, 100, 400],
tag: 'request',
actions: [ // you can customize these actions as you like
{
action: doSomething(), // you should define this
title: 'update'
},
{
action: doSomethingElse(), // you should define this
title: 'ignore'
}
]
})
})
}
})
}
showNotification()
}
Gatsby assumes a "decoupled" architecture. Gatsby wants to handle your frontend and the build process but how/where you store your data is up to you. So push notifications with Gatsby would be handled by a different service. You'd just need to add React code which handles the pushed data and presents it.

Refactoring mock response in ember cli mirage 0.2.x

I'm using ember cli mirage to write some acceptance tests for my Ember app. I succeeded to mock server response for login but I'm not happy how I did it. Ember cli mirage have shorthands for route handlers and I would like to use them but everything I try throws me an error(except this solution). Can someone help me to refactor this response?
this.post('/login', ({ users, resources })=> {
let user = users.first();
if(!Ember.isEmpty(resources.first())){
return {
data: {
type: 'user',
id: user.id,
attributes: user,
relationships: {
resources: {
data: [
{ id: resources.first().id, type: 'resource' }
]
}
}
},
};
} else {
return {
data: {
type: 'user',
id: user.id,
attributes: user
}
};
}
});
I have both user and resource model and factory defined, with relationships between them in user and resource model(it's many to many relationship). Here's how I create user in tests
test('User can login', function(assert){
let resources = server.createList('resource', 2),
user = server.create('user', {resources: resources});
loginUser(user.email);
andThen(()=>{
assert.ok(find('a:contains("Logout")'));
assert.equal('resource.content', currentPath());
});
});
If it's many-to-many, you should explicitly create a join record, as direct m2m relationship support does not yet exist.
// mirage/models/user.js
import { Model, hasMany } from 'ember-cli-mirage';
export default Model.extend({
userResources: hasMany()
});
// mirage/models/resource.js
import { Model, hasMany } from 'ember-cli-mirage';
export default Model.extend({
userResources: hasMany()
});
// mirage/models/user-resource.js
import { Model, belongsTo } from 'ember-cli-mirage';
export default Model.extend({
user: belongsTo(),
resource: belongsTo()
});
test('User can login', function(assert){
let user = server.create('user');
let resources = server.createList('resource', 2),
// create the join records
resources.forEach(resource => {
server.create('user-resource', { user, resource });
});
loginUser(user.email);
andThen(() => {
assert.ok(find('a:contains("Logout")'));
assert.equal('resource.content', currentPath());
});
});
If you need to mock an endpoint that exposes the m2m directly it will take a bit more work. But in general I find that if your Ember app exposes CRUD operations on the relationship, it's good to expose the join record, too. Makes things simpler.
That being said, Mirage will eventually support m2m relationships.

Categories

Resources