How to perform a "where" query using denodb? - deno

I'm trying to register a user and I get an error:
[uncaught application error]: TypeError - Cannot read properties of undefined (reading 'where')
Here is the code:
async register(context: any) {
const body = JSON.parse(await context.request.body().value);
const existing = await Users.where("email", body.email).get();
if (existing.length) {
context.response.status = 400;
return (context.response.body = { message: "User already exists" });
}
const hashedPassword = await Users.hashPassword(body.password);
const user = await Users.create({
email: body.email,
hashedPassword,
});
context.response.body = { message: "User created" };
}
Here is my model:
// import { Model, DataTypes } from "https://deno.land/x/denodb/mod.ts";
import { DataTypes, Model } from "https://deno.land/x/denodb/mod.ts";
import * as bcrypt from "https://deno.land/x/bcrypt/mod.ts";
import {
create,
getNumericDate,
verify,
} from "https://deno.land/x/djwt/mod.ts";
import { JwtConfig } from "../middleware/jwt.ts";
import { db } from "../db.ts";
class Users extends Model {
static table = "users";
static timestamps = true;
static fields = {
id: {
primaryKey: true,
type: DataTypes.STRING,
},
email: {
type: DataTypes.STRING,
unique: true,
},
hashedPassword: {
type: DataTypes.STRING,
},
};
static defaults = {
id: crypto.randomUUID(),
};
// ...
static async hashPassword(password: string) {
const salt = await bcrypt.genSalt(8);
return bcrypt.hash(password, salt);
}
static generateJwt(id: string) {
// Create the payload with the expiration date (token have an expiry date) and the id of current user (you can add that you want)
const payload = {
id,
iat: getNumericDate(new Date()),
};
// return the generated token
return create({ alg: "HS512", typ: "JWT" }, payload, JwtConfig.secretKey);
}
}
//db.link([Users]);
//await db.sync();
export default Users;

Had to uncomment this:
db.link([Users]);

Related

next auth not passing all user info to the client

I am trying to have a role for the user in the session
This is what I get from session.user on the client :
{ "email": "test value" }
what I want to get :
{
"email": "test value",
"role": "user"
}
For some reason I can access the role on the server side but not on the client
[...nextauth].ts :
//..
const authOptions: NextAuthOptions = {
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
type: "credentials",
credentials: {},
async authorize(credentials, req) {
const { email, password } = credentials as {
email: string;
password: string;
};
const saltRounds = 10;
const db = path.join(process.cwd(), "db");
const users = JSON.parse(fs.readFileSync(db + "/users.json", "utf-8"));
type User = {
id: string;
email: string;
name: string;
role: "user" | "admin";
password: string;
};
for (let i = 0; i < users.length; i++) {
const e = users[i] as User;
const emailMatch = e.email === email;
if (emailMatch) {
const passwordMatch = bcrypt.compareSync(password, e.password);
if (passwordMatch) {
console.log("user loggedin", e);
return {
id: e.id,
email: e.email,
name: e.name,
role: e.role,
};
}
}
}
throw new Error("Invalid email or password");
},
}),
],
pages: {
signIn: "/auth/signin",
},
callbacks: {
jwt(params) {
if (params.user?.role) {
params.token.role = params.user.role;
}
console.log("jwt", params);
return params.token;
},
},
};
export default NextAuth(authOptions);
I have tried searching for how to do it and I dont see what's wrong with my code.
Here you are not setting the session you have to use the session callback to update the session from the returned token:
async jwt(params) {
if (params.user?.role) {
params.token.role = params.user.role;
}
if (params.user?.email) {
params.token.email = params.user.email;
}
return params.token;
},
async session({ session, token }) {
session.role = token.role;
session.email = token.email;
return session;
},
For some reason I can access the role on the server side but not on the client
that's the role from the token because you have added the property role to it now you have to add properties to your session

How to return a resolved promise from a module export?

import { DataTypes } from "sequelize";
import dbConnect from "./dbConnect";
async function UserModel() {
const sequelize = await dbConnect();
const User = sequelize.define(
"User",
{
userKey: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING(50),
allowNull: false,
},
username: {
type: DataTypes.STRING(50),
allowNull: false,
},
},
{
freezeTableName: true, // enforces that table name = model name
}
);
return User;
}
export default UserModel;
The above module returns the UserModel correctly, but I need to resolve it twice --
const users = await (await User()).findAll();
-- in the module that imports it b/c I'm calling a method that also returns a promise.
I'd like to call it like this --
const users = await User().findAll();
-- but everything I've tried has failed.
See usage here:
export async function getServerSideProps() {
const users = await (await User()).findAll();
return {
props: {
users,
},
};
}
Can you shed some light on how to do this?

Adding update property to mutation function breaks mocked result in MockProvider

I've got the following function that gets triggered on a form submission
const [register, { loading }] = useMutation(RegisterDocument);
const router = useRouter();
const onSubmit = async (values: FormValues) => {
const v = { ...values };
delete v.confirmPassword;
const res = await register({
variables: { options: v },
update: (cache, { data }) => {
cache.writeQuery<MeQuery>({
query: MeDocument,
data: {
__typename: 'Query',
me: data?.register.user,
},
});
},
});
if (res.data?.register.user) {
router.push('/');
}
};
I then have the following test to submit the form
test('it should submit form without error', async () => {
const firstName = faker.name.firstName();
const surname = faker.name.lastName();
const username = faker.internet.userName().replace('#', '');
const email = faker.internet.email();
const password = faker.internet.password(6, false, /^[a-zA-Z0-9_.-]*$/);
const cache = new InMemoryCache().restore({});
const variables = {
options: { email, firstName, password, surname, username },
};
const user = { email, firstName, surname, username, id: 1, activated: false, photo: null };
const mocks = [
{
request: { query: RegisterDocument, variables },
result: { data: { register: { errors: null, user } } },
},
];
const { queryByTestId, container } = renderWithTheme(
<MockedProvider mocks={mocks} cache={cache}>
<Register />
</MockedProvider>,
);
await updateRegisterInputs(container); // util function that updates input values for submission
await submitForm({ queryByTestId, testId: 'register-submit', loadingTestId: 'register-loading' }); // util function that submits form
await waitFor(() => expect(onPush).toBeCalledWith('/'));
});
When I run this test res returns the following
{ data: { register: {} } }
However, once I remove the update property inside the register mutation function, res returns the following.
{ data: { register: { errors: null, user: [Object] } } }
Any ideas why the mocked return value returns an empty object for the register property only when the update property function is added?
Even just instantiating the update property like so;
update: () => {}
still breaks the response from the mutation.
I realised that the graphql doc required the __typename property in the relevant places in my mocks
So I have to update the mock to include the typenames.
const user = { email, firstName, surname, username, id: 1, activated: false, photo: null, __typename: 'User' };
const mocks = [
{
request: { query: RegisterDocument, variables },
result: { data: { register: { errors: null, user, __typename: 'UserResponse' } } },
},
];

Sending data to an imported module in React Native

I have a module called Chat.js that imports Fire.js in order to send data (message comes into Chat.js, and Fire.js handles storage).
I have a recipient's user ID that is only currently available in Chat.js, but it is important to get to Fire.js in order to store appropriately.
I removed some info for brevity, this is my current Chat.js:
import Fire from './Fire';
class Chat extends React.Component<Props> {
state = {
messages: [],
};
get user() {
return {
name: this.props.navigation.state.params.name,
_id: Fire.shared.uid,
};
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={Fire.shared.send}
user={this.user}
/>
);
}
componentDidMount() {
Fire.shared.on(message =>
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, message),
}))
);
}
componentWillUnmount() {
Fire.shared.off();
}
}
export default Chat;
And this is my current Fire.js:
import firebase from 'react-native-firebase';
class Fire {
constructor() {
}
get ref() {
var recipient = 'recipientId'
return firebase.database().ref('messages/' + this.uid + '/' + recipient);
}
parse = snapshot => {
const { timestamp: numberStamp, text, user } = snapshot.val();
const { key: _id } = snapshot;
const timestamp = new Date(numberStamp);
const message = {
_id,
timestamp,
text,
user,
};
return message;
};
on = callback =>
this.ref
.limitToLast(20)
.on('child_added', snapshot => callback(this.parse(snapshot)));
// send the message to the Backend
send = messages => {
for (let i = 0; i < messages.length; i++) {
const { text, user } = messages[i];
const message = {
text,
user,
timestamp: this.timestamp,
};
this.append(message);
}
};
append = message => this.ref.push(message);
// close the connection to the Backend
off() {
this.ref.off();
}
}
Fire.shared = new Fire();
export default Fire;
I currently need to get the recipient ID, which is available in chat.js under
this.props.navigation.state.params.uid
Into the Fire.js lines:
get ref()
{
var recipient = 'recipientId'
I can't seem to get this uid into get ref()
Use getter and setters in Fire.js.
In Fire.js
setRecipient (id){
this.recipientId = id;
}
get getRecipientId () {
return this.recipientId;
}
And then call Fire.setRecipient(yourId) in Chat.js.

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

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?

Resources