Firebase Storage download url not available after upload - firebase

I am using Angular and AngularFire2. I am trying to upload an image to firebase storage, then once that is done I am take that reference and get the download url and upload it to the database. For some reason even though the upload is complete and I have the snapshot, when I try to use that in order to get the URL it's giving me an error that the object does not exist. Any thoughts on what I might be doing wrong?
task.snapshotChanges().pipe(
concatMap(snap => {
return snap.ref.getDownloadURL()
}),
concatMap(url => this.db.collection('library').add({
name: this.image.name,
path: path,
largeUrl: url
}))
).subscribe(ref => {
this.completed = true;
}, error => console.log(error));
error:
Firebase Storage: Object 'library/1542515976022_lemonade-smoothie.jpg' does not exist.

Ok, so my issue was not really understanding concatMap. I thought it wasn't called until the last onNext() of the upload Observable. It was being called on the first onNext(), which means the file had not completely updated. Below is what I ended up doing, although it seems like there should be another way. What I would like is to only switch to the new Observable track if the downloaded bytes equals the total bytes. I'm not sure how to do this with RxJS though. If anyone had any thoughts let me know.
task
.snapshotChanges()
.pipe(finalize(() => this.uploadToDb(path)))
.subscribe();
uploadToDb(path: string) {
this.storage
.ref(path)
.getDownloadURL()
.pipe(
concatMap(url =>
this.db.collection('library').add({
name: this.image.name,
path: path,
largeUrl: url
})
)
)
.subscribe(
ref => (this.completed = true),
error => {
console.log(error);
this.error = true;
}
);
}

Related

ionic 3 sqlite - How to execute requests from files

I am currently developing a mobile app with ionic. On this application there is a local sqlite database.
I am looking to be able to execute queries from files for possible updates of the database.
For example, when you first install the application, the tables are created if they do not exist. If changes are made to these tables after installing the application, the changes are not taken into account.
I would like to create files which can be executed one by one according to PRAGMA user_version.
Something like :
// Get pragma user version, if it's undefined, it's set to 1.
// If user_version < 1, we execute the sql file, then we update the user_version
// to 1.
db.executeSql(`PRAGMA user_version;`, []).then((res)=> {
if(res.rows.item(0).user_version < 1) {
db.sqlBatch(`app/sqlFiles/1.sql`, []).then((res)=>{
console.log(res);
}, (err) => {
console.log("Error : " + JSON.stringify(err));
});
db.executeSql(`PRAGMA user_version = 1;`, []).then((res)=>{
console.log(res);
}, (err) => {
console.log("Error : " + JSON.stringify(err));
});
}
}, (err) => {
console.log("Error : " + JSON.stringify(err));
});
I thought about using a file reader but the file is not found during the execution of the function.
this.fileOpener.open('assets/SQLFile/1.sql', 'text/plain')
.then((file) => {
var reader = new FileReader();
reader.onloadend = function(evt) {
console.log("read success");
console.log(evt.target);
};
db.sqlBatch(reader.readAsText(file)).then((res)=>{
console.log(res);
}, (err) => {
console.log("Error : " + JSON.stringify(err));
});
console.log('File is opened');
})
.catch(e => console.log('Error opening file', e));
Will anyone have an idea for doing this kind of thing? (I am looking for a solution that respects good programming practices).
Please note that I am a beginner with ionic and sqlite
Thank you in advance !
But you're getting an error or what? Since the post seems to be valid but the implementation seems to be a little off.
After reading the file, storing the content into a variable, you should execute that "sql query" as usually.
Nevertheless it's not a secure way of do that.
When something in your app change due to an update or upgrade, wouldn't be easier and safer to specify that into the code instead of a single file containing all the database info and scheme?
Edited:
If you want to add custom files to deployment, check this out https://ionicframework.com/docs/developer-resources/app-scripts/

Map operator. Different function types and accessor

i have recently updated Firebase and AngularFire2 in my Ionic project
Versions:
Firebase: 5.0.3
AngularFire2: 5.0.0-rc.10
rxjs 6.2.0
now I tried to upgrade the project from the regular map to pipe using the migration guide:
Migration guide AngularFire2 version5
But if i use exact the same example for the following code block:
///my code
let dataBaseCollection = this.store.collection('items').snapshotChanges().pipe(
map(actions =>
actions.map(a => ({ key: a.key, ...a.payload.val() }))
)
).subscribe(items => {
return items.map(item => item.key);
});
///example
afDb.list('items').snapshotChanges().pipe(
map(actions =>
actions.map(a => ({ key: a.key, ...a.payload.val() }))
)
)
I get the following exceptions:
Argument of type 'OperatorFunction' is not assignable to parameter of type 'UnaryFunction, Observable<{ const: string; return: any; }[]>>'.
Types of parameters 'source' and 'source' are incompatible.
Type 'Observable' is not assignable to type 'Observable'. Two different types with this name exist, but they are unrelated.
Property 'source' is protected in type 'Observable' but public in type 'Observable'.
I already tried the two different operator from rxjs
import { map } from 'rxjs/operators';
import { map } from 'rxjs/operators/map';
I appreciate any help.
Well, I don't know if this is the issue, but a few observations:
let dataBaseCollection is receiving a Subscription from .subscribe and not the items. You cannot return inside the subscribe, so your code should be just this:
let dataBaseCollection = this.store.collection('items').snapshotChanges().pipe(
map(actions => actions.map(a => ({ key: a.key, ...a.payload.val() })))
)
If you want to map only the keys then just do:
let dataBaseCollection = this.store.collection('items').snapshotChanges().pipe(
map(actions => actions.map(a => a.key))
)
I get it running.
I do not have a real clue, what the actual problem was. But I completely deleted my local repository and get all files new.
Then I reinstalled all NPM files. Somewhere in this area was my problem. I would guess, that any of my npm packages was the problem.
Im sorry, that I can not clarify the issue more. Maybe someone else will have the same Problem in the future and can add it here.

Firebase Function onCreate does not work

I want to send an email when a new order is created in my firebase database, but nothing happens when I create an order. My function:
exports.sendEmailConfirmation = functions.database.ref('/orders').onCreate(event => {
const mailOptions = {
from: '"Someone." <noreply#firebase.com>',
to: 'someone#gmail.com',
};
// Building Email message.
mailOptions.subject = 'New order from mobile app!';
mailOptions.text = 'John Doe lorem ipsum';
return mailTransport.sendMail(mailOptions)
.then(() => console.log('¡¡¡ Enail sent !!!'))
.catch((error) => console.error('Error!!', error));
});
This code works using onWrite()....
Your function isn't triggering because /orders already exists. onCreate triggers will only run when the path you specify is newly created.
If you want to know when a child is newly added under /orders, you should use a wildcard in the path:
functions.database.ref('/orders/{orderId}')

ngrx effect not being called when action is dispatched from component

I am having an issue with the ngrx store not dispatching an action to the effect supposed to deal with it.
Here is the component that tries to dispatch:
signin() {
this.formStatus.submitted = true;
if (this.formStatus.form.valid) {
this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
}
}
The actions:
export const ActionTypes = {
STANDARD_SIGNIN: type('[Session] Standard Signin'),
LOAD_PERSONAL_INFO: type('[Session] Load Personal Info'),
LOAD_USER_ACCOUNT: type('[Session] Load User Account'),
RELOAD_PERSONAL_INFO: type('[Session] Reload Personal Info'),
CLEAR_USER_ACCOUNT: type('[Session] Clear User Account')
};
export class StandardSigninAction implements Action {
type = ActionTypes.STANDARD_SIGNIN;
constructor(public payload: Credentials) {
}
}
...
export type Actions
= StandardSigninAction
| LoadPersonalInfoAction
| ClearUserAccountAction
| ReloadPersonalInfoAction
| LoadUserAccountAction;
The effect:
#Effect()
standardSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.STANDARD_SIGNIN)
.map((action: StandardSigninAction) => action.payload)
.switchMap((credentials: Credentials) =>
this.sessionSigninService.signin(credentials)
.map(sessionToken => {
return new LoadPersonalInfoAction(sessionToken);
})
);
I can see in debug that the component does call the dispatch method. I can also confirm that StandardSigninAction is indeed instantiated because the breakpoint in the constructor is hit.
But the standardSignin$ effect is not called...
What can possibly cause an effect not being called?
How can I debug what is going on within the store?
Can someone please help?
P.S. I do run the above effect as follows in my imports:
EffectsModule.run(SessionEffects),
edit: Here is my SessionSigninService.signin method (does return an Observable)
signin(credentials: Credentials) {
const headers = new Headers({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'});
const options = new RequestOptions({headers: headers});
const body = 'username=' + credentials.username + '&password=' + credentials.password;
return this.http.post(this.urls.AUTHENTICATION.SIGNIN, body, options).map(res => res.headers.get('x-auth-token'));
}
This is not going to be a definitive answer, but, hopefully, it will be helpful.
Before you start:
Make sure you are using the latest versions of the #ngrx packages (that are appropriate for the version of Angular you are using).
If you've updated any packages, make sure you re-start your development environment (that is, restart the bundler, the server, etc.)
If you've not done so already, you should have a look at the implementation of the Store - so that you make some educated guesses as to what could be going wrong. Note is that the Store is pretty light. It's both an observable (using the state as its source) and an observer (that defers to the dispatcher).
If you look at store.dispatch you'll see that it's an alias for
store.next, which calls next on the Dispatcher.
So calling:
this.store.dispatch(new StandardSigninAction(this.formStatus.form.value.credentials));
should just see an action emitted from the dispatcher.
The Actions observable that's injected into your effects is also pretty light. It's just an observable that uses the Dispatcher as its source.
To look at the actions that are flowing through the effect, you could replace this:
#Effect()
standardSignin$: Observable<Action> = this.actions$
.ofType(session.ActionTypes.STANDARD_SIGNIN)
with this:
#Effect()
standardSignin$: Observable<Action> = this.actions$
.do((action) => console.log(`Received ${action.type}`))
.filter((action) => action.type === session.ActionTypes.STANDARD_SIGNIN)
ofType is not an operator; it's a method, so to add do-based logging, it needs to be replaced with a filter.
With the logging in place, if you are receiving the action, there is something wrong with the effect's implementation (or maybe the action types' strings/constants aren't what you think they are and something is mismatched).
If the effect is not receiving the dispatched action, the most likely explanation would be that the store through which you are dispatching the StandardSigninAction is not that same store that your effect is using - that is, you have a DI problem.
If that is the case, you should look at what differs from the other SessionEffects that you say are working. (At least you have something working, which is a good place to start experimenting.) Are they dispatched from a different module? Is the module that dispatches StandardSigninAction a feature module?
What happens if you hack one of the working SessionEffects to replace its dispatched action with StandardSigninAction? Does the effect then run?
Note that the questions at the end of this answer aren't questions that I want answered; they are questions that you should be asking yourself and investigating.
Your store's stream may be stopping because of either unhandled errors or - perhaps more confusingly - errors that seem 'handled' using .catch that actually kill the stream without re-emitting a new Observable to keep things going.
For example, this will kill the stream:
this.actions$
.ofType('FETCH')
.map(a => a.payload)
.switchMap(query => this.apiService.fetch$(query)
.map(result => ({ type: 'SUCCESS', payload: result }))
.catch(err => console.log(`oops: ${err}`))) // <- breaks stream!
But this will keep things alive:
this.actions$
.ofType('FETCH')
.map(a => a.payload)
.switchMap(query => this.apiService.fetch$(query)
.map(result => ({ type: 'SUCCESS', payload: result }))
.catch(e => Observable.of({ type: 'FAIL', payload: e}))) // re-emit
This is true for any rxjs Observable btw, which is especially important to consider when broadcasting to multiple observers (like ngrx store does internally using an internal Subject).
I am using a later version of ngrx (7.4.0), so cartant's suggestion of:
.do((action) => console.log(`Received ${action.type}`))
should be...
... = this.actions.pipe(
tap((action) => console.log(`Received ${action.type}`)),
...
And in the end I discovered I had missed adding my new effects export to module, like:
EffectsModule.forRoot([AuthEffects, ViewEffects]), // was missing the ', ViewEffects'
If you are using version 8, ensure you wrap each action with createEffect.
Example:
Create$ = createEffect(() => this.actions$.pipe(...))
Another possible reason is that if you used ng generate to create the module where you imported the Effects make sure it is imported in the App Module as the following command 'ng generate module myModule' will not add it to the app module.

Angular 2 - Checking for server errors from subscribe

I feel like this scenario should be in the Angular 2 docs, but I can't find it anywhere.
Here's the scenario
submit a form (create object) that is invalid on the server
server returns a 400 bad request with errors I display on the form
after the subscribe comes back, I want to check an error variable or something (ie. if no errors > then route to newly created detail page)
I imagine it working something like this:
this.projectService.create(project)
.subscribe(
result => console.log(result),
error => {
this.errors = error
}
);
}
if (!this.errors) {
//route to new page
}
I'm very new to Angular 2 so this may come from my lack of understanding in how an Observable works. I have no issue with displaying that data on the form, but can't figure out how to see it within the ts component. I really just want to check the success/fail of the http create.
As stated in the relevant RxJS documentation, the .subscribe() method can take a third argument that is called on completion if there are no errors.
For reference:
[onNext] (Function): Function to invoke for each element in the observable sequence.
[onError] (Function): Function to invoke upon exceptional termination of the observable sequence.
[onCompleted] (Function): Function to invoke upon graceful termination of the observable sequence.
Therefore you can handle your routing logic in the onCompleted callback since it will be called upon graceful termination (which implies that there won't be any errors when it is called).
this.httpService.makeRequest()
.subscribe(
result => {
// Handle result
console.log(result)
},
error => {
this.errors = error;
},
() => {
// 'onCompleted' callback.
// No errors, route to new page here
}
);
As a side note, there is also a .finally() method which is called on completion regardless of the success/failure of the call. This may be helpful in scenarios where you always want to execute certain logic after an HTTP request regardless of the result (i.e., for logging purposes or for some UI interaction such as showing a modal).
Rx.Observable.prototype.finally(action)
Invokes a specified action after the source observable sequence terminates gracefully or exceptionally.
For instance, here is a basic example:
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/finally';
// ...
this.httpService.getRequest()
.finally(() => {
// Execute after graceful or exceptionally termination
console.log('Handle logging logic...');
})
.subscribe (
result => {
// Handle result
console.log(result)
},
error => {
this.errors = error;
},
() => {
// No errors, route to new page
}
);
Please note that the previous syntax with callbacks has been deprecated as of 6.4 and is going to be removed with 8.0. Instead of
of([1,2,3]).subscribe(
(v) => console.log(v),
(e) => console.error(e),
() => console.info('complete')
)
you should now use
of([1,2,3]).subscribe({
next: (v) => console.log(v),
error: (e) => console.error(e),
complete: () => console.info('complete')
})
https://rxjs.dev/deprecations/subscribe-arguments
You can achieve with following way
this.projectService.create(project)
.subscribe(
result => {
console.log(result);
},
error => {
console.log(error);
this.errors = error
}
);
}
if (!this.errors) {
//route to new page
}
Updated rxjs way 2022
this.projectService.create(project)
.subscribe({
next: (data)=>console.log('data',data),
error: (err)=>console.log('error',err),
complete:()=>console.log('complete')
});

Resources