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 })
|
||||
name!: string;
|
||||
|
||||
@Column({ name: 'level', type: 'integer' })
|
||||
level!: number;
|
||||
|
||||
@Column({ name: 'experience', type: 'integer' })
|
||||
experience!: number;
|
||||
@Column({ name: 'renown', type: 'integer' })
|
||||
renown!: number;
|
||||
|
||||
@Column({ name: 'silver', type: 'integer' })
|
||||
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' })
|
||||
tier!: number;
|
||||
|
||||
@Column({ name: 'required_level', type: 'integer' })
|
||||
requiredLevel!: number;
|
||||
|
||||
@Column({ name: 'weapon_damage', type: 'integer' })
|
||||
weaponDamage!: number;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export enum ItemType {
|
||||
WEAPON = 'WEAPON',
|
||||
ARMOR = 'ARMOR',
|
||||
MATERIAL = 'MATERIAL',
|
||||
EQUIPMENT = 'EQUIPMENT',
|
||||
TRADE_GOOD = 'TRADE_GOOD',
|
||||
TROPHY = 'TROPHY',
|
||||
QUEST_ITEM = 'QUEST_ITEM',
|
||||
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' })
|
||||
characterId!: string;
|
||||
|
||||
@Column({ name: 'experience_granted', type: 'integer' })
|
||||
experienceGranted!: number;
|
||||
|
||||
@Column({ name: 'silver_granted', type: 'integer' })
|
||||
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