How to update a document in Fauna without knowing the ref id - database

I'm trying to update a document within a collection in Fauna, but I don't know the ref id besides looking in the database. I've been looking at this post: Can I update a FaunaDB document without knowing its ID? as I get the exact same error but I don't know exactly how to implement it into my own code.
I'm trying to update a document containing a hairdresserId within data.
{
"ref": Ref(Collection("hairdressers"), "328130955075125442"),
"ts": 1649343283790000,
data: {
"hairdresserId": "328027762241568962",
}
}
This is my API file:
import { updateJobProfileInfo } from "#/utils/Fauna"
import { getSession } from "next-auth/react"
export default async (req, res) => {
const session = await getSession({ req })
if (!session) return res.status(401)
const hairdresserId = session.user.id
if (req.method !== "PUT") {
return res.status(405).json({ msg: "Method not allowed" })
}
const {
image,
coverImage,
bio,
education,
phone,
email,
status,
preferences,
} = req.body
try {
const updated = await updateJobProfileInfo(
hairdresserId,
image,
coverImage,
bio,
education,
phone,
email,
status,
preferences
)
return res.status(200).json(updated)
} catch (err) {
console.error(err)
res.status(500).json({ msg: "Something went wrong." })
}
res.end()
}
This is my fauna function:
const updateJobProfileInfo = async (
hairdresserId,
image,
coverImage,
bio,
education,
phone,
email,
status,
preferences
) => {
return await faunaClient.query(
q.Update(q.Ref(q.Collection("hairdressers"), hairdresserId), {
data: {
image,
coverImage,
bio,
education,
phone,
email,
status,
preferences,
},
})
)
}
How can I update a document within my hairdressers collection when I don't have the ref but I know that the document contains hairdresserId ?

I was able to solve it by replacing this bit within the fauna function:
q.Update(q.Ref(q.Collection("hairdressers"), hairdresserId),
with this:
q.Update(
q.Select(
"ref",
q.Get(q.Match(q.Index("hairdresser_by_id"), hairdresserId))
),
I had to create an index with the following term hairdresserId and I named it hairdresser_by_id.
This is the complete fauna function:
const updateJobProfileInfo = async (
hairdresserId,
image,
coverImage,
bio,
education,
phone,
email,
status,
preferences
) => {
return await faunaClient.query(
q.Update(
q.Select(
"ref",
q.Get(q.Match(q.Index("hairdresser_by_id"), hairdresserId))
),
{
data: {
image,
coverImage,
bio,
education,
phone,
email,
status,
preferences,
},
}
)
)
}

Related

When sign in with google firebase always register user as new

Tech: Firebase, Next.js, Google Sign in, Firebase Stripe exstension
Bug reproduction:
When login with Google
Subscribe on stripe
Stripe saves subscription data for that user in firestore
Logout
Login in with Google and old data are overide with new one, and Subscription is lost
Does anyone had similar problem?
Maybe my implementation of Sign-in is bad, here is the Google Sign in code:
const handleGoogleLogin = () => {
signInWithPopup(auth, googleProvider)
.then(async result => {
if (!result.user) return;
const { displayName, email, uid, providerData, photoURL, phoneNumber } =
result.user;
const name = splitName(displayName as string);
const providerId =
(providerData.length && providerData[0]?.providerId) || '';
const data = {
firstName: name?.firstName || '',
lastName: name?.lastName || '',
email,
photoURL,
phoneNumber,
providerId,
};
await updateUser(uid, data);
})
.catch(error => {
console.log('Google login error: ', error);
});
};
Update user function:
export const updateUser = async (uid: string, data: UpdateUserParams) => {
try {
if (!uid) {
return;
}
await setDoc(doc(firestore, 'users', uid), {
account: {
...data,
initials: `${data.firstName[0]}${data.lastName[0]}`,
},
});
} catch (error) {
console.error('Error updating user: ', error);
}
};
setDoc is overwriting the contents of the document with each sign-in. You should instead use set with merge to prevent overwriting the fields you don't want to lose, or check first if the document exists before creating it.
See also:
https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document
Difference Between Firestore Set with {merge: true} and Update

Is there a way to use ctx.session.$create in api using Blitz.js

I am trying to use blitz.js login API in a Flutter project. So I have created a /api/auth/login.ts file with the following code
import { getAntiCSRFToken, getSession, SecurePassword } from "#blitzjs/auth"
import { authenticateUser } from "app/auth/mutations/login"
import { AuthenticationError } from "blitz"
import db from "db"
import { Role } from "types"
const handler = async (req, res) => {
const session = await getSession(req, res)
const { email, password } = req.body
if (req.method !== "POST" || !req.body.data || !session.userId) {
res.status(401).json({ error: `Do not tamper with this route!` })
} else {
console.log("Create a new session for the user")
// Create a new session for the user
//login
const user = await authenticateUser(email, password)
const user = await db.user.findFirst({ where: { email } })
if (!user) return res.json({ data: "Hello", email, password })
const result = await SecurePassword.verify(user.hashedPassword, password)
const { hashedPassword, ...rest } = user
await req.session.$create({ userId: user.id, role: user.role as Role })
res.json({ rest })
}
export default handler
I also tried to use their docs but it was not clear enough and understandable
Can I use ctx.session.$create and insert it to db using blitz.js api
I have solved the problem using this code
import { Role } from "types"
import { authenticateUser } from "app/auth/mutations/login"
import { getSession } from "#blitzjs/auth"
export default async function customRoute(req, res) {
const session = await getSession(req, res)
const { email, password } = req.body
console.log(email, password)
console.log(session.$isAuthorized())
const user = await authenticateUser(email, password)
if (user.id === session.userId) {
return res.status(409).json({ error: `Already exist` })
}
await session.$create({ userId: user.id, role: user.role as Role })
// // res.setHeader("Content-Type", "application/json")
res.end(JSON.stringify({ userId: session.userId }))
}
At first, I was getting a CSRF mismatch error and then a localStorage is undefined and now somehow everything is working with this code.

firestore update value in array inside doc

I'm looking at the firestore docs for hours and still didnt found solution for this case.
I need to add an ammout feature to product my E-commerce app.
data structure :
the main collection is "cart" and the doc is the user email.
here is the current code for add or set a product:
import firebase from 'firebase';
export async function addToCart(userMail, itemId, name, url, price, category, type, description) {
const ammout = 1
const cartItem = { itemId, name, url, price, category, type, description, ammout }
if (userMail === undefined) throw console.error('please Login');
const userDoc = await firebase.firestore().collection("cart").doc(userMail)
await userDoc.get().then((doc) => {
if (doc.exists) {
(async () => {
await userDoc.update({
item: firebase.firestore.FieldValue.arrayUnion(cartItem),
})
})()
}
else {
(async () => {
await userDoc.set({
item: firebase.firestore.FieldValue.arrayUnion(cartItem)
})
})()
}
})
}
The issue could be that you are using both await and Promise.then() in the same function, this could be creating some synchonicity issue, also you don't need to await for the userDoc variable to be populated, as you can see on the firestore documentation example.
So basically I would remove all the async/await from your code as they are not really needed and the Promise.then() on the .get() will be enough to ensure synchronicity in your code which could look something like this:
export function addToCart(userMail, itemId, name, url, price, category, type, description) {
const ammout = 1
const cartItem = { itemId, name, url, price, category, type, description, ammout }
if (userMail === undefined) throw console.error('please Login');
const userDoc = firebase.firestore().collection("cart").doc(userMail)
userDoc.get().then((doc) => {
if (doc.exists) {
userDoc.update({
item: firebase.firestore.FieldValue.arrayUnion(cartItem),
})
}
else {
userDoc.set({
item: firebase.firestore.FieldValue.arrayUnion(cartItem)
})
}
})
}

Can't update item on Express and React

I created a route here where in I am planning to post or update on this route via updateResortOwner:
router.route('/:userid').get(getOwnerResorts, resortOwner).post(protect, resortOwner, createOwnerResort)
router.route('/:userid/:id').get(getResortOwnerById).put(protect, resortOwner, updateResortOwner).delete(protect, resortOwner, deleteResortOwner)
On my controller I have this:
// #description Update a resort
// #route PUT /api/resorts/:userid
// #access Private/Admin
const updateResortOwner = expressAsyncHandler(async (req, res) => {
const {
name,
price_per_night,
description,
address,
city,
province,
zip_code,
latitude,
longitude,
phone,
website,
amenities,
image
} = req.body
const resort = await Resort.findById(req.params.id)
if(resort){
resort.name = name,
resort.price_per_night = price_per_night
resort.description = description
resort.address = address
resort.city = city
resort.province = province
resort.zip_code = zip_code
resort.latitude = latitude
resort.longitude = longitude
resort.phone = phone
resort.website = website
resort.amenities = amenities
resort.image = image
const updatedResort = await resort.save()
res.json(updatedResort)
} else{
res.status(404)
throw new Error('Resort not found!')
}
})
In order to make this work on my redux action:
export const updateResortOwner = (resort) => async (dispatch, getState) => {
try {
dispatch({ type: RESORT_OWNER_UPDATE_REQUEST })
const { userLogin: { userInfo } } = getState()
const config = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${userInfo.token}`
}
}
const { data } = await axios.post(`/api/resorts/${userInfo._id}/${resort._id}`, resort, config)
dispatch({
type: RESORT_OWNER_UPDATE_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: RESORT_OWNER_UPDATE_FAIL,
payload: error.response && error.response.data.message ?
error.response.data.message : error.message
})
}
}
And then on my submithandler on the frontend:
const submitHandler = (e) => {
e.preventDefault()
dispatch(updateResortOwner({
_id: resortId,
name,
price_per_night: pricePerNight,
description,
address,
city,
province,
zip_code: zipCode,
phone,
email,
website,
image,
amenities: {
tv,
reservation,
moderate_noise: moderateNoise,
free_wifi: freeWifi,
trendy,
credit_card: creditCard,
bar,
animals,
kids
}
}))
}
When I submit this its returning this error:
Not found - /api/resorts/5fe92b7a1ecf1a16e8ced784/5fe1798bebef0c2db08d4c76
Any idea what's happening here? How can I fix this?

How to pass additional data to a function that adds things to an object?

I am trying to create a user profile document for regular users and for merchants on Firebase. I am trying to add additional to data this document when a merchant signs up, but haven't succeeded. The difference is that merchants are supposed to have a roles array with their roles. If this is not the right approach to deal with differentiating users, I'd also be happy to hear what's best practice.
My userService file
async createUserProfileDocument(user, additionalData) {
console.log('additionalData: ', additionalData) //always undefined
if (!user) return
const userRef = this.firestore.doc(`users/${user.uid}`)
const snapshot = await userRef.get()
if (!snapshot.exists) {
const { displayName, email } = user
try {
await userRef.set({
displayName,
email,
...additionalData,
})
} catch (error) {
console.error('error creating user: ', error)
}
}
return this.getUserDocument(user.uid)
}
async getUserDocument(uid) {
if (!uid) return null
try {
const userDocument = await this.firestore.collection('users').doc(uid).get()
return { uid, ...userDocument.data() }
} catch (error) {
console.error('error getting user document: ', error)
}
}
This is what happens when the user signs up as a merchant in the RegisterMerchant component:
onSubmit={(values, { setSubmitting }) => {
async function writeToFirebase() {
//I can't pass the 'roles' array as additionalData
userService.createUserProfileDocument(values.user, { roles: ['businessOnwer'] })
authService.createUserWithEmailAndPassword(values.user.email, values.user.password)
await merchantsPendingApprovalService.collection().add(values)
}
writeToFirebase()
I am afraid this might have something to do with onAuthStateChange, which could be running before the above and not passing any additionalData? This is in the Middleware, where I control all of the routes.
useEffect(() => {
authService.onAuthStateChanged(async function (userAuth) {
if (userAuth) {
//is the below running before the file above and not passing any additional data?
const user = await userService.createUserProfileDocument(userAuth) //this should return the already created document?
//** do logic here depending on whether user is businessOwner or not
setUserObject(user)
} else {
console.log('no one signed in')
}
})
}, [])
There is onCreate callback function which is invoked when user is authenticated.
Here's how you could implement it
const onSubmit = (values, { setSubmitting }) => {
const { user: {email, password} } = values;
const additionalData = { roles: ['businessOnwer'] };
auth.user().onCreate((user) => {
const { uid, displayName, email } = user;
this.firestore.doc(`users/${uid}`).set({
displayName,
email,
...additionalData
});
});
authService.createUserWithEmailAndPassword(email, password);
}

Resources