Expected Argument Error for .doc() when called on Firestore Collection - firebase

I want to create a new user document in my Cloud Firestore database whenever a new user logs in. Each doc should have a unique id and I want a "uid" property for each user to match the unique auto-generated id for the doc. At first, I just always ran an update on the user, but I figured it could be helpful to separate my create and update logic. As you can see I haven't worked out how to query if a user exists, but I figured I should test the createUser function before continuing.
Anyway, while I was testing my createUser function I ran into a compilation error.
ERROR in src/app/services/auth.service.ts(64,22): error TS2554:
Expected 1 arguments, but got 0.
UPDATE:
When I try to run the function from localhost after compilation I get this error in the console.
Function CollectionReference.doc() requires its first argument to be
of type string, but it was: undefined
Here is my proposed solution:
import { Injectable } from '#angular/core';
import { User } from './../models/user.model';
import { PermissionsService } from './permissions.service';
import { auth } from 'firebase/app';
import { AngularFireAuth } from 'angularfire2/auth';
import {
AngularFirestore,
AngularFirestoreDocument,
AngularFirestoreCollection,
} from 'angularfire2/firestore';
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
#Injectable({
providedIn: 'root',
})
export class AuthService {
usersCollection: AngularFirestoreCollection<User> = null;
user: Observable<User>;
constructor(
private afAuth: AngularFireAuth,
private db: AngularFirestore,
private permissionsService: PermissionsService,
) {
this.usersCollection = db.collection('users');
this.user = this.afAuth.authState.pipe(
switchMap((user) => {
if (user) {
return this.db
.doc<User>(`users/${user.uid}`)
.valueChanges();
} else {
return of(null);
}
}),
);
}
loginGoogle() {
const provider = new auth.GoogleAuthProvider();
return this.oAuthLogin(provider);
}
loginFacebook() {
const provider = new auth.FacebookAuthProvider();
return this.oAuthLogin(provider);
}
loginTwitter() {
const provider = new auth.TwitterAuthProvider();
return this.oAuthLogin(provider);
}
oAuthLogin(provider) {
return this.afAuth.auth.signInWithPopup(provider).then((credential) => {
//if(the user exists already)
//this.updateUserData(credential.user);
//else
this.createUser();
});
}
createUser() {
const newUserRef = this.usersCollection.doc<User>(); // Error here
let newUser: User;
this.user.subscribe((userData) => {
newUser = {
uid: newUserRef.id,
email: userData.email,
photoURL: userData.photoURL,
displayName: userData.displayName,
roles: {
member: true,
},
permissions: this.permissionsService.memberPermissions;
};
});
newUserRef
.set(newUser)
.then(() => {
console.log('created user');
})
.catch((err) => {
console.log('Error adding user: ' + err);
});
}
updateUserData(user) {
const userRef: AngularFirestoreDocument<any> = this.db.doc(
`users/${user.uid}`,
);
const userPermissions = this.addPermissions(userRef);
console.log(userPermissions); // This works
const data: User = {
uid: user.uid,
email: user.email,
photoURL: user.photoURL,
displayName: user.displayName,
roles: {
member: true,
}, // I need to make sure this keeps current user roles
permissions: userPermissions,
};
console.log(data); // This works
userRef
.set(data)
.then(() => {
console.log('Success: Data for userDoc overwritten');
})
.catch((err) => {
console.error('Error writing to userDoc: ' + err);
});
}
addPermissions(userRef) {
const tempPermissions = [];
userRef.valueChanges().subscribe((userdata) => {
if (userdata.roles.reader === true) {
tempPermissions.push(this.permissionsService.memberPermissions);
}
if (userdata.roles.author === true) {
tempPermissions.push(this.permissionsService.authorPermissions);
}
if (userdata.roles.admin === true) {
tempPermissions.push(this.permissionsService.adminPermissions);
}
});
return tempPermissions;
}
checkPermissions(permission: string) {
if (!this.user) {
return false;
} else {
this.user.subscribe((data) => {
for (const p of data.permissions) {
if (p === permission) {
return true;
}
}
return false;
});
}
}
logout() {
this.afAuth.auth.signOut();
this.user = null;
}
}
I checked the documentation on the .doc() function and it should work fine with 0 arguments. It should be returning an empty doc reference. However, it keeps throwing the error saying it expects 1 argument. Any idea why this isn't working?

Related

How to create a function that returns new session format with extra key value pair

I am using NextJS with NextAuth with google and email providers. Unfortunately, the session returns only few fields that does not include userId of the user from the database.
I created however a function that I intend to use with every getServerSideProps request. The function returns the following:
{
user: {
name: 'daniel sas',
email: 'emailofuser#gmail.com',
image: 'https://lh3.gooleusercontent.com/a/AEdFTp6r44ZwqcfJORNnuYtbVv_LYbab-wv5Uyxk=s96-c',
userId: 'clbcpc0hi0002sb1wsiea3q5d'
},
expires: '2022-12-17T20:18:52.580Z'
}
The problem is I am getting an error that does not allow me to pass the props in the page:
Error: Your `getServerSideProps` function did not return an object. Did you forget to add a `return`?
In the function I get the user by the email, and attach the userId.
import { getSession } from "next-auth/react";
import prisma from './prisma'
// This function get the email and returns a new session object that includes
// the userId
export const requireAuthentication = async context => {
const session = await getSession(context);
const errorOrUserNotFound = () => {
return {
redirect: {
destination: '/signup',
permanent: false
}
}
}
// If there is no user or there is an error ret to signup page
if (!session) {
errorOrUserNotFound();
}
// If the user is not found return same redirect to signup
else {
try {
const user = await prisma.user.findUnique({where: { email: session.user.email }});
if (!user) return errorOrUserNotFound();
// Must return a new session here that contains the userId...
else {
const newSession = {
user: {
...session.user,
userId: user.id
},
expires: session.expires
};
console.log(newSession);
return {
props: {
session: newSession
}
}
}
}
catch (error) {
if (error) {
console.log(error);
}
}
}
}
The react component looks like this. In the getServerSideProps i return the await function. The problem is that when I log the prop in the serverside, I get the following:
{
props: { session: { user: [Object], expires: '2022-12-17T20:18:52.580Z' } }
}
However, if i log the props in the clientside, I get an empty object...
//Clientside compoen
import { getSession } from "next-auth/react"
import { Fragment, useState } from "react";
import { requireAuthentication } from "../../lib/requireAuthentication";
import CreateListModal from "./CreateListModal";
const DashboardPage = props => {
const [loading, setloading] = useState(false);
console.log(props);
return (
<section className="border-4 border-orange-800 max-w-5xl mx-auto">
<CreateListModal userId={props.userId} loading={loading} setloading={setloading} />
</section>
)
}
export const getServerSideProps = async context => {
const session = await getSession(context);
const reqAuth = await requireAuthentication(context);
console.log(reqAuth);
return reqAuth
}
export default DashboardPage;

Vue3 - Pinia + Auth0 - isAuthenticated always false

I'm developing a vue3 app using pinia as state manager and auth0 as authprovider.
In my vue router, I've the following code to manage the authentication:
router.beforeEach(async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
const authStore = useAuthStore();
const isLogged = authStore.isLogged();
if (!isLogged) await handleNotLogged(to, from, next);
else await handleLogged(to, from, next);
});
async function handleNotLogged(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {
const authStore = useAuthStore();
if (to?.query?.code && to?.query?.state) {
next({ name: '/logged/home' });
} else {
await authStore.login();
}
}
async function handleLogged(to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) {next()}
here is my authStore
import { defineStore } from 'pinia';
import { User } from '#models/user';
import { useStorage } from '#vueuse/core';
import { RouteLocation } from 'vue-router';
import { createAuth0 } from '#auth0/auth0-vue';
const authService = createAuth0({
domain: import.meta.env.VITE_APP_AUTH_URL,
client_id: import.meta.env.VITE_APP_AUTH_CLIENT_ID,
redirect_uri: `${window.location.origin}`,
});
const defaultUserData = {} as User;
const defaultLastRoute = { path: '/' } as RouteLocation;
export const useAuthStore = defineStore('AuthStore', {
state: () => ({
userData: useStorage('userData', defaultUserData, localStorage),
lastRoute: useStorage('lastRoute', defaultLastRoute, localStorage),
authService,
}),
actions: {
isLogged(): boolean {
try {
return this.authService.isAuthenticated;
} catch (error) {
return false;
}
},
async login(): Promise<boolean> {
try {
await this.authService.loginWithRedirect();
return true;
} catch (error) {
console.error(error);
return false;
}
},
async logout(): Promise<boolean> {
try {
await this.authService.logout();
return true;
} catch (error) {
console.error(error);
return false;
}
},
},
});
And also my main.ts
import App from './App.vue';
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import { registerPlugins } from '#plugins';
import { useAuthStore } from '#store/auth';
import router from '#router';
import vuetify from './plugins/vuetify';
async function main() {
const app = createApp(App);
registerPlugins();
const pinia = createPinia();
app.use(pinia);
const authStore = useAuthStore();
const { authService } = authStore;
app.use(authService);
app.use(router);
app.use(vuetify).mount('#app');
}
main();
The problem is that everytime the beforeEach is triggered, the auth0 isAuthenticated returns false. Even when i've just succesfully logged.
I've searched for some answers, and some said that ewhen there is a code and state in query params we should call the auth0.handleRedirectCallback but there's a note in the method saying
Note: The Auth0-Vue SDK handles this for you, unless you set skipRedirectCallback to true. In that case, be sure to explicitly call handleRedirectCallback yourself.
PS: The application in auth0 is configured as Single Page Application
There is already a topic with this question answered, I believe this one can help you:
Auth0 isAuthenticated() is always false

Maximum call stack size exceeded( in Nuxt + Firebase Project)

I'm currently creating an authentication feature in Nuxt and Firebase.
The login and logout process itself can be done and the header display changes accordingly, but there is an error in console when I press the login button.
Error content (in console)
Uncaught RangeError: Maximum call stack size exceeded
at Function.keys (<anonymous>)
code
Header.vue(This is the page containing the login button.)↓
googleLogin () {
const provider = new firebase.auth.GoogleAuthProvider()
auth.signInWithPopup(provider)
.then(res => {
this.dialogAuthVisible = false
this.$store.dispatch('auth/setUser',res.user)
}).catch(e => console.log(e))
}
store/auth.js↓
export const strict = false
export const state = () => ({
user: null
})
export const mutations = {
SET_USER (state, payload) {
state.user = payload
}
}
export const actions = {
setUser ({ commit }, user) {
commit('SET_USER',user)
}
}
export const getters = {
isAuthenticated (state) {
return !!state.user
}
}
default.vue↓
mounted () {
auth.onAuthStateChanged(user => {
const { uid, displayName, photoURL} = user
if (user) {
this.$store.dispatch('auth/setUser', { uid, displayName, photoURL})
} else {
this.$store.dispatch('auth/setUser', null)
}
})
}
If there's any information I'm missing, please let me know 🙇️.
Please teach me how to do this 🙇️.
I think the problem is in this code lines :
export const mutations = {
SET_USER (state, payload) {
state.user = payload
}
}
export const actions = {
setUser ({ commit }, user) {
commit('SET_USER',user)
}
}
There is a loop between this mutations and actions
Instead of setting the entire payload into the store object, I just picked the fields I needed, and that resolved the problem for me.
Before:
AUTH_STATUS_CHANGED ({commit}, data: any): any {
if (data && data.authUser) {
commit('SetAuthUser', data.authUser);
} else {
commit('SetAuthUser', null);
}
}
After:
AUTH_STATUS_CHANGED ({commit}, data: any): any {
if (data && data.authUser) {
const user = data.authUser;
commit('SetAuthUser', {
uid: user.uid,
email: user.email,
emailVerified: user.emailVerified,
displayName: user.displayName,
isAnonymous: user.isAnonymous,
photoURL: user.photoURL,
stsTokenManager: user.stsTokenManager,
createdAt: user.createdAt,
lastLoginAt: user.lastLoginAt,
apiKey: user.apiKey,
});
} else {
commit('SetAuthUser', null);
}
}
Inside the mutation, just add the value received from the mutation payload.

SQLite in Ionic 3 getting data null after navigate to another page

Please help me. I've made Database Provider to connect with SQLite in ionic 3. When i want to get the data rows it's always getting null but when I check data length it has 1
This is my DatabaseProvider
import { Injectable } from '#angular/core';
import { Platform } from 'ionic-angular';
import { SQLite, SQLiteObject } from '#ionic-native/sqlite';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Storage } from '#ionic/storage';
#Injectable()
export class DatabaseProvider {
private db: SQLiteObject;
private databaseReady: BehaviorSubject<boolean>;
constructor(private storage: Storage, private sqlite: SQLite, private platform: Platform) {
this.databaseReady = new BehaviorSubject(false);
this.platform.ready().then(() => {
this.sqlite.create({
name: 'takia.db',
location: 'default'
}).then((database: SQLiteObject) => {
this.db = database;
this.storage.get('database_filled').then(val => {
if (val) {
this.databaseReady.next(true);
} else {
this.initDB();
}
});
}).catch(e => { console.log(e); });
});
}
initDB(){
this.db.executeSql('CREATE TABLE IF NOT EXISTS users(user_id INTEGER PRIMARY KEY, username TEXT, email TEXT, password TEXT)', {})
.then(res => {
this.db.executeSql('INSERT INTO users VALUES(NULL,?,?,?,?)',['admin','admin.#email.com','password'])
.then(res => {
this.databaseReady.next(true);
this.storage.set('database_filled', true);
}).catch(e => {
console.log(e);
});
}).catch(e => {
console.log(e)
});
this.db.executeSql('CREATE TABLE IF NOT EXISTS questions(user_id INTEGER PRIMARY KEY, username TEXT, email TEXT, password TEXT)', {})
.then(res => {
this.db.executeSql('INSERT INTO users VALUES(NULL,?,?,?,?)',['admin','admin.tazkiaiibs.sch.id','bismillah'])
.then(res => {
this.databaseReady.next(true);
this.storage.set('database_filled', true);
}).catch(e => {
console.log(e);
});
}).catch(e => {
console.log(e)
});
}
getUser(){
return this.db.executeSql('SELECT user_id, username, email, password, COUNT(*) total FROM users', [])
.then(res => {
let users = [];
if (res.rows.length > 0) {
for (var i = 0; i < res.rows.length; i++) {
let row = res.rows.item(i);
users.push(row);
}
}
return users;
}, err => {
console.log('Error: ', err);
return [];
});
}
getDatabaseState() {
return this.databaseReady.asObservable();
}
}
and this is my code in component
constructor(public navCtrl: NavController,
public navParams: NavParams,
private dbProvider: DatabaseProvider,
private toastCtrl: ToastController) {
this.loadData();
}
loadData(){
this.dbProvider.getUser().then(data=>{
if(data.length>0){
this.id = data[0].id;
this.old_password = data[0].password;
this.username = data[0].username;
this.email = data[0].email;
let toast = this.toastCtrl.create({
message: 'ID '+data[0].username,
duration: 3000
});
toast.present();
}else{
let toast = this.toastCtrl.create({
message: 'No data'+data.length,
duration: 3000
});
toast.present();
}
});
}
When i run loadData function the length is not 0 but the data is null.
The comment under the question seemed to help, so I form it as an answer to hopefully collect some reputation points.
The problem seems to be a misformed SQL-query. The count(*) part makes the query return at least one row. Therefore the other fields might be NULL, when queried before data is added to the database.
The function "getUser" is called before the promise callback is executed.

How can I use Ionic 2 Pulling Refresher in my app?

Hi all I'M working on angularjs 2/ionic2 mobile app , i need to do pulling refresher in my app, we have tried this link:- https://ionicframework.com/docs/v2/api/components/refresher/Refresher/ process i got refreshing the page but it's not get dismissLoader, we have given the images of my app refreshing:-
we don't know where we did the mistake and where we need to add the correct functionality in my project...
while we pulling the page it's refreshing but it's not get dismiss, Refreshing text and icon showing it's not get dismissed...
what we expecting once we pulled the page it's need to refresh after that refreshing text and icon need to be dismiss...
**we added coding only in html:-****
<ion-refresher (ionRefresh)="setFilteredItems($event)">
<ion-refresher-content refreshingSpinner="circles" refreshingText="Refreshing...">
</ion-refresher-content>
</ion-refresher>
we have not added anything in type script part...so please check and update the solution please....
we have created example Plunker
please update the plunker as well to know the solution, thanks.....
My Type Script constructor code:-
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { GlobalStateService } from '../../services/global-state.service';
import { AccountSigninPage } from '../account-signin/account-signin';
import { AccountSignupPage } from '../account-signup/account-signup';
import { ActivityAddPage } from '../activity-add/activity-add';
import { Activity } from "../../services/actopi-sdk/model/Activity";
import { UserLoginService } from "../../services/account-management.service";
import { ResourceListPage } from '../resource-list/resource-list';
import { IamAuthorizerClient } from "../../services/actopi-api.service";
import { CustomAuthorizerClient, NoAuthorizationClient, UserPoolsAuthorizerClient } from "../../services/actopi-api.service";
import { Config } from '../../config/config'
import { Logger } from '../../services/logger.service';
import 'rxjs/add/operator/debounceTime';
import { FormControl } from '#angular/forms';
declare const AWS: any;
#Component({
templateUrl: 'activity-list.html',
})
export class ActivityListPage {
initialized = false;
accountSigninPage = AccountSigninPage;
accountSignupPage = AccountSignupPage;
activityAddPage = ActivityAddPage;
activitys: Activity[] = [];
resourceListPage = ResourceListPage;
searchTerm: string = '';
searchControl: FormControl;
displayDeleteActivityConfirmation(activityId, activityName) {
console.log("Deleting activityID " + activityId);
let confirm = this.globals.getAlertController().create({
title: 'Delete activity?',
message: `Are you sure you want to delete [<b>${activityName}</b>]? All resources and bookings associated with [<b>${activityName}</b>] will also be deleted!`,
buttons: [
{
text: 'Cancel',
handler: () => { /* do nothing */ }
},
{
text: 'OK',
handler: () => {
this.deleteActivity(activityId)
.then(() => {
this.globals.dismissLoader();
this.globals.displayToast(`Activity [${activityName}] has been successfully deleted`);
})
.catch((err) => {
this.globals.dismissLoader();
this.globals.displayAlert('Error encountered',
'Delete failed. Please check the console logs for more information.');
console.log(err);
});
}
}
]
});
confirm.present();
}
deleteActivity(activityId): Promise<void> {
return new Promise<void>((resolve, reject) => {
// delete from the database
this.globals.displayLoader("Deleting...");
this.customAuthClient.getClient().activitysDelete(activityId).subscribe(
() => {
// remove the item from the activitys array
let index = this.activitys.findIndex(activity => { return activity.activityId == activityId });
if (index > -1) {
this.activitys.splice(index, 1);
}
resolve();
},
(err) => {
reject(err);
}
);
});
}
gotoResourceListPage(activity) {
this.navCtrl.push(ResourceListPage, activity);
}
filterItems(searchTerm): void {
this.activitys = [];
this.userPoolsAuthClient.getClient().activitysList().subscribe(
(data) => {
this.activitys = data.items.filter((activity) => {
return activity.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
});
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
});
}
loadActivitysWithAuth(): void {
this.activitys = [];
this.userPoolsAuthClient.getClient().activitysList().subscribe(
(data) => {
// this.activitys = data.items
// sort by name
let searchTerm: string = '';
// this.activitys = data.items.sort((a, b) => {
// return a.name.localeCompare(b.name);
// });
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
}
);
};
loadActivitysWithoutAuth(): void {
this.activitys = [];
this.noAuthClient.getClient().activitysList().subscribe(
(data) => {
// this.activitys = data.items
// sort by name
this.activitys = data.items.sort((a, b) => {
return a.name.localeCompare(b.name);
});
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
}
);
};
constructor(private navCtrl: NavController, public globals: GlobalStateService, private noAuthClient: NoAuthorizationClient, private customAuthClient: CustomAuthorizerClient, private userPoolsAuthClient: UserPoolsAuthorizerClient, private authClient: IamAuthorizerClient) {
this.searchControl = new FormControl();
}
ionViewDidEnter() {
Logger.banner("Activitys");
this.activitys = [];
if (!this.initialized) {
this.initialized = false;
if (UserLoginService.getAwsAccessKey() != null) {
// if (CognitoUtil.getUserState() === UserState.SignedIn) {
// console.log(AWS.config.credentials);
UserLoginService.getAwsCredentials()
.then((data) => {
this.globals.displayLoader("Loading...");
this.setFilteredItems(refresher);
this.searchControl.valueChanges.debounceTime(700).subscribe(search => {
this.globals.displayLoader("Loading...");
this.setFilteredItems(refresher);
this.globals.dismissLoader();
});
})
.catch((err) => {
console.log("ERROR: Unable to load activitys!");
console.log(err)
})
}
}
}
setFilteredItems(refresher) {
return this.filterItems(this.searchTerm);
refresher.complete();
}
}
You need to call refresher.complete() to dismiss your refresher once loading of new data is done..
setFilteredItems(refresher?:any){
//async call to load.
// in the then function
if(refresher)
refresher.complete();
}
}
The refresher is sent from the html onRefresh. With ? you can call without passing object in your code.
this.setFilteredItems();
Also consider refactoring your code. You should ideally call complete after async task ia done and no point in returning another function to the html side and calling complete after return will just end up as dead code.

Resources