Created all initial routes for entries data entity

This commit is contained in:
2026-02-02 17:49:05 -05:00
parent 7bd0bbe2d8
commit 24c184a19f
7 changed files with 119 additions and 10 deletions

View File

@@ -1,6 +1,7 @@
import { DataSource, DataSourceOptions } from "typeorm"; import { DataSource, DataSourceOptions } from "typeorm";
import { Entry } from "./entries/entries.entity"; import { Entry } from "./entries/entries.entity";
import { Initial1770020781006 } from "./migrations/1770020781006-initial"; 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 = { export const dataSourceOptions: DataSourceOptions = {
type: "postgres", type: "postgres",
@@ -10,7 +11,7 @@ export const dataSourceOptions: DataSourceOptions = {
password: process.env.DB_PASSWORD, password: process.env.DB_PASSWORD,
database: process.env.DB_NAME, database: process.env.DB_NAME,
entities: [Entry], entities: [Entry],
migrations: [Initial1770020781006], migrations: [Initial1770020781006, AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619],
synchronize: false, synchronize: false,
} }

View File

@@ -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 { EntryService } from './entries.service';
import { EntryDTO } from './entries.dto'; import { EntryDTO } from './entries.dto';
import { Entry } from './entries.entity';
@Controller('entries') @Controller('entries')
export class EntriesController { export class EntriesController {
@@ -9,14 +10,41 @@ export class EntriesController {
private readonly entryService: EntryService private readonly entryService: EntryService
) {} ) {}
@Post() @Post()
async saveEntry(@Body() entry: EntryDTO) { async saveEntry(@Body() entry: EntryDTO): Promise<string | undefined> {
return (await this.entryService.save(entry)).uuid return (await this.entryService.save(entry)).uuid
} }
@Put(":uuid")
async updateEntry(@Param("uuid") uuid: string, @Body() entry: EntryDTO): Promise<string | undefined> {
return (await this.entryService.updateByUuid(uuid, entry)).uuid
}
@Get()
async findAll(): Promise<Entry[]>
{
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<Entry | null> {
return await this.entryService.findOneByUuid(uuid)
}
@Delete(":uuid") @Delete(":uuid")
async softDelete(@Param("uuid") uuid: string): Promise<void> async softDelete(@Param("uuid") uuid: string): Promise<void>
{ {
await this.entryService.softDeleteByUuid(uuid) await this.entryService.softDeleteByUuid(uuid)
} }
@Put("/restore/:uuid")
async restoreSoftDeleted(@Param("uuid") uuid: string): Promise<Entry | null> {
return await this.entryService.restoreDeletedByUuid(uuid)
}
} }

View File

@@ -3,6 +3,6 @@ import { Entry } from "./entries.entity";
export class EntryDTO implements Partial<Entry> { export class EntryDTO implements Partial<Entry> {
title?: string; title?: string;
description?: string; description?: string;
source?: string; sourceText?: string;
sourceUrl?: string; sourceUrl?: string;
} }

View File

@@ -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 { export class Entry {
@Column() @Column()
@Generated("uuid") @Generated("uuid")
@@ -16,8 +16,17 @@ export class Entry {
description?: string; description?: string;
@Column({ type: String, default: "" }) @Column({ type: String, default: "" })
source?: string; sourceText?: string;
@Column({ type: String, default: "" }) @Column({ type: String, default: "" })
sourceUrl?: string; sourceUrl?: string;
@CreateDateColumn()
created_at?: Date;
@UpdateDateColumn()
updated_at?: Date;
@DeleteDateColumn()
deleted_at?: Date;
} }

View File

@@ -17,13 +17,47 @@ export class EntryService {
this.logger.log("Creating entry") this.logger.log("Creating entry")
const lEntry = new Entry(); const lEntry = new Entry();
lEntry.description = data.description lEntry.description = data.description
lEntry.source = data.source lEntry.sourceText = data.sourceText
lEntry.sourceUrl = data.sourceUrl lEntry.sourceUrl = data.sourceUrl
lEntry.title = data.title lEntry.title = data.title
await this.entryRepository.save(lEntry) await this.entryRepository.save(lEntry)
return lEntry return lEntry
} }
async updateByUuid(uuid: string, data: EntryDTO): Promise<Entry> {
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<Entry | null> {
this.logger.log(`Returning entry with UUID ${uuid}`)
return this.entryRepository.findOneByOrFail({uuid: uuid})
}
async findAll(): Promise<Entry[]> {
this.logger.log("Returning all entries")
return this.entryRepository.find()
}
async findAllAndDeleted(): Promise<Entry[]> {
this.logger.log("Returning all entries, active and deleted")
return this.entryRepository.find({ withDeleted: true })
}
async softDeleteByUuid(uuid: string): Promise<void> { async softDeleteByUuid(uuid: string): Promise<void> {
this.logger.log(`Soft deleting entry with UUID ${uuid}`) this.logger.log(`Soft deleting entry with UUID ${uuid}`)
const deleteResponse = await this.entryRepository.softDelete({ const deleteResponse = await this.entryRepository.softDelete({
@@ -33,4 +67,19 @@ export class EntryService {
throw new EntryNotFoundException(uuid) throw new EntryNotFoundException(uuid)
} }
} }
async restoreDeletedByUuid(uuid: string): Promise<Entry | null> {
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
})
}
}
} }

View File

@@ -4,11 +4,11 @@ export class Initial1770020781006 implements MigrationInterface {
name = 'Initial1770020781006' name = 'Initial1770020781006'
public async up(queryRunner: QueryRunner): Promise<void> { 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"))`); 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<void> { public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "entities"`); await queryRunner.query(`DROP TABLE "entries"`);
} }
} }

View File

@@ -0,0 +1,22 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619 implements MigrationInterface {
name = 'AddCreateUpdateDeleteColsAndRenameSourceToSourceText1770067407619'
public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
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 ''`);
}
}