feat(renown): add Reputation/Renown/TurnIn entities and update Character/ItemDefinition/CombatReward
This commit is contained in:
@@ -17,11 +17,8 @@ export class Character {
|
|||||||
@Column({ name: 'name', type: 'varchar', length: 150 })
|
@Column({ name: 'name', type: 'varchar', length: 150 })
|
||||||
name!: string;
|
name!: string;
|
||||||
|
|
||||||
@Column({ name: 'level', type: 'integer' })
|
@Column({ name: 'renown', type: 'integer' })
|
||||||
level!: number;
|
renown!: number;
|
||||||
|
|
||||||
@Column({ name: 'experience', type: 'integer' })
|
|
||||||
experience!: number;
|
|
||||||
|
|
||||||
@Column({ name: 'silver', type: 'integer' })
|
@Column({ name: 'silver', type: 'integer' })
|
||||||
silver!: number;
|
silver!: number;
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import 'reflect-metadata';
|
||||||
|
import { getMetadataArgsStorage } from 'typeorm';
|
||||||
|
import { Character } from '../../characters/entities/character.entity';
|
||||||
|
import { ItemDefinition } from '../../items/entities/item-definition.entity';
|
||||||
|
import { ItemType } from '../../items/item-type.enum';
|
||||||
|
import { CombatReward } from '../../rewards/entities/combat-reward.entity';
|
||||||
|
import { ReputationFaction } from '../../reputation/entities/reputation-faction.entity';
|
||||||
|
import { CharacterReputation } from '../../reputation/entities/character-reputation.entity';
|
||||||
|
import { RenownMilestoneDefinition } from '../../renown/entities/renown-milestone-definition.entity';
|
||||||
|
import { CharacterRenownMilestone } from '../../renown/entities/character-renown-milestone.entity';
|
||||||
|
import { TurnInDefinition } from '../../turn-in/entities/turn-in-definition.entity';
|
||||||
|
|
||||||
|
function columnNames(target: unknown): string[] {
|
||||||
|
return getMetadataArgsStorage()
|
||||||
|
.columns.filter((column) => column.target === target)
|
||||||
|
.map((column) => column.propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function uniqueIndexFor(target: unknown, columns: string[]): boolean {
|
||||||
|
const index = getMetadataArgsStorage().indices.find(
|
||||||
|
(candidate) =>
|
||||||
|
candidate.target === target && columns.every((column) => candidate.columns?.includes(column)),
|
||||||
|
);
|
||||||
|
const meta = index as typeof index & { options?: { unique?: boolean }; unique?: boolean };
|
||||||
|
return (meta?.options?.unique ?? meta?.unique) === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Slice 0.6.5 entity metadata', () => {
|
||||||
|
it('Character exposes renown and no longer exposes level/experience', () => {
|
||||||
|
const names = columnNames(Character);
|
||||||
|
expect(names).toContain('renown');
|
||||||
|
expect(names).not.toContain('level');
|
||||||
|
expect(names).not.toContain('experience');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ItemDefinition no longer exposes requiredLevel', () => {
|
||||||
|
expect(columnNames(ItemDefinition)).not.toContain('requiredLevel');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ItemType has exactly the five Slice 0.6.5 values', () => {
|
||||||
|
expect(Object.values(ItemType).sort()).toEqual(
|
||||||
|
['CONSUMABLE', 'EQUIPMENT', 'QUEST_ITEM', 'TRADE_GOOD', 'TROPHY'].sort(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CombatReward no longer exposes experienceGranted', () => {
|
||||||
|
expect(columnNames(CombatReward)).not.toContain('experienceGranted');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ReputationFaction has a unique key', () => {
|
||||||
|
expect(uniqueIndexFor(ReputationFaction, ['key'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CharacterReputation enforces one row per character per faction', () => {
|
||||||
|
expect(uniqueIndexFor(CharacterReputation, ['characterId', 'factionId'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('RenownMilestoneDefinition has a unique key', () => {
|
||||||
|
expect(uniqueIndexFor(RenownMilestoneDefinition, ['key'])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CharacterRenownMilestone enforces one row per character per milestone', () => {
|
||||||
|
expect(
|
||||||
|
uniqueIndexFor(CharacterRenownMilestone, ['characterId', 'milestoneId']),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TurnInDefinition has a unique key', () => {
|
||||||
|
expect(uniqueIndexFor(TurnInDefinition, ['key'])).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -43,9 +43,6 @@ export class ItemDefinition {
|
|||||||
@Column({ name: 'tier', type: 'integer' })
|
@Column({ name: 'tier', type: 'integer' })
|
||||||
tier!: number;
|
tier!: number;
|
||||||
|
|
||||||
@Column({ name: 'required_level', type: 'integer' })
|
|
||||||
requiredLevel!: number;
|
|
||||||
|
|
||||||
@Column({ name: 'weapon_damage', type: 'integer' })
|
@Column({ name: 'weapon_damage', type: 'integer' })
|
||||||
weaponDamage!: number;
|
weaponDamage!: number;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
export enum ItemType {
|
export enum ItemType {
|
||||||
WEAPON = 'WEAPON',
|
EQUIPMENT = 'EQUIPMENT',
|
||||||
ARMOR = 'ARMOR',
|
TRADE_GOOD = 'TRADE_GOOD',
|
||||||
MATERIAL = 'MATERIAL',
|
TROPHY = 'TROPHY',
|
||||||
|
QUEST_ITEM = 'QUEST_ITEM',
|
||||||
CONSUMABLE = 'CONSUMABLE',
|
CONSUMABLE = 'CONSUMABLE',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
Index,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { Character } from '../../characters/entities/character.entity';
|
||||||
|
import { RenownMilestoneDefinition } from './renown-milestone-definition.entity';
|
||||||
|
|
||||||
|
@Entity({ name: 'character_renown_milestones' })
|
||||||
|
@Index('IDX_character_renown_milestones_character_milestone', ['characterId', 'milestoneId'], {
|
||||||
|
unique: true,
|
||||||
|
})
|
||||||
|
export class CharacterRenownMilestone {
|
||||||
|
@PrimaryGeneratedColumn('uuid', { name: 'id' })
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'character_id', type: 'uuid' })
|
||||||
|
characterId!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'milestone_id', type: 'uuid' })
|
||||||
|
milestoneId!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'completed_at', type: 'timestamptz' })
|
||||||
|
completedAt!: Date;
|
||||||
|
|
||||||
|
@Column({ name: 'times_completed', type: 'integer' })
|
||||||
|
timesCompleted!: number;
|
||||||
|
|
||||||
|
@ManyToOne(() => Character, { onDelete: 'CASCADE' })
|
||||||
|
@JoinColumn({ name: 'character_id' })
|
||||||
|
character!: Character;
|
||||||
|
|
||||||
|
@ManyToOne(() => RenownMilestoneDefinition, { onDelete: 'RESTRICT' })
|
||||||
|
@JoinColumn({ name: 'milestone_id' })
|
||||||
|
milestone!: RenownMilestoneDefinition;
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
Index,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
@Entity({ name: 'renown_milestone_definitions' })
|
||||||
|
@Index('IDX_renown_milestone_definitions_key', ['key'], { unique: true })
|
||||||
|
export class RenownMilestoneDefinition {
|
||||||
|
@PrimaryGeneratedColumn('uuid', { name: 'id' })
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'key', type: 'varchar', length: 100 })
|
||||||
|
key!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'name', type: 'varchar', length: 150 })
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'description', type: 'text' })
|
||||||
|
description!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'renown_reward', type: 'integer' })
|
||||||
|
renownReward!: number;
|
||||||
|
|
||||||
|
@Column({ name: 'repeatable', type: 'boolean' })
|
||||||
|
repeatable!: boolean;
|
||||||
|
|
||||||
|
@Column({ name: 'enabled', type: 'boolean' })
|
||||||
|
enabled!: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt!: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt!: Date;
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
Index,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { Character } from '../../characters/entities/character.entity';
|
||||||
|
import { ReputationFaction } from './reputation-faction.entity';
|
||||||
|
|
||||||
|
@Entity({ name: 'character_reputation' })
|
||||||
|
@Index('IDX_character_reputation_character_faction', ['characterId', 'factionId'], {
|
||||||
|
unique: true,
|
||||||
|
})
|
||||||
|
export class CharacterReputation {
|
||||||
|
@PrimaryGeneratedColumn('uuid', { name: 'id' })
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'character_id', type: 'uuid' })
|
||||||
|
characterId!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'faction_id', type: 'uuid' })
|
||||||
|
factionId!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'reputation', type: 'integer' })
|
||||||
|
reputation!: number;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt!: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt!: Date;
|
||||||
|
|
||||||
|
@ManyToOne(() => Character, { onDelete: 'CASCADE' })
|
||||||
|
@JoinColumn({ name: 'character_id' })
|
||||||
|
character!: Character;
|
||||||
|
|
||||||
|
@ManyToOne(() => ReputationFaction, { onDelete: 'RESTRICT' })
|
||||||
|
@JoinColumn({ name: 'faction_id' })
|
||||||
|
faction!: ReputationFaction;
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
Index,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
@Entity({ name: 'reputation_factions' })
|
||||||
|
@Index('IDX_reputation_factions_key', ['key'], { unique: true })
|
||||||
|
export class ReputationFaction {
|
||||||
|
@PrimaryGeneratedColumn('uuid', { name: 'id' })
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'key', type: 'varchar', length: 100 })
|
||||||
|
key!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'name', type: 'varchar', length: 150 })
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'description', type: 'text' })
|
||||||
|
description!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'region_key', type: 'varchar', length: 100 })
|
||||||
|
regionKey!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'enabled', type: 'boolean' })
|
||||||
|
enabled!: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt!: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt!: Date;
|
||||||
|
}
|
||||||
@@ -29,9 +29,6 @@ export class CombatReward {
|
|||||||
@Column({ name: 'character_id', type: 'uuid' })
|
@Column({ name: 'character_id', type: 'uuid' })
|
||||||
characterId!: string;
|
characterId!: string;
|
||||||
|
|
||||||
@Column({ name: 'experience_granted', type: 'integer' })
|
|
||||||
experienceGranted!: number;
|
|
||||||
|
|
||||||
@Column({ name: 'silver_granted', type: 'integer' })
|
@Column({ name: 'silver_granted', type: 'integer' })
|
||||||
silverGranted!: number;
|
silverGranted!: number;
|
||||||
|
|
||||||
|
|||||||
54
apps/api/src/turn-in/entities/turn-in-definition.entity.ts
Normal file
54
apps/api/src/turn-in/entities/turn-in-definition.entity.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
Index,
|
||||||
|
JoinColumn,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { ItemDefinition } from '../../items/entities/item-definition.entity';
|
||||||
|
import { ReputationFaction } from '../../reputation/entities/reputation-faction.entity';
|
||||||
|
|
||||||
|
@Entity({ name: 'turn_in_definitions' })
|
||||||
|
@Index('IDX_turn_in_definitions_key', ['key'], { unique: true })
|
||||||
|
export class TurnInDefinition {
|
||||||
|
@PrimaryGeneratedColumn('uuid', { name: 'id' })
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'key', type: 'varchar', length: 100 })
|
||||||
|
key!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'item_definition_id', type: 'uuid' })
|
||||||
|
itemDefinitionId!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'faction_id', type: 'uuid' })
|
||||||
|
factionId!: string;
|
||||||
|
|
||||||
|
@Column({ name: 'silver_reward_per_item', type: 'integer' })
|
||||||
|
silverRewardPerItem!: number;
|
||||||
|
|
||||||
|
@Column({ name: 'reputation_reward_per_item', type: 'integer' })
|
||||||
|
reputationRewardPerItem!: number;
|
||||||
|
|
||||||
|
@Column({ name: 'repeatable', type: 'boolean' })
|
||||||
|
repeatable!: boolean;
|
||||||
|
|
||||||
|
@Column({ name: 'enabled', type: 'boolean' })
|
||||||
|
enabled!: boolean;
|
||||||
|
|
||||||
|
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||||
|
createdAt!: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||||
|
updatedAt!: Date;
|
||||||
|
|
||||||
|
@ManyToOne(() => ItemDefinition, { onDelete: 'RESTRICT' })
|
||||||
|
@JoinColumn({ name: 'item_definition_id' })
|
||||||
|
itemDefinition!: ItemDefinition;
|
||||||
|
|
||||||
|
@ManyToOne(() => ReputationFaction, { onDelete: 'RESTRICT' })
|
||||||
|
@JoinColumn({ name: 'faction_id' })
|
||||||
|
faction!: ReputationFaction;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user