-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #304 from TheCodeGhinux/chore/db
chore: database schema and typeorm setup
- Loading branch information
Showing
14 changed files
with
1,436 additions
and
135 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,17 @@ | ||
import HealthController from "./health.controller"; | ||
import HealthController from './health.controller'; | ||
|
||
describe('Health Check Test', () => { | ||
let healthController: HealthController; | ||
let healthController: HealthController; | ||
|
||
beforeEach(() => { | ||
healthController = new HealthController(); | ||
}); | ||
beforeEach(() => { | ||
healthController = new HealthController(); | ||
}); | ||
|
||
describe('Get Health endpoint', () => { | ||
it('should return healthy endpoint', async () => { | ||
const result = 'healthy endpoint'; | ||
describe('Get Health endpoint', () => { | ||
it('should return healthy endpoint', async () => { | ||
const result = 'healthy endpoint'; | ||
|
||
expect(await healthController.health()).toBe(result); | ||
}); | ||
expect(await healthController.health()).toBe(result); | ||
}); | ||
|
||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { DataSource, DataSourceOptions } from 'typeorm'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import * as dotenv from 'dotenv'; | ||
|
||
dotenv.config(); | ||
|
||
const isDevelopment = process.env.NODE_ENV === 'development'; | ||
|
||
const dataSource = new DataSource({ | ||
type: process.env.DB_TYPE as 'postgres', | ||
username: process.env.DB_USERNAME, | ||
password: process.env.DB_PASSWORD, | ||
host: process.env.DB_HOST, | ||
database: process.env.DB_DATABASE, | ||
entities: [process.env.DB_ENTITIES], | ||
migrations: [process.env.DB_MIGRATIONS], | ||
synchronize: isDevelopment, | ||
migrationsTableName: 'migrations', | ||
}); | ||
|
||
export async function initializeDataSource() { | ||
if (!dataSource.isInitialized) { | ||
await dataSource.initialize(); | ||
} | ||
return dataSource; | ||
} | ||
|
||
export default dataSource; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { Controller, Get, Post } from '@nestjs/common'; | ||
import { SeedingService } from './seeding.service'; | ||
|
||
@Controller('seed') | ||
export class SeedingController { | ||
constructor(private readonly seedingService: SeedingService) {} | ||
|
||
@Post() | ||
async seedDatabase() { | ||
await this.seedingService.seedDatabase(); | ||
return { message: 'Database seeding initiated' }; | ||
} | ||
|
||
@Get('users') | ||
async getUsers() { | ||
return this.seedingService.getUsers(); | ||
} | ||
|
||
@Get('profiles') | ||
async getProfiles() { | ||
return this.seedingService.getProfiles(); | ||
} | ||
|
||
@Get('products') | ||
async getProducts() { | ||
return this.seedingService.getProducts(); | ||
} | ||
|
||
@Get('organisations') | ||
async getOrganisations() { | ||
return this.seedingService.getOrganisations(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { SeedingService } from './seeding.service'; | ||
import { SeedingController } from './seeding.controller'; | ||
import { TypeOrmModule } from '@nestjs/typeorm'; | ||
import { User } from 'src/entities/user.entity'; | ||
import { Profile } from 'src/entities/profile.entity'; | ||
import { Product } from 'src/entities/product.entity'; | ||
import { Organisation } from 'src/entities/organisation.entity'; | ||
|
||
@Module({ | ||
imports: [TypeOrmModule.forFeature([User, Profile, Product, Organisation])], | ||
providers: [SeedingService], | ||
controllers: [SeedingController], | ||
}) | ||
export class SeedingModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { DataSource } from 'typeorm'; | ||
import { User } from 'src/entities/user.entity'; | ||
import { Profile } from 'src/entities/profile.entity'; | ||
import { Product } from 'src/entities/product.entity'; | ||
import { Organisation } from 'src/entities/organisation.entity'; | ||
|
||
@Injectable() | ||
export class SeedingService { | ||
constructor(private readonly dataSource: DataSource) {} | ||
|
||
async seedDatabase() { | ||
const userRepository = this.dataSource.getRepository(User); | ||
|
||
try { | ||
const existingUsers = await userRepository.count(); | ||
if (existingUsers > 0) { | ||
console.log('Database is already populated. Skipping seeding.'); | ||
return; | ||
} | ||
|
||
const queryRunner = this.dataSource.createQueryRunner(); | ||
await queryRunner.connect(); | ||
await queryRunner.startTransaction(); | ||
|
||
try { | ||
const profileRepository = this.dataSource.getRepository(Profile); | ||
const productRepository = this.dataSource.getRepository(Product); | ||
const organisationRepository = this.dataSource.getRepository(Organisation); | ||
|
||
const u1 = userRepository.create({ | ||
first_name: 'John', | ||
last_name: 'Smith', | ||
email: '[email protected]', | ||
password: 'password', | ||
}); | ||
const u2 = userRepository.create({ | ||
first_name: 'Jane', | ||
last_name: 'Smith', | ||
email: '[email protected]', | ||
password: 'password', | ||
}); | ||
|
||
await userRepository.save([u1, u2]); | ||
|
||
const savedUsers = await userRepository.find(); | ||
if (savedUsers.length !== 2) { | ||
throw new Error('Failed to create all users'); | ||
} | ||
|
||
const p1 = profileRepository.create({ | ||
username: 'johnsmith', | ||
bio: 'bio data', | ||
phone: '1234567890', | ||
avatar_image: 'image.png', | ||
user: savedUsers[0], | ||
}); | ||
const p2 = profileRepository.create({ | ||
username: 'janesmith', | ||
bio: 'bio data', | ||
phone: '0987654321', | ||
avatar_image: 'image.png', | ||
user: savedUsers[1], | ||
}); | ||
|
||
await profileRepository.save([p1, p2]); | ||
|
||
const savedProfiles = await profileRepository.find(); | ||
if (savedProfiles.length !== 2) { | ||
throw new Error('Failed to create all profiles'); | ||
} | ||
|
||
const pr1 = productRepository.create({ | ||
product_name: 'Product 1', | ||
description: 'Description 1', | ||
product_price: 100, | ||
user: savedUsers[0], | ||
}); | ||
const pr2 = productRepository.create({ | ||
product_name: 'Product 2', | ||
description: 'Description 2', | ||
product_price: 200, | ||
user: savedUsers[1], | ||
}); | ||
|
||
await productRepository.save([pr1, pr2]); | ||
|
||
const savedProducts = await productRepository.find(); | ||
if (savedProducts.length !== 2) { | ||
throw new Error('Failed to create all products'); | ||
} | ||
|
||
const or1 = organisationRepository.create({ | ||
org_name: 'Org 1', | ||
description: 'Description 1', | ||
users: savedUsers, | ||
}); | ||
const or2 = organisationRepository.create({ | ||
org_name: 'Org 2', | ||
description: 'Description 2', | ||
users: [savedUsers[0]], | ||
}); | ||
|
||
await organisationRepository.save([or1, or2]); | ||
|
||
const savedOrganisations = await organisationRepository.find(); | ||
if (savedOrganisations.length !== 2) { | ||
throw new Error('Failed to create all organisations'); | ||
} | ||
|
||
await queryRunner.commitTransaction(); | ||
} catch (error) { | ||
await queryRunner.rollbackTransaction(); | ||
console.error('Seeding failed:', error.message); | ||
} finally { | ||
await queryRunner.release(); | ||
} | ||
} catch (error) { | ||
console.error('Error while checking for existing data:', error.message); | ||
} | ||
} | ||
|
||
async getUsers(): Promise<User[]> { | ||
try { | ||
return this.dataSource.getRepository(User).find({ relations: ['profile', 'products', 'organisations'] }); | ||
} catch (error) { | ||
console.error('Error fetching users:', error.message); | ||
throw error; | ||
} | ||
} | ||
|
||
async getProfiles(): Promise<Profile[]> { | ||
try { | ||
return this.dataSource.getRepository(Profile).find({ relations: ['user'] }); | ||
} catch (error) { | ||
console.error('Error fetching profiles:', error.message); | ||
throw error; | ||
} | ||
} | ||
|
||
async getProducts(): Promise<Product[]> { | ||
try { | ||
return this.dataSource.getRepository(Product).find({ relations: ['user'] }); | ||
} catch (error) { | ||
console.error('Error fetching products:', error.message); | ||
throw error; | ||
} | ||
} | ||
|
||
async getOrganisations(): Promise<Organisation[]> { | ||
try { | ||
return this.dataSource.getRepository(Organisation).find({ relations: ['users'] }); | ||
} catch (error) { | ||
console.error('Error fetching organisations:', error.message); | ||
throw error; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm'; | ||
import { User } from './user.entity'; | ||
|
||
@Entity() | ||
export class Organisation { | ||
@PrimaryGeneratedColumn('uuid') | ||
org_id: string; | ||
|
||
@Column({ nullable: false }) | ||
org_name: string; | ||
|
||
@Column({ nullable: false }) | ||
description: string; | ||
|
||
@ManyToMany(() => User, user => user.organisations) | ||
users: User[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; | ||
import { User } from './user.entity'; | ||
|
||
@Entity() | ||
export class Product { | ||
@PrimaryGeneratedColumn('uuid') | ||
id: string; | ||
|
||
@Column({ nullable: false }) | ||
product_name: string; | ||
|
||
@Column({ nullable: false }) | ||
product_price: number; | ||
|
||
@Column({ nullable: false }) | ||
description: string; | ||
|
||
@ManyToOne(() => User, user => user.products) | ||
@JoinColumn({ name: 'user_id' }) | ||
user: User; | ||
} |
Oops, something went wrong.