diff --git a/src/main/entities/base.ts b/src/main/entities/base-entity.ts similarity index 80% rename from src/main/entities/base.ts rename to src/main/entities/base-entity.ts index 7ae3f46..d7e07da 100644 --- a/src/main/entities/base.ts +++ b/src/main/entities/base-entity.ts @@ -1,7 +1,7 @@ import { Entity, PrimaryGeneratedColumn } from 'typeorm'; @Entity() -export abstract class Base { +export class BaseEntity { @PrimaryGeneratedColumn() public id: number; } diff --git a/src/main/entities/library/author.ts b/src/main/entities/library/author.ts index 0cf1875..aefb8f1 100644 --- a/src/main/entities/library/author.ts +++ b/src/main/entities/library/author.ts @@ -1,9 +1,10 @@ import { Entity, ManyToMany } from 'typeorm'; +import { BaseEntity } from '../base-entity'; import { MultiNamed } from './base/multi-named'; import { Book } from './book'; @Entity() -export class Author extends MultiNamed { +export class Author extends MultiNamed(BaseEntity) { @ManyToMany(() => Book, { nullable: false, onDelete: 'CASCADE', diff --git a/src/main/entities/library/base/multi-named.ts b/src/main/entities/library/base/multi-named.ts index f3d8fd3..330b993 100644 --- a/src/main/entities/library/base/multi-named.ts +++ b/src/main/entities/library/base/multi-named.ts @@ -1,11 +1,14 @@ import { Column, Entity } from 'typeorm'; -import { Base } from '../../base'; -@Entity() -export abstract class MultiNamed extends Base { - @Column() - public nameCanonical: string; +export function MultiNamed(BaseClass: T) { + @Entity() + class MixinClass extends BaseClass { + @Column() + public nameCanonical: string; - @Column('simple-array') - public names: string[]; + @Column('simple-array') + public names: string[]; + } + + return MixinClass; } diff --git a/src/main/entities/library/base/rateable.ts b/src/main/entities/library/base/rateable.ts new file mode 100644 index 0000000..3a1647e --- /dev/null +++ b/src/main/entities/library/base/rateable.ts @@ -0,0 +1,11 @@ +import { Column, Entity } from 'typeorm'; + +export function Rateable(BaseClass: T) { + @Entity() + class MixinClass extends BaseClass { + @Column({ nullable: true }) + public rating: number; + } + + return MixinClass; +} diff --git a/src/main/entities/library/base/tslint.json b/src/main/entities/library/base/tslint.json new file mode 100644 index 0000000..4770a4d --- /dev/null +++ b/src/main/entities/library/base/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": ["../../../../../tslint.json"], + "rules": { + "typedef": false + } +} diff --git a/src/main/entities/library/book.ts b/src/main/entities/library/book.ts index e398feb..e8163be 100644 --- a/src/main/entities/library/book.ts +++ b/src/main/entities/library/book.ts @@ -1,13 +1,15 @@ import { Entity, JoinTable, ManyToMany, OneToMany } from 'typeorm'; +import { BaseEntity } from '../base-entity'; import { Author } from './author'; import { MultiNamed } from './base/multi-named'; +import { Rateable } from './base/rateable'; import { Character } from './character'; import { Copy } from './copy'; import { Fiction } from './fiction'; import { Tag } from './tag'; @Entity() -export class Book extends MultiNamed { +export class Book extends Rateable(MultiNamed(BaseEntity)) { @OneToMany( () => Copy, (copy: Copy) => copy.original, diff --git a/src/main/entities/library/character.ts b/src/main/entities/library/character.ts index 5f464b1..88c2f37 100644 --- a/src/main/entities/library/character.ts +++ b/src/main/entities/library/character.ts @@ -1,10 +1,11 @@ import { Entity, ManyToMany } from 'typeorm'; +import { BaseEntity } from '../base-entity'; import { MultiNamed } from './base/multi-named'; import { Book } from './book'; import { Fiction } from './fiction'; @Entity() -export class Character extends MultiNamed { +export class Character extends MultiNamed(BaseEntity) { @ManyToMany(() => Book, { nullable: false, onDelete: 'CASCADE', diff --git a/src/main/entities/library/copy-type.ts b/src/main/entities/library/copy-type.ts index 7c82edb..ea1232c 100644 --- a/src/main/entities/library/copy-type.ts +++ b/src/main/entities/library/copy-type.ts @@ -1,5 +1,5 @@ import { Column, Entity, ManyToOne } from 'typeorm'; -import { Base } from '../base'; +import { BaseEntity } from '../base-entity'; import { Copy } from './copy'; const enum CopyTypes { @@ -10,7 +10,7 @@ const enum CopyTypes { } @Entity() -export class CopyType extends Base { +export class CopyType extends BaseEntity { @ManyToOne( () => Copy, (copy: Copy) => copy.types, diff --git a/src/main/entities/library/copy.ts b/src/main/entities/library/copy.ts index 24819e5..0ceeec8 100644 --- a/src/main/entities/library/copy.ts +++ b/src/main/entities/library/copy.ts @@ -1,5 +1,6 @@ import { Column, Entity, JoinTable, ManyToMany, ManyToOne, OneToMany } from 'typeorm'; -import { Base } from '../base'; +import { BaseEntity } from '../base-entity'; +import { Rateable } from './base/rateable'; import { Book } from './book'; import { CopyType } from './copy-type'; import { Language } from './language'; @@ -7,7 +8,7 @@ import { Source } from './source'; import { Translator } from './translator'; @Entity() -export class Copy extends Base { +export class Copy extends Rateable(BaseEntity) { @ManyToOne( () => Book, (book: Book) => book.copies, diff --git a/src/main/entities/library/fiction.ts b/src/main/entities/library/fiction.ts index d67d3b0..8fd8745 100644 --- a/src/main/entities/library/fiction.ts +++ b/src/main/entities/library/fiction.ts @@ -1,10 +1,11 @@ import { Entity, ManyToMany } from 'typeorm'; +import { BaseEntity } from '../base-entity'; import { MultiNamed } from './base/multi-named'; import { Book } from './book'; import { Character } from './character'; @Entity() -export class Fiction extends MultiNamed { +export class Fiction extends MultiNamed(BaseEntity) { @ManyToMany(() => Book, { nullable: false, onDelete: 'CASCADE', diff --git a/src/main/entities/library/language.ts b/src/main/entities/library/language.ts index 2deaa2b..3e6b532 100644 --- a/src/main/entities/library/language.ts +++ b/src/main/entities/library/language.ts @@ -1,9 +1,9 @@ import { Column, Entity, ManyToMany } from 'typeorm'; -import { Base } from '../base'; +import { BaseEntity } from '../base-entity'; import { Copy } from './copy'; @Entity() -export class Language extends Base { +export class Language extends BaseEntity { @Column({ nullable: false, unique: true, diff --git a/src/main/entities/library/site.ts b/src/main/entities/library/site.ts index c78061b..bcdf75c 100644 --- a/src/main/entities/library/site.ts +++ b/src/main/entities/library/site.ts @@ -1,9 +1,10 @@ import { Entity, OneToMany } from 'typeorm'; +import { BaseEntity } from '../base-entity'; import { MultiNamed } from './base/multi-named'; import { Source } from './source'; @Entity() -export class Site extends MultiNamed { +export class Site extends MultiNamed(BaseEntity) { @OneToMany( () => Source, (source: Source) => source.site, diff --git a/src/main/entities/library/source.ts b/src/main/entities/library/source.ts index f1ccef7..2c91327 100644 --- a/src/main/entities/library/source.ts +++ b/src/main/entities/library/source.ts @@ -1,10 +1,10 @@ import { Column, Entity, ManyToMany, ManyToOne } from 'typeorm'; -import { Base } from '../base'; +import { BaseEntity } from '../base-entity'; import { Copy } from './copy'; import { Site } from './site'; @Entity() -export class Source extends Base { +export class Source extends BaseEntity { @Column({ nullable: false, unique: true, diff --git a/src/main/entities/library/tag.ts b/src/main/entities/library/tag.ts index cd8a473..266e85a 100644 --- a/src/main/entities/library/tag.ts +++ b/src/main/entities/library/tag.ts @@ -1,9 +1,10 @@ import { Entity, ManyToMany } from 'typeorm'; +import { BaseEntity } from '../base-entity'; import { MultiNamed } from './base/multi-named'; import { Book } from './book'; @Entity() -export class Tag extends MultiNamed { +export class Tag extends MultiNamed(BaseEntity) { @ManyToMany(() => Book, { nullable: false, onDelete: 'CASCADE', diff --git a/src/main/entities/library/translator.ts b/src/main/entities/library/translator.ts index eb3ddf3..7e13de9 100644 --- a/src/main/entities/library/translator.ts +++ b/src/main/entities/library/translator.ts @@ -1,9 +1,10 @@ import { Entity, ManyToMany } from 'typeorm'; +import { BaseEntity } from '../base-entity'; import { MultiNamed } from './base/multi-named'; import { Copy } from './copy'; @Entity() -export class Translator extends MultiNamed { +export class Translator extends MultiNamed(BaseEntity) { @ManyToMany(() => Copy, { nullable: false, onDelete: 'CASCADE', diff --git a/src/main/migrations/library/1574452206367-add_rating.ts b/src/main/migrations/library/1574452206367-add_rating.ts new file mode 100644 index 0000000..c63ce9e --- /dev/null +++ b/src/main/migrations/library/1574452206367-add_rating.ts @@ -0,0 +1,51 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class addRating1574452206367 implements MigrationInterface { + name = 'addRating1574452206367'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "temporary_copy" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "favorited" boolean NOT NULL DEFAULT (0), "isDigital" boolean NOT NULL, "originalId" integer NOT NULL, "rating" integer, CONSTRAINT "FK_e8ce0011cf0a8b9fdc8ad908a44" FOREIGN KEY ("originalId") REFERENCES "book" ("id") ON DELETE CASCADE ON UPDATE CASCADE)`, + undefined + ); + await queryRunner.query( + `INSERT INTO "temporary_copy"("id", "favorited", "isDigital", "originalId") SELECT "id", "favorited", "isDigital", "originalId" FROM "copy"`, + undefined + ); + await queryRunner.query(`DROP TABLE "copy"`, undefined); + await queryRunner.query(`ALTER TABLE "temporary_copy" RENAME TO "copy"`, undefined); + await queryRunner.query( + `CREATE TABLE "temporary_book" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "nameCanonical" varchar NOT NULL, "names" text NOT NULL, "rating" integer)`, + undefined + ); + await queryRunner.query( + `INSERT INTO "temporary_book"("id", "nameCanonical", "names") SELECT "id", "nameCanonical", "names" FROM "book"`, + undefined + ); + await queryRunner.query(`DROP TABLE "book"`, undefined); + await queryRunner.query(`ALTER TABLE "temporary_book" RENAME TO "book"`, undefined); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "book" RENAME TO "temporary_book"`, undefined); + await queryRunner.query( + `CREATE TABLE "book" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "nameCanonical" varchar NOT NULL, "names" text NOT NULL)`, + undefined + ); + await queryRunner.query( + `INSERT INTO "book"("id", "nameCanonical", "names") SELECT "id", "nameCanonical", "names" FROM "temporary_book"`, + undefined + ); + await queryRunner.query(`DROP TABLE "temporary_book"`, undefined); + await queryRunner.query(`ALTER TABLE "copy" RENAME TO "temporary_copy"`, undefined); + await queryRunner.query( + `CREATE TABLE "copy" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "favorited" boolean NOT NULL DEFAULT (0), "isDigital" boolean NOT NULL, "originalId" integer NOT NULL, CONSTRAINT "FK_e8ce0011cf0a8b9fdc8ad908a44" FOREIGN KEY ("originalId") REFERENCES "book" ("id") ON DELETE CASCADE ON UPDATE CASCADE)`, + undefined + ); + await queryRunner.query( + `INSERT INTO "copy"("id", "favorited", "isDigital", "originalId") SELECT "id", "favorited", "isDigital", "originalId" FROM "temporary_copy"`, + undefined + ); + await queryRunner.query(`DROP TABLE "temporary_copy"`, undefined); + } +} diff --git a/src/main/migrations/library/tslint.json b/src/main/migrations/library/tslint.json new file mode 100644 index 0000000..0b4e3a4 --- /dev/null +++ b/src/main/migrations/library/tslint.json @@ -0,0 +1,8 @@ +{ + "extends": ["../../../../tslint.json"], + "rules": { + "class-name": false, + "member-access": false, + "typedef": false + } +} diff --git a/src/types/constructor.ts b/src/types/constructor.ts new file mode 100644 index 0000000..c83a5f3 --- /dev/null +++ b/src/types/constructor.ts @@ -0,0 +1 @@ +type Constructor = new (...args: any[]) => T;