Firebase function not running/logging - firebase

I have a firebase cloud function and for some reason it is not running or logging even with just console.log("hello world") inside. This is confusing me, I think the issue could be because of promises, but even so I think it should work with just a console.log().
I call the function like this:
const addJobFunction = firestore.functions().httpsCallable("addJob");
addJobFunction({companyName: comp[0].data.Name, jobTitle: this.state.jobTitle,
jobLink: this.state.jobLink, companyKey: this.state.company});
and the function looks like:
exports.addJob = functions.https.onCall(async (data, context) => {
console.log("hello world");
db.collection("jobs")
.add({
company: data.companyName,
title: data.jobTitle,
link: data.jobLink,
data: [],
});
});
The result in my logs whether the database add is there or not (I.e just a console.log) is this.

Turns out I needed event.preventDefault() in my form submit function.
Works like this:
submitJob = (event) => {
event.preventDefault();
console.log(this.state.company);
let comp = this.state.companies.filter((company) => {
return company.key === this.state.company;
});
const addJobFunction = firestore.functions().httpsCallable("addJob");
addJobFunction({
companyName: comp[0].data.Name,
jobTitle: this.state.jobTitle,
jobLink: this.state.jobLink,
companyKey: this.state.company,
});
this.setState({
jobTitle: '',
jobLink: '',
})
};

Related

Mongoose Schema Custom Async Validator throwing TypeError - callback is not a function error

Trying to make a custom async schema validator in mongoose, to check that "tags" for a course being created contains at least one item. (Using SetTimeout() to simulate async). The part of the Schema for tags is :
tags: {
type: Array,
validate: {
isAsync: true,
validator: function (v, cb) {
setTimeout(() => {
//do some async work
const result = v && v.length > 0;
cb(result);
}, 3000);
},
message: "A course should have at least one tag!",
},
},
The code for creating a course is:
async function createCourse() {
const course = new Course({
name: "Node.js Course",
author: "Anon",
category: "web",
tags: [],
isPublished: true,
price: 13,
});
try {
const result = await course.save();
cl("createCourse result", result);
} catch (ex) {
cl("createCourse validate error", ex.message);
}
}
createCourse();
I have an empty array for tags and expected the caught error "A course should have at least one tag". Instead I am getting TypeError: cb is not a function for cb(result), the callback result? Even if I have an item in the tags array it still gives the callback error and in fact it displays the createCourse result BEFORE the schema async completes and then throws the error when it does complete! (If I dont use the async validator but just a plain validator then it works fine).
tags: {
type: Array,
validate: {
validator: function(v) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(v && v.length > 0);
}, 3000);
})
},
message: 'A course should have at least one tag.'
}
},
After trial and error, I came up with the solution below. No changes needed to createCourse(), just to the Schema tags section and added a delay function.
tags: {
type: Array,
validate: {
//isAsync: true,
validator: async function (v) {
await delay(3);
const result = v && v.length > 0;
return result;
},
message: "A Document should have at least one tag!",
},
},
And this calls a delay function, used to simulate a "real" async situation where this data may be being saved to a remote server.
const delay = (n) => {
return new Promise(function (resolve) {
setTimeout(resolve, n * 1000);
});
};

How can I imlement soft Assertion in my Cypress test

I am struggling with implementing soft assertions in my Cypress test.
I need to convert all the assertions to soft assertions. The problem I encounter is that I cannot locate the element in the jsonAssertion. For example cy.get('span[class="h4"]') is the element and I need to assert that it contains some text. How can this be done with jsonAssertion.softAssert()?
This is my test:
describe('Load Validation Test', function(){
const jsonAssertion = require("soft-assert")
it('Load Validation Test', function(){
let url = Cypress.config().baseUrl
cy.visit(url+'activityTaskManagement')
cy.get('span[class="h4"]').should('contain.text','Manage Activities')
cy.get('button[ng-click="vm.addActivityTask();"]').should('be.visible')
cy.get('button[ng-click="vm.addActivityTaskBulk();"]').should('be.visible')
cy.get('input[placeholder="Activity Name"]').should('be.visible')
cy.get('div table[class="table table-striped b-t b-light table-nowrap"]').should('be.visible')
})
})
For soft-assert, see How can i use soft assertion in Cypress
As custom commands,
const jsonAssertion = require("soft-assert")
Cypress.Commands.add('softAssert', (actual, expected, message) => {
jsonAssertion.softAssert(actual, expected, message)
if (jsonAssertion.jsonDiffArray.length) {
jsonAssertion.jsonDiffArray.forEach(diff => {
const log = Cypress.log({
name: 'Soft assertion error',
displayName: 'softAssert',
message: diff.error.message
})
})
}
});
Cypress.Commands.add('softAssertAll', () => jsonAssertion.softAssertAll())
In the test
cy.get('span[class="h4"]').then($el=> {
const actual = $el.text()
cy.softAssert(actual, 'Manage Activities')
})
There's also package that proxies expect
const { proxy, flush } = require("#alfonso-presa/soft-assert");
const { expect } = require("chai");
const softExpect = proxy(expect);
cy.get('span[class="h4"]')
.invoke('text')
.should(actual => {
softExpect(actual).to.eq('Manage Activities')
})
flush() // Now fail the test if above fails
})
If you just want to assert that the element span[class="h4"] has some text you can do:
cy.get('span[class="h4"]').should('include.text','Manage Activities')
Using soft assert you can do something like this:
const jsonAssertion = require('soft-assert')
describe('Load Validation Test', function () {
it('Load Validation Test', function () {
let url = Cypress.config().baseUrl
cy.visit(url + 'activityTaskManagement')
cy.get('span[class="h4"]')
.invoke(text)
.then((text) => {
jsonAssertion.softContains(
text,
'Manage Activities',
'Some custom message'
)
})
cy.get('button[ng-click="vm.addActivityTask();"]').should('be.visible')
cy.get('button[ng-click="vm.addActivityTaskBulk();"]').should('be.visible')
cy.get('input[placeholder="Activity Name"]').should('be.visible')
cy.get(
'div table[class="table table-striped b-t b-light table-nowrap"]'
).should('be.visible')
})
})

How do I return the result of a recursive fetch?

I have the first asynchronous function
fetch("https://api.priceapi.com/v2/jobs", {
body: body,
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
method: "POST"
}).then((response) => {
return response.json();
}).then((data) => {
return fetchRepeat(data.job_id)
})
And the second recursive asynchronous function.
function fetchRepeat(id){
fetch("https://api.priceapi.com/v2/jobs/"+ id +"/download.json?token=" + priceapisecret.secret)
.then((response) => {
return response.json()
}).then((data) =>{
if(data.status == "finished"){
var bookdata = {
title: data.results[0].content.name,
price: data.results[0].content.price
}
return bookdata;
}
else{
fetchRepeat(id)
}
})
}
I want to be able to access bookdata in the first async function. How do I do that?
In order to talk about a return your fetchRepeat needs to return the promise. It did not so returning undefined was the result. The last then also didn't return the value of the recursion and thus also resolved to undefined.
Here is a working version:
function fetchRepeat(id) {
// return the promise
return fetch(`https://api.priceapi.com/v2/jobs/${id}/download.json?token=${priceapisecret.secret}`)
.then(response => response.json())
.then(({ status, results: [{ content: { name: title, price } }] = [{ content: {} }] }) =>
(status === 'finished' ? { title, price } : fetchRepeat(id))); // return result of recursion
}
Now I let ESLint handle the formatting and since I use airbnb it prefers destructuring. The error in the last then was obvious since ELSint complained about consistent return. I urge you to use a linter and an IDE which enforces a coding style to reduce bugs in your code and make it easier for others to read.

Google Cloud Functions - warning Avoid nesting promises promise/no-nesting

Given the following function I get the warning:
warning Avoid nesting promises promise/no-nesting (line 6)
How should I re-estructure the function to fix the warning?
function FindNearbyJobs(uid, lat, lng){
return admin.database().ref(`users/${uid}/nearbyjobs`).remove().then(data => {
return new Promise((resolve, reject) => {
const geoQueryJobs = geoFireJobs.query({center: [lat, lng], radius: 3 });
geoQueryJobs.on("key_entered", (key, location, distance) => {
return Promise.all([admin.database().ref(`jobs/${key}/category`).once('value'), admin.database().ref(`users/${uid}/account/c`).once('value')]).then(r => {
const cP = r[0];
const cO = r[1];
if (cO.val().includes(cP.val())){
return admin.database().ref(`users/${uid}/nearbyjobs/${key}`).set({ d: distance });
}else{
return null;
}
});
});
geoQueryJobs.on("ready", () => {
resolve();
});
});
});
}
You have a promise then() call nested inside another promise's then(). This is considered to be poor style, and makes your code difficult to read. If you have a sequence of work to perform, it's better to chain your work one after another rather than nest one inside another. So, instead of nesting like this:
doSomeWork()
.then(results1 => {
return doMoreWork()
.then(results2 => {
return doFinalWork()
})
})
Sequence the work like this:
doSomeWork()
.then(results => {
return doMoreWork()
})
.then(results => {
return doFinalWork()
})
Searching that error message also yields this helpful discussion.

React Native - Can only update a mounted or mounting component

I try to subscribe from react-native to meteor by using ddp driver. During the componentDidMount, it gives me the exception
Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op.
This is my code
getInitialState: function() {
return {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => !_.isEqual(row1, row2),
}),
loaded: false,
};
},
componentDidMount: function() {
console.log('component mounted');
console.log(this.props['actor']);
ddp.initializeWithSubscribe(() => {
ddp.subscribe('select-all-meals-by-restaurant', [this.props['actor']['obj']['_id']]);
});
var ddpClient = ddp.connection;
var observer = ddpClient.observe('meals');
observer.added = () => this.updateRows(_.cloneDeep(_.values(ddpClient.collections.meals)));
observer.changed = () => this.updateRows(_.cloneDeep(_.values(ddpClient.collections.meals)));
observer.removed = () => this.updateRows(_.cloneDeep(_.values(ddpClient.collections.meals)));
},
/*------------------------------------------------------------------------------
* Util function for watching data
*-----------------------------------------------------------------------------*/
updateRows: function(rows) {
console.log('rows :' + rows);
this.setState({
dataSource: this.state.dataSource.cloneWithRows(rows),
loaded: true,
});
},
Can you suggest me a way to solve this ?
This explains what's happening and how to fix it:
https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
Basically you are receiving updates after you component has been unmounted. You need to use componentWillUnmount to unsubscribe from changes from Meteor.

Resources