How to know when the feathers-client is connected to a service - feathersjs

I'm trying to test an event filter however there's a timing issue that I'm not sure how to resolve. Other than wrapping the REST request in a setTimeout, how could I get this working?
const app = require('../../src/app');
const feathers = require('feathers/client')
const socketio = require('feathers-socketio/client');
const hooks = require('feathers-hooks');
const io = require('socket.io-client');
const rp = require('request-promise');
const service = app.service('users');
let server = null;
describe('\'users\' service', () => {
beforeEach((done) => {
server = app.listen('3030');
server.once('listening', done);
});
afterEach((done) => {
server.close(done);
});
it('returns stuff #test', (done) => {
const socket = io('http://localhost:3030');
const app = feathers()
.configure(hooks())
.configure(socketio(socket));
const messageService = app.service('users');
messageService.on('created', message => {
console.log('Created a message', message);
done();
});
socket.on('connection', () => {
//
// The messageService is not connected yet
// so messages.filters.js will not fire
//
// Giving it a chance to connect with setTimeout does work...
// setTimeout(() => {
rp({
method: 'POST',
url: 'http://localhost:3030/users',
body: {
test: 'Message from REST'
},
json: true
});
// }, 500);
});
});
});
I have tried replacing the socket.on with these as well:
messageService.on('connection'
service.on('connection' (based on Node.js EventEmitter)
and so on...
Edit
I have since found service.on('newListener' works however it is being triggered many times. I need to track down the single connection:
const messageService = app.service('users');
messageService.on('created', message => {
console.log('Created a message', message);
done();
});

It's simply service.on('newListener'.
https://nodejs.org/api/events.html#events_event_newlistener
Listeners registered for the 'newListener' event will be passed the event name and a reference to the listener being added.
However, when I implemented this I found that it is listening to 5 different events. So, you need to filter those down:
service.on('newListener', (event, listener) => {
if (event === 'created') {
rp({
method: 'POST',
url: 'http://localhost:3030/users',
body: {
test: 'Message from REST'
},
json: true
});
}
});

Related

trying to deploy a todolist in blockchain, when adding a new task, I get invalid address error

I am making a simple block chain using the ETH blockchain technology.
I am making a simple todolist, following this tutorial.
My todolist works fine and I can see the tasks in there, how ever when I try to add a new task I get this following error:
Uncaught (in promise) Error: invalid address
v http://localhost:3000/js/web3.min.js:2
l http://localhost:3000/js/web3.min.js:2
formatInput http://localhost:3000/js/web3.min.js:2
formatInput http://localhost:3000/js/web3.min.js:2
toPayload http://localhost:3000/js/web3.min.js:2
e http://localhost:3000/js/web3.min.js:2
sendTransaction http://localhost:3000/js/web3.min.js:2
execute http://localhost:3000/js/web3.min.js:2
synchronizeFunction http://localhost:3000/vendor/truffle-contract/dist/truffle-contract.js:206
synchronizeFunction http://localhost:3000/vendor/truffle-contract/dist/truffle-contract.js:157
promise callback*synchronizeFunction/< http://localhost:3000/vendor/truffle-contract/dist/truffle-contract.js:156
createTask http://localhost:3000/js/app.js:124
onsubmit http://localhost:3000/:1
web3.min.js:2:4288
createTask http://localhost:3000/js/app.js:125
AsyncFunctionThrow self-hosted:696
(Async: async)
onsubmit http://localhost:3000/:1
​
this is my app.js
App = {
loading: false,
contracts: {},
load: async () => {
await App.loadWeb3();
await App.loadAccount();
await App.loadContract();
await App.render();
},
// https://medium.com/metamask/https-medium-com-metamask-breaking-change-injecting-web3-7722797916a8
loadWeb3: async () => {
if (typeof web3 !== "undefined") {
App.web3Provider = web3.currentProvider;
web3 = new Web3(web3.currentProvider);
} else {
window.alert("Please connect to Metamask.");
}
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum);
try {
// Request account access if needed
await ethereum.enable();
// Acccounts now exposed
web3.eth.sendTransaction({
/* ... */
});
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = web3.currentProvider;
window.web3 = new Web3(web3.currentProvider);
// Acccounts always exposed
web3.eth.sendTransaction({
/* ... */
});
}
// Non-dapp browsers...
else {
console.log(
"Non-Ethereum browser detected. You should consider trying MetaMask!"
);
}
},
loadAccount: async () => {
// Set the current blockchain account
App.account = web3.eth.accounts[0];
console.log(App.account);
},
loadContract: async () => {
// Create a JavaScript version of the smart contract
const todoList = await $.getJSON("TodoList.json");
App.contracts.TodoList = TruffleContract(todoList);
App.contracts.TodoList.setProvider(App.web3Provider);
// Hydrate the smart contract with values from the blockchain
App.todoList = await App.contracts.TodoList.deployed();
// console.log(todoList);
},
render: async () => {
// Prevent double render
if (App.loading) {
return;
}
// Update app loading state
App.setLoading(true);
// Render Account
$("#account").html(App.account);
// Render Tasks
await App.renderTasks();
// Update loading state
App.setLoading(false);
},
renderTasks: async () => {
// Load the total task count from the blockchain
const taskCount = await App.todoList.taskCount();
const $taskTemplate = $(".taskTemplate");
// Render out each task with a new task template
for (var i = 1; i <= taskCount; i++) {
// Fetch the task data from the blockchain
const task = await App.todoList.tasks(i);
const taskId = task[0].toNumber();
const taskContent = task[1];
const taskCompleted = task[2];
// Create the html for the task
const $newTaskTemplate = $taskTemplate.clone();
$newTaskTemplate.find(".content").html(taskContent);
$newTaskTemplate
.find("input")
.prop("name", taskId)
.prop("checked", taskCompleted)
.on("click", App.toggleCompleted);
// Put the task in the correct list
if (taskCompleted) {
$("#completedTaskList").append($newTaskTemplate);
} else {
$("#taskList").append($newTaskTemplate);
}
// Show the task
$newTaskTemplate.show();
}
},
createTask: async () => {
App.setLoading(true);
const content = $("#newTask").val();
await App.todoList.createTask(content);
window.location.reload();
},
toggleCompleted: async (e) => {
App.setLoading(true);
const taskId = e.target.name;
await App.todoList.toggleCompleted(taskId);
window.location.reload();
},
setLoading: (boolean) => {
App.loading = boolean;
const loader = $("#loader");
const content = $("#content");
if (boolean) {
loader.show();
content.hide();
} else {
loader.hide();
content.show();
}
},
};
$(() => {
$(window).load(() => {
App.load();
});
});
in console it shows me the address to be: 0xc5cfa0a0345f74e26cecfd8ec3a5cfa3843955ac
I am using metamask and genache, and I tried to connect my smart contacrt to my memask wallet, so I know I am connected, but I am not sure why I am getting this error.
SHould I delete my metamask and do it again?
I tried to look for this solution here, but the solutions are mostly old and doesnt make scnese what I need to do.
Help would be really apperciated.
fixed it, in the app.js you have to replace this line:
App.account = web3.eth.accounts[0];
with the following line:
web3.eth.defaultAccount=web3.eth.accounts[0]

cannot connect client app with web3js to metamask

I am a dapp beginner. This is a demo app for a todolist
I am unable to get a connection to the blockchain in web3js. Websocket connection error
localhost is localhost:8545
using web3js CDN : https://cdn.jsdelivr.net/npm/web3#latest/dist/web3.min.js
this is my app.js
App = {
loading: false,
contracts: {},
load: async () => {
console.log('app loading ...')
console.log(web3);
await App.loadWeb3()
await App.loadAccount()
// await App.loadContract()
// await App.render()
},
// https://medium.com/metamask/https-medium-com-metamask-breaking-change-injecting-web3-7722797916a8
loadWeb3: async () => {
let web3 = new Web3('ws://localhost:8545');
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider
web3.setProvider('ws://localhost:8546');
web3.eth.getAccounts().then(console.log);
} else {
window.alert("Please connect to Metamask.")
}
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum)
try {
// Request account access if needed
await ethereum.enable()
// Acccounts now exposed
web3.eth.sendTransaction({/* ... */})
console.log('MetaMask is installed!');
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = web3.currentProvider
window.web3 = new Web3(web3.currentProvider)
// Acccounts always exposed
web3.eth.sendTransaction({/* ... */})
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!')
}
},
loadAccount: async () => {
// Set the current blockchain account
App.account = web3.eth.accounts[0]
console.log(App.account)
// web3 set up by loadWeb3, includes all accounts, loading first one via MetaMask
},
loadContract: async () => {
// Create a JavaScript version of the smart contract
const todoList = await $.getJSON('TodoList.json')
App.contracts.TodoList = TruffleContract(todoList)
App.contracts.TodoList.setProvider(App.web3Provider)
// Hydrate the smart contract with values from the blockchain
App.todoList = await App.contracts.TodoList.deployed()
},
render: async () => {
// Prevent double render
if (App.loading) {
return
}
// Update app loading state
App.setLoading(true)
// Render Account
$('#account').html(App.account)
// Render Tasks
await App.renderTasks()
// Update loading state
App.setLoading(false)
},
renderTasks: async () => {
// Load the total task count from the blockchain
const taskCount = await App.todoList.taskCount()
const $taskTemplate = $('.taskTemplate')
// Render out each task with a new task template
for (var i = 1; i <= taskCount; i++) {
// Fetch the task data from the blockchain
const task = await App.todoList.tasks(i)
const taskId = task[0].toNumber()
const taskContent = task[1]
const taskCompleted = task[2]
// Create the html for the task
const $newTaskTemplate = $taskTemplate.clone()
$newTaskTemplate.find('.content').html(taskContent)
$newTaskTemplate.find('input')
.prop('name', taskId)
.prop('checked', taskCompleted)
.on('click', App.toggleCompleted)
// Put the task in the correct list
if (taskCompleted) {
$('#completedTaskList').append($newTaskTemplate)
} else {
$('#taskList').append($newTaskTemplate)
}
// Show the task
$newTaskTemplate.show()
}
},
createTask: async () => {
App.setLoading(true)
const content = $('#newTask').val()
await App.todoList.createTask(content)
window.location.reload()
},
toggleCompleted: async (e) => {
App.setLoading(true)
const taskId = e.target.name
await App.todoList.toggleCompleted(taskId)
window.location.reload()
},
setLoading: (boolean) => {
App.loading = boolean
const loader = $('#loader')
const content = $('#content')
if (boolean) {
loader.show()
content.hide()
} else {
loader.hide()
content.show()
}
}
}
$(() => {
$(window).load(() => {
App.load()
})
})
I get this error in the console :
Again I am a total newbie, any help is appreciated.
Any source of info did not help
Hey after metamask update , it no longer injects web3 .
You can check the blog below , It has shown how to connect metamask with our project .
https://dapp-world.com/blogs/01/how-to-connect-metamask-with-dapp--1616927367052
Only thing is its web application , you can relate it with your project .
Hope it works !

cloud function for sending fcm notifications to a collection of tokens

I am trying to send a notification whenever a new update to my database takes place. I have the onUpdate side working but I am new to FCM and I am stuck at the sending the notification.
The structure of the devices collection is:
+devices/
+tokenId/
-tokenId:njurhvnlkdnvlksnvlñaksnvlkak
-userId:nkjnjfnwjfnwlknlkdwqkwdkqwdd
The function that I have right now that gets stuck with an empty value of token is:
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp();
const db = admin.firestore();
const settings = { timestampsInSnapshots: true };
db.settings(settings);
.....
exports.fcmSend = functions.firestore
.document(`chats/{chatId}`).onUpdate((change, context) => {
const messageArray = change.after.data().messages;
const message = messageArray[(messageArray.length-1)].content
if (!change.after.data()) {
return console.log('nothing new');
}
const payload = {
notification: {
title: "nuevo co-lab",
body: message,
}
};
return admin.database().ref(`/devices`)
.once('value')
.then(token => token.val())
.then(userFcmToken => {
console.log("Sending...", userFcmToken);
return admin.messaging().sendToDevice(userFcmToken, payload)
})
.then(res => {
console.log("Sent Successfully", res);
})
.catch(err => {
console.log("Error: ", err);
});
});
I am not able to get the token from the database. It is null or undefined. Can anyone help me with this second part of the function?
Thanks a lot in advance!
Thanks Frank for the tip!
I managed to solve the problem with this code in case anybody needs it:
const payload = {
notification: {
title: "nuevo mensaje de co-lab",
body: message,
}
};
// Get the list of device tokens.
const allTokens = await admin.firestore().collection('devices').get();
const tokens = [];
allTokens.forEach((tokenDoc) => {
tokens.push(tokenDoc.id);
});
if (tokens.length > 0) {
// Send notifications to all tokens.
return await admin.messaging().sendToDevice(tokens, payload);
}else {
return null;
}

How to fix Cannot set headers after they are sent to the client?

After reading up on this topic for the last 2.5 hours I cant determine how to fix my: Cannot set headers after they are sent to the client issue, but I think it has to do with the below code at the bottom especially the code is in bold.
Any help or assistance will be greatly appreciated.
app.post("/api/tracking/retrieve", (req, res) => {
res.setHeader('Content-Type', 'application/json');
// before all the iterations
const trackingCodes = ['EZ6000000006', 'EZ4000000004'];
const carrierCodes = ['UPS', 'UPS'];
trackingCodes.forEach((trackingCode) => {
carrierCodes.forEach((carrierCode) => {
const tracker = new api.Tracker({
tracking_code: trackingCode,
carrier: carrierCode
})
tracker.save().then(function (data) {
table = 'tracking_table';
col = ['user_id', 'tracking_number'];
val = [user_id, tracker.tracking_code];
**// !ISSUE: :: ::: :::: ::::: :::::: ::::::: //**
main.create(table, col, val, function (data) {
res.send(JSON.stringify({
id: "",
user_id: user_id,
tracking_number: data.tracking_code
})); // replace this for your res.json()
});
}
)
.catch(error => {
// handle errors
console.log('There has been an error with your submission.')
});
})
})
res.end()
});
As #kiran Mathew has answered, the res.json() are called again and again inside for loop which is why 'cannot set headers after response sent' occurs.
You could have a result array 'trackingNumbers' to store all tracking_number and later exiting from the loop, sent a single response.
app.post("/api/tracking/retrieve", (req, res) => {
const trackingCodes = ["EZ6000000006", "EZ4000000004"];
const carrierCodes = ["UPS", "UPS"];
const trackingNumbers = [];
trackingCodes.forEach(trackingCode => {
carrierCodes.forEach(carrierCode => {
const tracker = new api.Tracker({
tracking_code: trackingCode,
carrier: carrierCode
});
tracker
.save()
.then(function(data) {
table = "tracking_table";
col = ["user_id", "tracking_number"];
val = [user_id, tracker.tracking_code];
// !ISSUE: :: ::: :::: ::::: :::::: ::::::: //**
main.create(table, col, val, function(data) {
// res.json({
// id: "",
// user_id: user_id,
// tracking_number: data.tracking_code
// });
trackingNumbers.push(data.tracking_code);
});
})
.catch(error => {
// handle errors
console.log("There has been an error with your submission.");
});
res.json({
id: "",
user_id: user_id,
tracking_number: trackingNumbers
});
});
});
});
The issue with your code is you are calling res.json() in an iterative loop.
You should call that only once since
res.json() implements res.write(),res.setHeaders() and res.end() under the hood,
which means once res.end() is called it ends the request and cannot send anymore.
You are better off writing the responses using
res.setHeader('Content-Type', 'application/json'); // before all the iterations
res.send(JSON.stringify({key:"value"})); // replace this for your res.json()
res.end() // after iterations

What's the best way to mock a nested function?

consider a function
exports.projectNotifyLaunch = (admin, functions) => {
return functions.database.ref("/projects/{pid}").onCreate(snap => {
const { title } = snap.val();
const notification = {
title: `${title} just launched!`,
body: `We just heard about a new cryptocurrency project called ${title}`
};
return admin.messaging().sendToTopic("premium", { notification });
});
};
How should I mock deeply nested functions such as
functions.database.ref("/projects/{pid}").onCreate(snap => {});
or
admin.messaging().sendToTopic("premium", { notification });
in Jest? I want to fire off the snap=>{} callback and assert against the value of notification.
I was able to make this work
This works but it's quite verbose. I'm wondering if there is a better way, or a type of testing I'm not aware of with Jest.
describe("send notification to premium users on new project", () => {
// INPUTS
const snap = {
val: () => ({
title: "Test Title"
})
};
const functions = {
database: {
ref: () => ({
onCreate: callback => callback(snap)
})
}
};
// outputs
let topicStub = null;
let notificationStub = null;
const admin = {
messaging: () => ({
sendToTopic: (topic, notification) => {
topicStub = topic;
notificationStub = notification;
}
})
};
projectNotifyLaunch(admin, functions);
test("title is correct", () => {
expect(notificationStub.notification.title).toBe(
"Test Title just launched!"
);
});
test("topic is premium", () => {
expect(topicStub).toBe("premium");
});
});