Progress on API routes
This commit is contained in:
43
README.md
43
README.md
@@ -1,29 +1,9 @@
|
|||||||
<p align="center">
|
# Glossary API
|
||||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
## Stack
|
||||||
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
|
||||||
|
|
||||||
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
- [Nest](https://github.com/nestjs/nest)
|
||||||
<p align="center">
|
- TypeORM
|
||||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
|
||||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
|
||||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
|
||||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
|
||||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
|
||||||
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
|
||||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
|
||||||
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
|
|
||||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
|
||||||
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
|
|
||||||
</p>
|
|
||||||
<!--[](https://opencollective.com/nest#backer)
|
|
||||||
[](https://opencollective.com/nest#sponsor)-->
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
|
||||||
|
|
||||||
## Project setup
|
## Project setup
|
||||||
|
|
||||||
@@ -31,9 +11,18 @@
|
|||||||
$ npm install
|
$ npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Running local infrastructure
|
||||||
|
|
||||||
|
To simplify development, all local infrastructure is handled with Docker. The folder `dev_infra` contains the folloeing:
|
||||||
|
|
||||||
|
- `compose.yaml`: A Docker compose file that stands up a Postgres database and a pgAdmin instance to visualize.
|
||||||
|
|
||||||
## Compile and run the project
|
## Compile and run the project
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Set env variables
|
||||||
|
$ . ./set-env
|
||||||
|
|
||||||
# development
|
# development
|
||||||
$ npm run start
|
$ npm run start
|
||||||
|
|
||||||
@@ -87,12 +76,6 @@ Check out a few resources that may come in handy when working with NestJS:
|
|||||||
|
|
||||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||||
|
|
||||||
## Stay in touch
|
|
||||||
|
|
||||||
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
|
||||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
|
||||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
|
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
|
||||||
|
|||||||
31
dev_infra/compose.yaml
Normal file
31
dev_infra/compose.yaml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
services:
|
||||||
|
# PostgreSQL database
|
||||||
|
postgresql_db:
|
||||||
|
image: postgres:latest
|
||||||
|
container_name: postgresql-db
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "5432:5432" # Default PostgreSQL port
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: glossary_dev_user
|
||||||
|
POSTGRES_DB: glossary_dev_db
|
||||||
|
POSTGRES_PASSWORD: glossary_dev_password
|
||||||
|
volumes:
|
||||||
|
- postgresql_db_data:/var/lib/postgresql # Docker managed volume
|
||||||
|
|
||||||
|
# pgAdmin4 Web UI for PostgreSQL database
|
||||||
|
pgadmin:
|
||||||
|
image: dpage/pgadmin4:latest
|
||||||
|
container_name: pgadmin
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8888:80" # Expose Web UI to port 8888
|
||||||
|
environment:
|
||||||
|
PGADMIN_DEFAULT_EMAIL: admin@example.com
|
||||||
|
PGADMIN_DEFAULT_PASSWORD: postgresql
|
||||||
|
volumes:
|
||||||
|
- pgadmin_data:/var/lib/pgadmin # Docker managed volume
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgresql_db_data:
|
||||||
|
pgadmin_data:
|
||||||
747
package-lock.json
generated
747
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -17,14 +17,26 @@
|
|||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:cov": "jest --coverage",
|
"test:cov": "jest --coverage",
|
||||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||||
|
"typeorm": "ts-node ./node_modules/typeorm/cli.js",
|
||||||
|
"db:gen_migration": "ts-node ./node_modules/typeorm/cli.js migration:generate src/migrations/migration -d src/appDataSource.ts",
|
||||||
|
"db:gen_migration_named": "ts-node ./node_modules/typeorm/cli.js migration:generate src/migrations/$npm_config_name -d src/appDataSource.ts",
|
||||||
|
"db:show_migrations": "ts-node ./node_modules/typeorm/cli.js migration:show -d src/appDataSource.ts",
|
||||||
|
"db:run_migrations": "ts-node ./node_modules/typeorm/cli.js migration:run -d src/appDataSource.ts",
|
||||||
|
"db:run_migrations_prod": "ts-node ./node_modules/typeorm/cli.js migration:run -d dist/appDataSource.ts"
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^11.0.1",
|
"@nestjs/common": "^11.0.1",
|
||||||
|
"@nestjs/config": "^4.0.2",
|
||||||
"@nestjs/core": "^11.0.1",
|
"@nestjs/core": "^11.0.1",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
|
"@nestjs/typeorm": "^11.0.0",
|
||||||
|
"pg": "^8.18.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1"
|
"rxjs": "^7.8.1",
|
||||||
|
"typeorm": "^0.3.28"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/eslintrc": "^3.2.0",
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
|
|||||||
8
set-env.sh
Normal file
8
set-env.sh
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export DB_TYPE=postgres
|
||||||
|
export DB_HOST=localhost
|
||||||
|
export DB_PORT=5432
|
||||||
|
export DB_USERNAME=glossary_dev_user
|
||||||
|
export DB_PASSWORD=glossary_dev_password
|
||||||
|
export DB_NAME=glossary_dev_db
|
||||||
|
|
||||||
|
npm run db:run_migrations
|
||||||
@@ -1,10 +1,25 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
import { dataSourceOptions } from './appDataSource';
|
||||||
|
import { EntryModule } from './entries/entries.module';
|
||||||
|
import { DataSource, EntityManager } from 'typeorm';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [
|
||||||
|
ConfigModule.forRoot(),
|
||||||
|
TypeOrmModule.forRoot(dataSourceOptions),
|
||||||
|
EntryModule
|
||||||
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {
|
||||||
|
constructor(
|
||||||
|
private dataSource: DataSource,
|
||||||
|
private entityManager: EntityManager,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|||||||
17
src/appDataSource.ts
Normal file
17
src/appDataSource.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { DataSource, DataSourceOptions } from "typeorm";
|
||||||
|
import { Entry } from "./entries/entries.entity";
|
||||||
|
import { Initial1770020781006 } from "./migrations/1770020781006-initial";
|
||||||
|
|
||||||
|
export const dataSourceOptions: DataSourceOptions = {
|
||||||
|
type: "postgres",
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: parseInt(process.env.DB_PORT as string, 10) || 5432,
|
||||||
|
username: process.env.DB_USERNAME,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_NAME,
|
||||||
|
entities: [Entry],
|
||||||
|
migrations: [Initial1770020781006],
|
||||||
|
synchronize: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const appDataSource = new DataSource(dataSourceOptions)
|
||||||
18
src/entries/entries.controller.spec.ts
Normal file
18
src/entries/entries.controller.spec.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { EntriesController } from './entries.controller';
|
||||||
|
|
||||||
|
describe('EntriesController', () => {
|
||||||
|
let controller: EntriesController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [EntriesController],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
controller = module.get<EntriesController>(EntriesController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
22
src/entries/entries.controller.ts
Normal file
22
src/entries/entries.controller.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Body, Controller, Delete, Inject, Param, Post } from '@nestjs/common';
|
||||||
|
import { EntryService } from './entries.service';
|
||||||
|
import { EntryDTO } from './entries.dto';
|
||||||
|
|
||||||
|
@Controller('entries')
|
||||||
|
export class EntriesController {
|
||||||
|
constructor(
|
||||||
|
@Inject(EntryService)
|
||||||
|
private readonly entryService: EntryService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
async saveEntry(@Body() entry: EntryDTO) {
|
||||||
|
return (await this.entryService.save(entry)).uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(":uuid")
|
||||||
|
async softDelete(@Param("uuid") uuid: string): Promise<void>
|
||||||
|
{
|
||||||
|
await this.entryService.softDeleteByUuid(uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/entries/entries.dto.ts
Normal file
8
src/entries/entries.dto.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Entry } from "./entries.entity";
|
||||||
|
|
||||||
|
export class EntryDTO implements Partial<Entry> {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
source?: string;
|
||||||
|
sourceUrl?: string;
|
||||||
|
}
|
||||||
23
src/entries/entries.entity.ts
Normal file
23
src/entries/entries.entity.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Column, Entity, Generated, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity({ name: "entities"})
|
||||||
|
export class Entry {
|
||||||
|
@Column()
|
||||||
|
@Generated("uuid")
|
||||||
|
uuid?: string;
|
||||||
|
|
||||||
|
@PrimaryGeneratedColumn("increment")
|
||||||
|
id?: number;
|
||||||
|
|
||||||
|
@Column({ type: String, default: "" })
|
||||||
|
title?: string;
|
||||||
|
|
||||||
|
@Column({ type: String, default: "" })
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@Column({ type: String, default: "" })
|
||||||
|
source?: string;
|
||||||
|
|
||||||
|
@Column({ type: String, default: "" })
|
||||||
|
sourceUrl?: string;
|
||||||
|
}
|
||||||
15
src/entries/entries.module.ts
Normal file
15
src/entries/entries.module.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||||
|
import { Entry } from "./entries.entity";
|
||||||
|
import { EntryService } from "./entries.service";
|
||||||
|
import { EntriesController } from "./entries.controller";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([Entry]),
|
||||||
|
],
|
||||||
|
exports: [EntryService],
|
||||||
|
providers: [EntryService],
|
||||||
|
controllers: [EntriesController],
|
||||||
|
})
|
||||||
|
export class EntryModule {}
|
||||||
36
src/entries/entries.service.ts
Normal file
36
src/entries/entries.service.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
|
import { InjectRepository } from "@nestjs/typeorm";
|
||||||
|
import { Entry } from "./entries.entity";
|
||||||
|
import { Repository } from "typeorm";
|
||||||
|
import { EntryDTO } from "./entries.dto";
|
||||||
|
import EntryNotFoundException from "./exceptions/entryNotFound.exception";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EntryService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Entry)
|
||||||
|
private entryRepository: Repository<Entry>
|
||||||
|
) {}
|
||||||
|
private readonly logger = new Logger(EntryService.name)
|
||||||
|
|
||||||
|
async save(data: EntryDTO): Promise<Entry> {
|
||||||
|
this.logger.log("Creating entry")
|
||||||
|
const lEntry = new Entry();
|
||||||
|
lEntry.description = data.description
|
||||||
|
lEntry.source = data.source
|
||||||
|
lEntry.sourceUrl = data.sourceUrl
|
||||||
|
lEntry.title = data.title
|
||||||
|
await this.entryRepository.save(lEntry)
|
||||||
|
return lEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
async softDeleteByUuid(uuid: string): Promise<void> {
|
||||||
|
this.logger.log(`Soft deleting entry with UUID ${uuid}`)
|
||||||
|
const deleteResponse = await this.entryRepository.softDelete({
|
||||||
|
uuid: uuid
|
||||||
|
})
|
||||||
|
if (!deleteResponse.affected) {
|
||||||
|
throw new EntryNotFoundException(uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/entries/exceptions/entryNotFound.exception.ts
Normal file
9
src/entries/exceptions/entryNotFound.exception.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { NotFoundException } from "@nestjs/common";
|
||||||
|
|
||||||
|
class EntryNotFoundException extends NotFoundException {
|
||||||
|
constructor(uuid: string) {
|
||||||
|
super(`Entry with UUID ${uuid} not found`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EntryNotFoundException
|
||||||
14
src/migrations/1770020781006-initial.ts
Normal file
14
src/migrations/1770020781006-initial.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||||
|
|
||||||
|
export class Initial1770020781006 implements MigrationInterface {
|
||||||
|
name = 'Initial1770020781006'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE TABLE "entities" ("uuid" uuid NOT NULL DEFAULT uuid_generate_v4(), "id" SERIAL NOT NULL, "title" character varying NOT NULL DEFAULT '', "description" character varying NOT NULL DEFAULT '', "source" character varying NOT NULL DEFAULT '', "sourceUrl" character varying NOT NULL DEFAULT '', CONSTRAINT "PK_8640855ae82083455cbb806173d" PRIMARY KEY ("id"))`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP TABLE "entities"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user