Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup typeorm #7

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ extends:
- "eslint:recommended"
- "airbnb-typescript/base"
parserOptions:
project: './tsconfig.json'
project: "./tsconfig.json"
env:
browser: true
node: true
Expand All @@ -13,8 +13,9 @@ rules:
"@typescript-eslint/no-unused-vars":
- "error"
- argsIgnorePattern: "type|_*"
"@typescript-eslint/indent": "off"
class-methods-use-this: "off"
consistent-return: off
consistent-return: "off"
func-names: "off"
global-require: "off"
import/no-cycle: "off"
Expand Down
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @echang594
* @echang594 @jennymar
6 changes: 4 additions & 2 deletions .github/workflows/auto-comment.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
name: Auto Comment on PRs
on: pull_request
on:
pull_request:
types: [opened]
jobs:
run:
auto-comment:
runs-on: ubuntu-latest
steps:
- uses: wow-actions/auto-comment@v1
Expand Down
17 changes: 17 additions & 0 deletions DataSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Container from 'typedi';
import { DataSource } from 'typeorm';
import { models } from './models';

export const dataSource = new DataSource({
type: 'postgres',
host: process.env.RDS_HOST,
port: Number(process.env.RDS_PORT),
username: process.env.RDS_USER,
password: process.env.RDS_PASSWORD,
database: process.env.RDS_DATABASE,
entities: models,
synchronize: true, // DO NOT USE IN PRODUCTION, make migrations
});

// important for dependency injection for repositories
Container.set(DataSource, dataSource);
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
rds.acmucsd.local:
container_name: rds.acmucsd.local
rds.hackathon.acmucsd.local:
container_name: rds.hackathon.acmucsd.local
image: postgres:17
restart: always
ports:
Expand Down
2 changes: 1 addition & 1 deletion firebaseConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ const firebaseConfig = {
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

export { auth };
export { auth };
21 changes: 12 additions & 9 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { register, login } from './authService';

register('[email protected]', 'password')
.then((user) => {
console.log('registered user:', user);
});
import 'reflect-metadata';

// must import data source before using repositories
import { dataSource } from './DataSource';

login('[email protected]', 'mysecretpassword')
.then((user) => {
console.log('login user:', user);
dataSource
.initialize()
.then(() => {
console.log('created connection');
})
.catch((error) => {
console.log(error);
});

console.log('Hello World!');
43 changes: 43 additions & 0 deletions models/UserModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Column, Entity, PrimaryColumn } from 'typeorm';
import { ApplicationStatus, UserAccessType } from '../types/Enums';

@Entity()
export class UserModel {
@PrimaryColumn()
uid: string;

@Column()
email: string;

@Column()
firstName: string;

@Column()
lastName: string;

@Column({
type: 'enum',
enum: UserAccessType,
default: UserAccessType.STANDARD,
})
accessType: UserAccessType;

@Column({
type: 'enum',
enum: ApplicationStatus,
default: ApplicationStatus.NOT_SUBMITTED,
})
applicationStatus: ApplicationStatus;

public isRestricted(): boolean {
return this.accessType === UserAccessType.RESTRICTED;
}

public isManager(): boolean {
return this.accessType === UserAccessType.MANAGER;
}

public isAdmin(): boolean {
return this.accessType === UserAccessType.ADMIN;
}
}
3 changes: 3 additions & 0 deletions models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { UserModel } from './UserModel';

export const models = [UserModel];
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"main": "index.js",
"scripts": {
"build": "tsc",
"start": "nodemon index.ts",
"start": "node build/index.js",
"dev": "node -r ts-node/register --env-file=.env --watch index.ts",
"test": "jest --config jest.config.json --runInBand",
"release": "node build/index.js",
"lint": "eslint ./ --ext .ts",
Expand All @@ -14,20 +15,23 @@
"repository": "https://github.com/acmucsd/hackathon-portal.git",
"author": "Eric Chang <[email protected]>",
"dependencies": {
"dotenv": "^16.4.5",
"pg": "^8.13.1",
"reflect-metadata": "^0.2.2",
"typedi": "^0.10.0",
"typeorm": "^0.3.20",
"firebase": "^11.0.1"
},
"devDependencies": {
"@types/jest": "^29.5.13",
"@types/node": "^22.7.9",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.57.1",
"eslint-config-airbnb-typescript": "^18.0.0",
"eslint-plugin-import": "^2.31.0",
"jest": "^29.7.0",
"nodemon": "^3.0.2",
"ts-jest": "^29.2.5",
"ts-node-dev": "^2.0.0",
"ts-node": "^10.9.2",
"typescript": "^5.6.3"
}
}
24 changes: 24 additions & 0 deletions repositories/UserRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DataSource, In } from 'typeorm';
import { UserModel } from '../models/UserModel';
import Container from 'typedi';
import { Uid } from '../types/Internal';

export const UserRepository = Container.get(DataSource)
.getRepository(UserModel)
.extend({
async findAll(): Promise<UserModel[]> {
return this.find();
},

async findByUid(uid: Uid): Promise<UserModel | null> {
return this.findOneBy({ uid });
},

async findByEmail(email: string): Promise<UserModel | null> {
return this.findOneBy({ email });
},

async findByEmails(emails: string[]): Promise<UserModel[]> {
return this.findBy({ email: In(emails) });
},
});
27 changes: 27 additions & 0 deletions repositories/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Service } from 'typedi';
import { DataSource, EntityManager } from 'typeorm';
import { UserRepository } from './UserRepository';

export class Repositories {
public static user(entityManager: EntityManager) {
return entityManager.withRepository(UserRepository);
}
}

@Service()
export class TransactionsManager {
private dataSource: DataSource;

constructor(dataSource: DataSource) {
this.dataSource = dataSource;
}

// add automatic retries if transaction failing often - see membership portal
public readOnly<T>(fn: (entityManager: EntityManager) => Promise<T>) {
return this.dataSource.transaction('REPEATABLE READ', fn);
}

public readWrite<T>(fn: (entityManager: EntityManager) => Promise<T>) {
return this.dataSource.transaction('SERIALIZABLE', fn);
}
}
21 changes: 21 additions & 0 deletions services/UserService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Service } from 'typedi';
import { Repositories, TransactionsManager } from '../repositories';
import { UserModel } from '../models/UserModel';
import { Uid } from '../types/Internal';

@Service()
export class UserService {
private transactionsManager: TransactionsManager;

constructor(transactionsManager: TransactionsManager) {
this.transactionsManager = transactionsManager;
}

public async findByUid(uid: Uid): Promise<UserModel> {
const user = await this.transactionsManager.readOnly(
async (entityManager) => Repositories.user(entityManager).findByUid(uid),
);
if (!user) throw new Error('User not found');
return user;
}
}
34 changes: 26 additions & 8 deletions authService.ts → tests/authService.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
import { auth } from './firebaseConfig';
import { createUserWithEmailAndPassword, signInWithEmailAndPassword,
sendPasswordResetEmail, UserCredential } from 'firebase/auth';
import { auth } from '../firebaseConfig';
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
sendPasswordResetEmail,
UserCredential,
} from 'firebase/auth';

export const register = async (email: string, password: string): Promise<UserCredential | void> => {
export const register = async (
email: string,
password: string,
): Promise<UserCredential | void> => {
try {
console.log('authService.ts');
const userCredential: UserCredential = await createUserWithEmailAndPassword(auth, email, password);
const userCredential: UserCredential = await createUserWithEmailAndPassword(
auth,
email,
password,
);
console.log('User registered:', userCredential.user);
return userCredential;
} catch (error) {
console.error('Error registering user:', error);
}
};

export const login = async (email: string, password: string): Promise<UserCredential | void> => {
export const login = async (
email: string,
password: string,
): Promise<UserCredential | void> => {
try {
const userCredential: UserCredential = await signInWithEmailAndPassword(auth, email, password);
const userCredential: UserCredential = await signInWithEmailAndPassword(
auth,
email,
password,
);
console.log('User logged in:', userCredential.user);
return userCredential;
} catch (error) {
Expand All @@ -30,4 +48,4 @@ export const resetPassword = async (email: string): Promise<void> => {
} catch (error) {
console.error('Error sending password reset email:', error);
}
};
};
3 changes: 3 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"declaration": true,
"outDir": "./build",
"strict": true,
"strictPropertyInitialization": false,
"skipLibCheck": true
},
"include": [
Expand Down
15 changes: 15 additions & 0 deletions types/Enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export enum UserAccessType {
RESTRICTED = 'RESTRICTED',
STANDARD = 'STANDARD',
MANAGER = 'MANAGER',
ADMIN = 'ADMIN',
}

export enum ApplicationStatus {
NOT_SUBMITTED = 'NOT_SUBMITTED',
SUBMITTED = 'SUBMITTED',
WITHDRAWN = 'WITHDRAWN',
ACCEPTED = 'ACCEPTED',
REJECTED = 'REJECTED',
ALL_DONE = 'ALL_DONE',
}
2 changes: 2 additions & 0 deletions types/Internal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type Uid = string;
export type Uuid = string;
Loading
Loading