From 24c184a19fe72a99c51f9bfc8cdc25d50b21df99 Mon Sep 17 00:00:00 2001 From: Jesse Desjardins Date: Mon, 2 Feb 2026 17:49:05 -0500 Subject: [PATCH] Created all initial routes for entries data entity --- src/appDataSource.ts | 3 +- src/entries/entries.controller.ts | 32 +++++++++++- src/entries/entries.dto.ts | 2 +- src/entries/entries.entity.ts | 15 ++++-- src/entries/entries.service.ts | 51 ++++++++++++++++++- src/migrations/1770020781006-initial.ts | 4 +- ...te_cols_and_rename_source_to_sourceText.ts | 22 ++++++++ 7 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 src/migrations/1770067407619-add_create_update_delete_cols_and_rename_source_to_sourceText.ts diff --git a/src/appDataSource.ts b/src/appDataSource.ts index 9ec78f1..ab2f974 100644 --- a/src/appDataSource.ts +++ b/src/appDataSource.ts @@ -1,6 +1,7 @@ import { DataSource, DataSourceOptions } from "typeorm"; import { Entry } from "./entries/entries.entity"; import { Initial1770020781006 } from "./migrations/1770020781006-initial"; +import { AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619 } from "./migrations/1770067407619-add_create_update_delete_cols_and_rename_source_to_sourceText"; export const dataSourceOptions: DataSourceOptions = { type: "postgres", @@ -10,7 +11,7 @@ export const dataSourceOptions: DataSourceOptions = { password: process.env.DB_PASSWORD, database: process.env.DB_NAME, entities: [Entry], - migrations: [Initial1770020781006], + migrations: [Initial1770020781006, AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619], synchronize: false, } diff --git a/src/entries/entries.controller.ts b/src/entries/entries.controller.ts index b34079d..9fc289e 100644 --- a/src/entries/entries.controller.ts +++ b/src/entries/entries.controller.ts @@ -1,6 +1,7 @@ -import { Body, Controller, Delete, Inject, Param, Post } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Inject, Param, Post, Put } from '@nestjs/common'; import { EntryService } from './entries.service'; import { EntryDTO } from './entries.dto'; +import { Entry } from './entries.entity'; @Controller('entries') export class EntriesController { @@ -9,14 +10,41 @@ export class EntriesController { private readonly entryService: EntryService ) {} + @Post() - async saveEntry(@Body() entry: EntryDTO) { + async saveEntry(@Body() entry: EntryDTO): Promise { return (await this.entryService.save(entry)).uuid } + @Put(":uuid") + async updateEntry(@Param("uuid") uuid: string, @Body() entry: EntryDTO): Promise { + return (await this.entryService.updateByUuid(uuid, entry)).uuid + } + + @Get() + async findAll(): Promise + { + const entries = await this.entryService.findAll() + return entries.sort((a, b) => { + return ( + new Date(b.created_at as Date).getTime() - new Date(a.created_at as Date).getTime() + ) + }) + } + + @Get(":uuid") + async findOneByUuid(@Param("uuid") uuid: string): Promise { + return await this.entryService.findOneByUuid(uuid) + } + @Delete(":uuid") async softDelete(@Param("uuid") uuid: string): Promise { await this.entryService.softDeleteByUuid(uuid) } + + @Put("/restore/:uuid") + async restoreSoftDeleted(@Param("uuid") uuid: string): Promise { + return await this.entryService.restoreDeletedByUuid(uuid) + } } diff --git a/src/entries/entries.dto.ts b/src/entries/entries.dto.ts index 3558b33..64ecf54 100644 --- a/src/entries/entries.dto.ts +++ b/src/entries/entries.dto.ts @@ -3,6 +3,6 @@ import { Entry } from "./entries.entity"; export class EntryDTO implements Partial { title?: string; description?: string; - source?: string; + sourceText?: string; sourceUrl?: string; } \ No newline at end of file diff --git a/src/entries/entries.entity.ts b/src/entries/entries.entity.ts index aee8627..1f476f9 100644 --- a/src/entries/entries.entity.ts +++ b/src/entries/entries.entity.ts @@ -1,6 +1,6 @@ -import { Column, Entity, Generated, PrimaryGeneratedColumn } from "typeorm"; +import { Column, CreateDateColumn, DeleteDateColumn, Entity, Generated, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm"; -@Entity({ name: "entities"}) +@Entity({ name: "entries"}) export class Entry { @Column() @Generated("uuid") @@ -16,8 +16,17 @@ export class Entry { description?: string; @Column({ type: String, default: "" }) - source?: string; + sourceText?: string; @Column({ type: String, default: "" }) sourceUrl?: string; + + @CreateDateColumn() + created_at?: Date; + + @UpdateDateColumn() + updated_at?: Date; + + @DeleteDateColumn() + deleted_at?: Date; } \ No newline at end of file diff --git a/src/entries/entries.service.ts b/src/entries/entries.service.ts index 5cfd18b..a78d1dc 100644 --- a/src/entries/entries.service.ts +++ b/src/entries/entries.service.ts @@ -17,13 +17,47 @@ export class EntryService { this.logger.log("Creating entry") const lEntry = new Entry(); lEntry.description = data.description - lEntry.source = data.source + lEntry.sourceText = data.sourceText lEntry.sourceUrl = data.sourceUrl lEntry.title = data.title await this.entryRepository.save(lEntry) return lEntry } + async updateByUuid(uuid: string, data: EntryDTO): Promise { + this.logger.log(`Updating entry with UUID ${uuid}`) + const oldEntry = await this.entryRepository.findOneBy({ uuid: uuid}) + if (!oldEntry) { + throw new EntryNotFoundException(uuid) + } else { + const lEntry = new Entry(); + // `id` is the primary key + lEntry.id = oldEntry.id + // These options can be updated + lEntry.description = data.description + lEntry.sourceText = data.sourceText + lEntry.sourceUrl = data.sourceUrl + lEntry.title = data.title + await this.entryRepository.save(lEntry) + return lEntry + } + } + + async findOneByUuid(uuid: string): Promise { + this.logger.log(`Returning entry with UUID ${uuid}`) + return this.entryRepository.findOneByOrFail({uuid: uuid}) + } + + async findAll(): Promise { + this.logger.log("Returning all entries") + return this.entryRepository.find() + } + + async findAllAndDeleted(): Promise { + this.logger.log("Returning all entries, active and deleted") + return this.entryRepository.find({ withDeleted: true }) + } + async softDeleteByUuid(uuid: string): Promise { this.logger.log(`Soft deleting entry with UUID ${uuid}`) const deleteResponse = await this.entryRepository.softDelete({ @@ -33,4 +67,19 @@ export class EntryService { throw new EntryNotFoundException(uuid) } } + + async restoreDeletedByUuid(uuid: string): Promise { + this.logger.log(`Restoring entry with UUID ${uuid}`) + const restoreResponse = await this.entryRepository.restore({ + uuid: uuid + }) + + if (!restoreResponse.affected) { + throw new EntryNotFoundException(uuid) + } else { + return this.entryRepository.findOneByOrFail({ + uuid: uuid + }) + } + } } \ No newline at end of file diff --git a/src/migrations/1770020781006-initial.ts b/src/migrations/1770020781006-initial.ts index e8508b2..e9d6ea5 100644 --- a/src/migrations/1770020781006-initial.ts +++ b/src/migrations/1770020781006-initial.ts @@ -4,11 +4,11 @@ export class Initial1770020781006 implements MigrationInterface { name = 'Initial1770020781006' public async up(queryRunner: QueryRunner): Promise { - 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"))`); + await queryRunner.query(`CREATE TABLE "entries" ("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 { - await queryRunner.query(`DROP TABLE "entities"`); + await queryRunner.query(`DROP TABLE "entries"`); } } diff --git a/src/migrations/1770067407619-add_create_update_delete_cols_and_rename_source_to_sourceText.ts b/src/migrations/1770067407619-add_create_update_delete_cols_and_rename_source_to_sourceText.ts new file mode 100644 index 0000000..94c76ce --- /dev/null +++ b/src/migrations/1770067407619-add_create_update_delete_cols_and_rename_source_to_sourceText.ts @@ -0,0 +1,22 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619 implements MigrationInterface { + name = 'AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "entries" DROP COLUMN "source"`); + await queryRunner.query(`ALTER TABLE "entries" ADD "sourceText" character varying NOT NULL DEFAULT ''`); + await queryRunner.query(`ALTER TABLE "entries" ADD "created_at" TIMESTAMP NOT NULL DEFAULT now()`); + await queryRunner.query(`ALTER TABLE "entries" ADD "updated_at" TIMESTAMP NOT NULL DEFAULT now()`); + await queryRunner.query(`ALTER TABLE "entries" ADD "deleted_at" TIMESTAMP`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "entries" DROP COLUMN "deleted_at"`); + await queryRunner.query(`ALTER TABLE "entries" DROP COLUMN "updated_at"`); + await queryRunner.query(`ALTER TABLE "entries" DROP COLUMN "created_at"`); + await queryRunner.query(`ALTER TABLE "entries" DROP COLUMN "sourceText"`); + await queryRunner.query(`ALTER TABLE "entries" ADD "source" character varying NOT NULL DEFAULT ''`); + } + +}