Why VS Code does not stop at breakpoint? - reactjs

Debugger starts, .vscode/launch.json is set up. But I got this error when an api endpoint get called in Next.js:
API resolved without sending a response for /api/registerEmail, this may result in stalled requests.
I made this endpoint, something wrong here maybe? But the expected json is returned by the endpoint, strange.
import { NextApiRequest, NextApiResponse } from 'next'
import { connectToDatabase } from 'lib/connectToDatabase'
import { initUserDTO } from 'lib/initUserDTO'
import bcrypt from 'bcryptjs-react'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { mongoClient } = await connectToDatabase()
if (mongoClient) {
const db = mongoClient.db('tikex')
const collection = db.collection('users')
const u = await collection.findOne({
email: req.body.email,
fbId: null,
googleId: null,
})
if (u) {
res.status(400).json({ error: 'Email already exist' })
res.end()
} else {
bcrypt.hash(req.body.password, 10, async function (err, hash) {
const body = { ...req.body, password: hash }
await collection.insertOne(body)
const u = await collection.findOne({
email: req.body.email,
fbId: null,
googleId: null,
})
let u2 = await initUserDTO(u)
res.status(200).json(u2)
res.end()
})
}
} else {
res.status(400).json({ name: 'Database connection error' })
res.end()
}
}

Related

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.

How to handle error format in Redux-toolkit rtk-query graphql application

I'm developing an application based on redux-toolkit rtk-query and graphql.
I use graphql-codegen to generate the reducers starting from the graphql schema and everything working as expected.
Now i have a problem to handle errors. Has i understand redux-toolkit raise custom error with a specific format like this
{
name: "Error",
message: "System error",
stack:
'Error: System error: {"response":{"errors":[{"message":"System error","locations":[{"line":3,"column":3}],"path":["completaAttivita"],"extensions":{"errorCode":505,"classification":"VALIDATION","errorMessage":"Messaggio di errore","verboseErrorMessage":"it.cmrc.sid.backend.exception.CustomException: I riferimenti contabili non sono piĆ¹ validi","causedBy":"No Cause!"}}],"data":{"completaAttivita":null},"status":200,"headers":{"map":{"content-length":"398","content-type":"application/json"}}},"request":{"query":"\\n mutation completaAttivita($taskName: TipoAttivita, $taskId: String, $determinaId: BigInteger, $revisione: Boolean, $nota: NotaInputInput, $avanzaStatoDetermina: Boolean, $attribuzioniOrizzontali: AttribuzioniOrizzontaliInputInput, $firmaInput: FirmaInputInput, $roles: [String]) {\\n completaAttivita(\\n taskName: $taskName\\n taskId: $taskId\\n determinaId: $determinaId\\n revisione: $revisione\\n nota: $nota\\n avanzaStatoDetermina: $avanzaStatoDetermina\\n attribuzioniOrizzontali: $attribuzioniOrizzontali\\n firmaInput: $firmaInput\\n roles: $roles\\n ) {\\n id\\n }\\n}\\n ","variables":{"taskId":"24ac495b-46ca-42f4-9be2-fd92f0398114","determinaId":1342,"taskName":"firmaDirigente","firmaInput":{"username":"fdfs","password":"fdsf","otp":"fdsdf"}}}}\n at eval (webpack-internal:///../../node_modules/graphql-request/dist/index.js:354:31)\n at step (webpack-internal:///../../node_modules/graphql-request/dist/index.js:63:23)\n at Object.eval [as next] (webpack-internal:///../../node_modules/graphql-request/dist/index.js:44:53)\n at fulfilled (webpack-internal:///../../node_modules/graphql-request/dist/index.js:35:58)'
};
But my graphql endpoint return this
{
errors: [
{
message: "System error",
locations: [{ line: 3, column: 3 }],
path: ["completaAttivita"],
extensions: {
errorCode: 505,
classification: "VALIDATION",
errorMessage: "Messaggio di errore",
verboseErrorMessage:
"it.cmrc.sid.backend.exception.CustomException: Messaggio di errore",
causedBy: "No Cause!"
}
}
],
data: { completaAttivita: null }
};
Using rtk-query and the autogenerated client i have no access to the complete response from server.
And i need to extract the error messagge in the exceptions object.
From redix-toolkit documentation i understand that i need to catch the error and call rejectwithvalue() from a createAsyncThunk but i dont'undertand of to do that.
Here the base api object
import { createApi } from '#reduxjs/toolkit/query/react';
import { graphqlRequestBaseQuery } from './base-request';
import { GraphQLClient } from 'graphql-request';
import { getSession } from 'next-auth/react';
export const client = new GraphQLClient(
`${process.env.NEXT_PUBLIC_API_URL}/graphql`,
{
credentials: 'same-origin',
headers: {
Accept: 'application/json'
}
}
);
export const api = createApi({
baseQuery: graphqlRequestBaseQuery({
client,
prepareHeaders: async (headers, { getState }) => {
const session = await getSession();
if (session) {
headers.set('Authorization', `Bearer ${session?.access_token}`);
}
return headers;
}
}),
endpoints: () => ({}),
refetchOnMountOrArgChange: true
});
Thanks to #phry for merge my solution.
#rtk-query/graphql-request-base-query (version > 2.1.0) introduce a new configuration to handle errors format. Here a small explanation.
Typization
graphqlRequestBaseQuery<CustomErrorFormat>
Custom Error handler
...
customErrors: (props: ClientError) => CustomErrorFormat
...
Full example https://codesandbox.io/s/headless-microservice-uzujqb?file=/src/App.tsx
import { createApi } from '#reduxjs/toolkit/query/react';
import { graphqlRequestBaseQuery } from '#rtk-query/graphql-request-base-query';
import { ClientError, GraphQLClient } from 'graphql-request';
import { getSession } from 'next-auth/react';
export const client = new GraphQLClient(
`${process.env.NEXT_PUBLIC_API_URL}/graphql`,
{
credentials: 'same-origin',
headers: {
Accept: 'application/json'
}
}
);
export const api = createApi({
baseQuery: graphqlRequestBaseQuery<
Partial<ClientError & { errorCode: number }>
>({
client,
prepareHeaders: async (headers, { getState }) => {
const session = await getSession();
if (session) {
headers.set('Authorization', `Bearer ${session?.access_token}`);
}
return headers;
},
customErrors: ({ name, stack, response }) => {
const { errorMessage = '', errorCode = 500 } = response?.errors?.length
? response?.errors[0]?.extensions
: {};
return {
name,
message: errorMessage,
errorCode,
stack
};
}
}),
endpoints: () => ({}),
refetchOnMountOrArgChange: true
});
You can always write a wrapper around your baseQuery to reformat it:
const originalBaseQuery = graphqlRequestBaseQuery(...)
const wrappedBaseQuery = async (...args) => {
const result = await originalBaseQuery(...args);
if (result.error) {
// modify `result.error` here however you want
}
return result
}
It could also be necessary that you need to try..catch for that:
const originalBaseQuery = graphqlRequestBaseQuery(...)
const wrappedBaseQuery = async (...args) => {
try {
return await originalBaseQuery(...args);
} catch (e) {
// modify your error here
return { error: e.foo.bar }
}
}
I think this just slipped by when I was writing graphqlRequestBaseQuery and so far nobody has asked about it. If you have found a nice pattern of handling this, a pull request against graphqlRequestBaseQuery would also be very welcome.

Next-Auth with firebase Authentication

just wanna have my custom credential provider which authenticate the entered username and password with Firebase Authentication on sign in page
pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth"
import { getDatabase } from "firebase/database"
import { DB } from "../../../constants/firebase"
import { FirebaseAdapter } from "#next-auth/firebase-adapter"
import * as firestoreFunctions from "firebase/firestore"
import CredentialsProvider from "next-auth/providers/credentials"
export default NextAuth({
session: {
strategy: "database",
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: {
label: "Username",
type: "text",
placeholder: "somebody#gmail.com",
},
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
const database = getDatabase()
console.log(database)
const user = {
id: 1,
usename: "j",
password: "123456789",
}
if (
credentials?.username === user.usename &&
credentials.password === "123456789"
) {
return user
}
return null
},
}),
],
adapter: FirebaseAdapter({
db: DB,
...firestoreFunctions,
}),
// pages: {
// signIn: "/auth/signin",
// signOut: "/auth/signout",
// error: "/auth/error", // Error code passed in query string as ?error=
// verifyRequest: "/auth/verify-request", // (used for check email message)
// newUser: "/auth/new-user", // New users will be directed here on first sign in (leave the property out if not of interest)
// },
callbacks: {
async jwt({ token, user }) {
if (user) {
token.email = user.email
}
return token
},
async session({ session, token, user }) {
if (token) {
session.user!.email = token.email
}
return session
},
redirect({ url, baseUrl }) {
if (url.startsWith(baseUrl)) return url
else if (url.startsWith("/"))
return new URL(url, baseUrl).toString()
return baseUrl
},
},
})
firebase.ts
import { initializeApp, getApp, getApps } from "firebase/app"
import { getAnalytics } from "firebase/analytics"
import { getFirestore } from "#firebase/firestore"
import { getStorage } from "#firebase/storage"
import getFirebaseObject from "./firebaseConfig"
const app = !getApps.length ? initializeApp(getFirebaseObject()) : getApp()
const DB = getFirestore(app)
const storages = getStorage()
const analytics = getAnalytics(app)
export { app, DB, analytics, storages }
as you see
const user = {
id: 1,
usename: "j",
password: "123456789",
}
in fact except of these static data wanna search and get right user info from the Firebase
I know there are a some other way of doing this but I like working with next-auth for last change wanna make sure there's a spot of light in this was ;)
i found this public repository where the author does something similar to what you want to achieve, which is create a custom token with your database credentials.
May be this repository can help you. It has a few errors, but it gave me a general idea about what to do, as I had a similar case.
try {
if (user !== null) {
await customTokenSignIn(user.id, user.email);
(await getUser(user.id)) ??
(await createUser(toReqUser(user, account)));
const data = await getUser(user.id);
setResUser(user, data as ResUser);
return true;
}
return false;
} catch (e) {
console.error(e);
return false;
}
const customTokenSignIn = async (id: string, email: string) => {
const hash = toHash(id);
const customToken = await adminAuth.createCustomToken(hash);
await auth.signInWithCustomToken(customToken).then((res) => {
res.user?.updateEmail(email);
});
await adminAuth.setCustomUserClaims(hash, { sid: id });
await createUserToken({ id: id, firebaseUid: hash });
};

How to mock a method on a non-default exported class?

Code under test
// imports
const router = express.Router()
// This is what needs to be mocked
const client = new AwesomeGraphQLClient({
endpoint: process.env.GRAPHCMS_URL || '',
fetch,
fetchOptions: {
headers: {
authorization: `Bearer ${process.env.GRAPHCMS_TOKEN}`
}
}
})
interface LoginRequest {
email: string
password: string
}
router.post(
'/login',
async (req: Request<{}, {}, LoginRequest>, res: Response) => {
try {
const JWT_SECRET = getEnvironment('JWT_SECRET')
const { email, password } = req.body
if (!email || !password) {
res.status(400).json({
message: 'auth.provide.credentials',
full: 'You should provide an email and password'
})
return
}
if (!JWT_SECRET) {
res.status(500).json({
message: 'auth.secret.not.found',
full: 'Secret not found'
})
// TODO error logging
return
}
const { appUsers } = await client.request<
GetUserByEmailResponse,
GetUserByEmailVariables
>(getUserByEmailQuery, {
email
})
if (appUsers.length === 0) {
res.status(404).json({
message: 'auth.wrong.credentials',
full: 'You provided wrong credentials'
})
return
}
const user = appUsers[0]
const result: boolean = await bcrypt.compare(password, user.password)
if (result) {
var token = jwt.sign({ id: user.id, email: user.email }, JWT_SECRET)
res.status(200).json({
token
})
return
}
res.status(200).json({
message: 'auth.wrong.credentials',
full: 'You provided wrong credentials in the end'
})
} catch (e) {
console.log('E', e)
const error: ErrorObject = handleError(e)
res.status(error.code).json(error)
}
}
)
Tests for code above
import request from 'supertest'
import app from '../../../app'
import { mocked } from 'ts-jest/utils'
import { compare } from 'bcrypt'
import { AwesomeGraphQLClient } from 'awesome-graphql-client'
const mockRequestFn = jest.fn().mockReturnValue({
appUsers: [
{
id: 'tests'
}
]
})
jest.mock('awesome-graphql-client', () => ({
AwesomeGraphQLClient: jest.fn().mockImplementation(() => ({
request: mockRequestFn
}))
}))
I am trying to mock a method on a non default exported class from Awesome GraphQL. I also want to spy on this method, so I created a separate jest.fn() with a return value. The problem is that request is not a function: TypeError: client.request is not a function.
How can I mock and spy on the method of a mocked non default exported class?
SOLUTION
Managed to find a workaround. Make the method a function that returns the called mockRequest. This way you can spy on AwesomeGraphQLClient.request with mockRequest.toHaveBeenCalledTimes(x).
let mockRequest = jest.fn().mockReturnValue({
appUsers: [
{
id: 'tests'
}
]
})
jest.mock('awesome-graphql-client', () => {
return {
AwesomeGraphQLClient: jest.fn().mockImplementation(() => {
return {
request: () => mockRequest()
}
})
}
})

Sending verification email with Firebase and React Native

I am trying to send the validation email upon the account registration, using firebase. The registration is being done successfully but whenever I try to code email verification it gives me an error. Probably because I don't know where to place it. All my firebase methods are on Fire.js, which are the following:
import firebaseKeys from './Config';
import firebase from 'firebase';
require("firebase/firestore");
class Fire {
constructor() {
if (!firebase.apps.length) {
firebase.initializeApp(firebaseKeys);
}
}
addPost = async ({ text, localUri }) => {
const remoteUri = await this.uploadPhotoAsync(localUri, 'photos/${this.uid}/${Date.now()}');
return new Promise((res, rej) => {
this.firestore.collection('posts').add({
text,
uid: this.uid,
timestamp: this.timestamp,
image: remoteUri
})
.then(ref => {
res(ref);
})
.catch(error => {
rej(error);
});
});
}
uploadPhotoAsync = async (uri, filename) => {
return new Promise(async (res, rej) => {
const response = await fetch(uri);
const file = await response.blob();
let upload = firebase
.storage()
.ref(filename)
.put(file);
upload.on(
"state_changed",
snapshot => {},
err => {
rej(err);
},
async () => {
const url = await upload.snapshot.ref.getDownloadURL();
res(url);
}
);
});
}
createUser = async user => {
let remoteUri = null
try {
await firebase.auth().createUserWithEmailAndPassword(user.email, user.password)
//I tried to code it here with user.sendEmailVerification();
let db = this.firestore.collection("users").doc(this.uid)
db.set({
name: user.name,
email: user.email,
avatar: null
})
if (user.avatar) {
remoteUri = await this.uploadPhotoAsync(user.avatar, 'avatars/${this.uid}')
db.set({avatar: remoteUri}, {merge: true});
}
} catch (error) {
alert("Error: ", error);
}
};
get firestore() {
return firebase.firestore();
}
get uid() {
return (firebase.auth().currentUser || {}).uid;
}
get timestamp() {
return Date.now();
}
}
Fire.shared = new Fire();
export default Fire;
The createUserWithEmailAndPassword() method returns a Promise which resolves with a UserCredential AND (as the the doc indicates) "on successful creation of the user account, this user will also be signed in to your application."
So you can easily get the signed in user by using the user property of the UserCredential, and call the sendEmailVerification() method, as follows:
try {
const userCredential = await firebase.auth().createUserWithEmailAndPassword(user.email, user.password);
await userCredential.user.sendEmailVerification();
//In the next line, you should most probably use userCredential.user.uid as the ID of the Firestore document (instead of this.uid)
cont db = this.firestore.collection("users").doc(this.uid);
//...
} catch (...)
Note that you may pass an ActionCodeSettings object to the sendEmailVerification() method, see the doc.

Resources