Firebase: Get notified just after the firebase-function trigger promise completed - firebase

In my app I pushed some object to my firebase-database and immediately after that (after the then-promise fully filled) I fetch the object (with the returned key) from the database (with the on-value method).
In addition, I make some changes on the pushed object using the firebase-functions.
How can I receive the object (in the app) just after the changes and not before? (like other ordinary backend services)

I hope this helps you, I have not tested this piece of code but it should help you in the right direction.
Also dont use this exact code in production, there is plenty room for improvement, this is just an example code.
exports.testFunction = functions.https.onRequest((req, res) => {
if (req && req.body) {
if (
req.body.hasOwnProperty('name') &&
req.body.hasOwnProperty('age')
) {
const person = {
name: req.body['name'],
age: req.body['age']
}
// Make some changes to the person object
person['hobby'] = 'Programmer';
// Add object to FireStore
admin
.firestore()
.collection('/persons')
.add(person)
.then((success) => {
// Return the added & changed person
res.status(201).send(JSON.stringify(person));
})
.catch((error) => {
// Error
console.error('Something broke', error)
res.status(500).send();
});
}
else {
// Error
res.status(500).send({err: 'Missing property'});
}
}
else {
// Error
res.status(500).send({err: 'Missing something'});
}
});

Related

Unable to use array's method "find" in Vue 3

I am trying to get current location of a user and then push it into array. Before I do so, I check whether a city with the same name is already there. In that case, I won't push it into the array. However, when I am trying to check it, it says: Uncaught (in promise) TypeError: Cannot read properties of null (reading 'find').
const found = ref(false);
const weatherResponse = ref([]);
function getLocation() {
console.log("SETTING LOCATION");
navigator.geolocation.getCurrentPosition((position) => {
console.log(`Lat: ${position.coords.latitude}, Lon: ${position.coords.longitude}`);
if (position.coords.latitude && position.coords.longitude) {
axios.get(`https://api.weatherapi.com/v1/current.json?key=${API_KEY}&q=${Math.round(position.coords.latitude)},${Math.round(position.coords.longitude)}&aqi=no`)
.then((response) => {
found.value = weatherResponse.value.find((item) => item.location.name == response.data.location.name);
if (response.data?.error?.code != 1006 && !found.value) {
weatherResponse.value.push(response.data);
this.$store.commit("addToList", response.data);
console.log(weatherResponse.value);
}
})
}
},
(error) => {
console.log(error.message);
}
)
}
I've already tried using fetch, axios to grab the API, but the "find()" method is still not working. Regarding "found" variable, I tried using it in ref as well as declaring it as "let found".
After trying and testing, I've finally managed to get everything to work. My issue was in (weirdly) main.js. Because it was set out like this: createApp(App).use(cors, store).mount('#app') it, I guess, caused VueX.store not to load in properly because mounted hook was called and it was throwing all sorts of mistakes. Putting it like const app = createApp(App); app.use(store); app.use(cors); app.mount("#app"); actually made it work.

Firebase trigger on delete

Im trying to make a function where I can identify who delete, the problem is Im not getting any answer from the firebase server, someone could help me ? Thanks
exports.deleteFunction = functions.database.ref('/clientes')
.onDelete((context) => {
// Grab the current value of what was written to the Realtime Database.
console.log("delete");
console.log(context);
});
Here is the function who is deleting
confirm = (e) => {
if (id_deleta) {
firebaseDatabase.ref('/clientes/categorias/').child(id_deleta)
.remove();
notification('success', 'Excluido com sucesso');
this.callCategoria();
} else {
notification('error', 'Ocorreu um erro, tente mais tarde');
}
}
Code correction
The onDelete event handler is defined as
function(non-null functions.database.DataSnapshot, optional non-null functions.EventContext)
So in your code above, .onDelete((context) => { should be .onDelete((snapshot, context) => {.
Getting the deleted ID
Next, if you are trying to get the value of id_deleta from the onDelete event, you can use var id_deleta = snapshot.key.
exports.deleteFunction = functions.database.ref('/clientes')
.onDelete((snapshot, context) => {
var id_deleta = snapshot.key;
console.log("deleted ID %s", id_deleta); // logs "deleted ID 1234", etc.
console.log(snapshot.val()); // logs the deleted data, no need for this
console.log(context); // logs the event context
});

RxJS wait for second observable then retry original observable on error - TypeScript/Angular 2

I am fairly new to Angular 2, TypeScript and RxJS and I am creating a simple application that leverages the Salesforce Ajax Toolkit connections library.
I am trying to write a handler to catch when a token has expired any time a method from the connections library is called. I have created a service that essentially wraps the connections library to use observables. For example if we look at the insert function I have created my own wrapper function:
public insert(object: sforce.SObject): Observable<any> {
return new Observable(observer => {
// successfully inserted the record
let insertSuccess = (result) => {
observer.next(result);
observer.complete();
}
// An error occured inserting the record
let insertError = (result) => {
// This does not work yet
if (result.faultcode.indexOf('INVALID_SESSION_ID') != -1) {
this.refreshToken();
}
else {
observer.error(result);
}
}
let callback = { onSuccess: insertSuccess, onFailure: insertError };
sforce.connection.create([object], callback);
});
}
I have another function that refreshes the access token:
public refreshToken(): void {
this.loginService.login().subscribe(
response => {
Globals.SESSION_TOKEN = response.access_token;
//initialize the salesforce connection
this.init(Globals.SESSION_TOKEN, this.loginService.AuthParams.SOAP_URL);
},
error => {
}
);
}
I essentially want the original insert function to wait for refreshToken to complete. If it is successful I want to retry the same insert again, otherwise I want the original insert observable to call observer.error.
I've looked into retry and retryWhen, however I haven't been able to figure out how to implement it to wait for the refreshToken() function to complete. Any guidance or advice on this matter would be greatly appreciated. Thank you in advance.
The catch operator accepts a function which processes an error and the source Observable. This means that if you catch an error you can determine whether you want to resubscribe to the original source in the catch block:
public insert(object: sforce.SObject): Observable<any> {
return new Observable(observer => {
// successfully inserted the record
let insertSuccess = (result) => {
observer.next(result);
observer.complete();
}
// An error occured inserting the record
let insertError = (result) => observer.error(result);
let callback = { onSuccess: insertSuccess, onFailure: insertError };
sforce.connection.create([object], callback);
}).catch((err, source) => {
if (err.faultcode.indexOf('INVALID_SESSION_ID') != -1) {
//This waits for the refresh to complete and then resubscribes
//to the source
//If the refresh errors then it will skip the resubscribe
return this.refreshToken().flatMapTo(source);
}
//Non-authentication error
return Observable.throw(err);
});
}
Then make your refreshToken function into something like so:
public refreshToken(): Observable<any> {
return this.loginService.login()
.tap(response => {
Globals.SESSION_TOKEN = response.access_token;
//initialize the salesforce connection
this.init(Globals.SESSION_TOKEN, this.loginService.AuthParams.SOAP_URL);
});
}

Meteor 1.3 + React: detect subscription failure?

I have a simple Meteor subscription, and I display a loading message while the data is being loaded. But I don't know how to display error message if subscription failed.
export const MyAwesomeComponent = createContainer(() => {
let sub = Meteor.subscribe('some-data');
if (!sub.ready()) return { message: 'Loading...'};
if (sub.failed()) return { message: 'Failed.' }; // How to do this?
return {
data: Data.find().fetch()
}
}, MyInternalRenderComponent);
Problem is, the subscription object doesn't have a failed() method, only a ready() query. How to pass the failure of a subscription as props in a createContainer() method?
I know the Meteor.subscribe method has an onStop callback for this case, but I don't know how to glue it toghether that to pass a property.
After a lot of researching I managed to get this working and I think it answers your question.
Bear in mind I'm using Meteor 1.6, but it should give you the info to get it working on your side.
On the publication/publish:
try {
// get the data and add it to the publication
...
self.ready();
} catch (exception) {
logger.error(exception);
// send the exception to the client through the publication
this.error(new Meteor.Error('500', 'Error getting data from API', exception));
}
On the UI Component:
const errorFromApi = new ReactiveVar();
export default withTracker(({ match }) => {
const companyId = match.params._id;
let subscription;
if (!errorFromApi.get()) {
subscription = Meteor.subscribe('company.view', companyId, {
onStop: function (e) {
errorFromApi.set(e);
}
});
} else {
subscription = {
ready: () => {
return false;
}
};
}
return {
loading: !subscription.ready(),
company: Companies.findOne(companyId),
error: errorFromApi.get()
};
})(CompanyView);
From here all you need to do is get the error prop and render the component as desired.
This is the structure of the error prop (received on the onStop callback from subscribe):
{
error: String,
reason: String,
details: String
}
[Edit]
The reason there is a conditional around Meteor.subscribe() is to avoid an annoying infinite loop you'd get from the natural withTracker() updates, which would cause new subscriptions / new errors from the publication and so on.

Http request made multiple times in Angular2 service

I have created a service that makes a simple GET request:
private accountObservable = null;
constructor(private _http: Http) {
}
getAccount () {
// If we have account cached, use it instead
if (this.accountObservable === null) {
this.accountObservable = this._http.get('http://localhost/api/account')
.map(res => <Account> res.json().data)
.catch(this.handleError);
}
return this.accountObservable;
}
I have added that service in my bootstrap function to provide it globally (my hope is to provide the same instance to all components):
provide(AccountService, { useClass: AccountService })
The problem is when I call this service in different components, a GET request is made every time. So if I add it to 3 components, 3 GET requests will be made even though I check if an observable already exist.
ngOnInit() {
this._accountService.getAccount().subscribe(
account => this.account = account,
error => this.errorMessage = <any>error
);
}
How can I prevent the GET request to be made multiple times?
Use Observable.share():
if (this.accountObservable === null) {
this.accountObservable = this._http.get('./data/data.json')
.share()
.map(res => res.json())
.catch(this.handleError);
}
Plunker
In the Plunker, AppComponent and Component2 both call getAccount().subscribe() twice.
With share(), the Chrome Developer tools Network tab shows one HTTP request for data.json. With share() commented out, there are 4 requests.
There are two types of observables.
Cold Observable : each subscriber receive all the events ( from the begining )
Hot observable : each subscriber receive the events that are emited after subscription.
Cold Observables are the default one. That's what the WS calling is triggered many times.
To make an Observable Hot you have to use following Rx's operators chain :
.publish().refCount()
In your case :
getAccount () {
let accountObservable = this._http.get('http://localhost/api/account')
.map(res => <Account> res.json().data)
.catch(this.handleError);
return accountObservable.publish().refCount();
}
In my case it was because of form post and button clik was set to same listener
The updated solution is:
1) Change your getAccount() method to use share:
getAccount () {
// If we have account cached, use it instead
if (this.accountObservable === null) {
this.accountObservable = this._http.get('http://localhost/api/account')
.pipe(share())
.map(res => <Account> res.json().data)
.catch(this.handleError);
}
return this.accountObservable;
}
2) Add import { share } from 'rxjs/operators'; to the top of your .ts file to get rid of the error on share.

Resources