From 6d2f01c24612901ef411202ea077458afc33081c Mon Sep 17 00:00:00 2001 From: hxtree Date: Fri, 13 Oct 2023 23:54:33 +0000 Subject: [PATCH] feat: add affiliation endpoint Signed-off-by: hxtree --- services/character-sheet/openapi-spec.json | 209 ++++++++++++------ services/character-sheet/src/app.module.ts | 4 +- .../character-sheet/src/database.module.ts | 15 ++ .../affilation/affiliation.controller.ts | 30 --- .../affiliation/affiliation.controller.ts | 71 ++++++ .../affiliation.e2e-spec.ts | 42 +++- .../affiliation.module.ts | 9 + .../affiliation.service.ts | 4 - .../query-affiliation.dto.ts | 0 .../affiliation/update-affiliation.dto.ts | 29 +++ .../character-sheet.e2e-spec.ts | 2 - .../character-sheet/character-sheet.module.ts | 8 +- .../src/modules/npc/npc.module.ts | 9 +- 13 files changed, 315 insertions(+), 117 deletions(-) create mode 100644 services/character-sheet/src/database.module.ts delete mode 100644 services/character-sheet/src/modules/affilation/affiliation.controller.ts create mode 100644 services/character-sheet/src/modules/affiliation/affiliation.controller.ts rename services/character-sheet/src/modules/{affilation => affiliation}/affiliation.e2e-spec.ts (64%) rename services/character-sheet/src/modules/{affilation => affiliation}/affiliation.module.ts (54%) rename services/character-sheet/src/modules/{affilation => affiliation}/affiliation.service.ts (85%) rename services/character-sheet/src/modules/{affilation => affiliation}/query-affiliation.dto.ts (100%) create mode 100644 services/character-sheet/src/modules/affiliation/update-affiliation.dto.ts diff --git a/services/character-sheet/openapi-spec.json b/services/character-sheet/openapi-spec.json index 8c11efe6b..1e11234d0 100644 --- a/services/character-sheet/openapi-spec.json +++ b/services/character-sheet/openapi-spec.json @@ -381,69 +381,6 @@ "responses": { "200": { "description": "" } } } }, - "/character-sheets/{id}": { - "get": { - "operationId": "CharacterSheetController_findOne", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { "type": "string" } - } - ], - "responses": { "200": { "description": "" } } - }, - "delete": { - "operationId": "CharacterSheetController_delete", - "parameters": [ - { - "name": "id", - "required": true, - "in": "path", - "schema": { "type": "string" } - } - ], - "responses": { "200": { "description": "" } } - } - }, - "/character-sheets": { - "get": { - "operationId": "CharacterSheetController_findAll", - "parameters": [], - "responses": { "200": { "description": "" } } - }, - "post": { - "operationId": "CharacterSheetController_create", - "parameters": [], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateCharacterSheetDto" - } - } - } - }, - "responses": { "201": { "description": "" } } - } - }, - "/npcs": { - "post": { - "operationId": "NpcController_create", - "parameters": [], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/CreateSpawnDto" } - } - } - }, - "responses": { "201": { "description": "" } } - } - }, "/items": { "get": { "operationId": "ItemController_list", @@ -537,6 +474,101 @@ ], "responses": { "200": { "description": "" } } } + }, + "/character-sheets/{id}": { + "get": { + "operationId": "CharacterSheetController_findOne", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { "200": { "description": "" } } + }, + "delete": { + "operationId": "CharacterSheetController_delete", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { "type": "string" } + } + ], + "responses": { "200": { "description": "" } } + } + }, + "/character-sheets": { + "get": { + "operationId": "CharacterSheetController_findAll", + "parameters": [], + "responses": { "200": { "description": "" } } + }, + "post": { + "operationId": "CharacterSheetController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCharacterSheetDto" + } + } + } + }, + "responses": { "201": { "description": "" } } + } + }, + "/npcs": { + "post": { + "operationId": "NpcController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateSpawnDto" } + } + } + }, + "responses": { "201": { "description": "" } } + } + }, + "/affiliations/{id}": { + "get": { + "operationId": "AffiliationController_find", + "parameters": [ + { + "name": "characterSheetId", + "required": true, + "in": "path", + "schema": { + "default": "c63bd3b9-02a2-4f32-b836-9aac1665cc96", + "type": "string" + } + } + ], + "responses": { "200": { "description": "" } } + } + }, + "/affiliations": { + "post": { + "operationId": "AffiliationController_update", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/UpdateAffiliationDto" } + } + } + }, + "responses": { "201": { "description": "" } } + } } }, "info": { @@ -554,11 +586,11 @@ "properties": { "_id": { "type": "string", - "default": "5e799b22-74c0-4f51-963c-9d402a9825e1" + "default": "d8fba7e1-3bb2-4715-986e-1846209627a0" }, "instanceId": { "type": "string", - "default": "43d7e427-4ba7-4f0e-9afc-d0dccd54a69a" + "default": "05f888f0-07ad-484f-9f73-33b828abc09e" }, "archetypeId": { "type": "string", @@ -615,11 +647,11 @@ "properties": { "id": { "type": "string", - "default": "62309dfb-d1e0-49e3-84c7-d9a86d43cf88" + "default": "712756ed-6b55-42c1-ba6d-2568729b2403" }, "instanceId": { "type": "string", - "default": "903ce92d-a8fb-406f-a03f-5cee1effc3c4" + "default": "8445ffd3-4e7f-4d58-b2a9-620b0d3c4847" }, "place": { "type": "string", @@ -636,6 +668,49 @@ } }, "required": ["id", "instanceId", "place"] + }, + "UpdateAffiliationDto": { + "type": "object", + "properties": { + "characterSheetId": { + "type": "string", + "default": "ebfe4388-5a84-494a-9e55-b806b9719689" + }, + "affiliationId": { + "type": "string", + "enum": [ + "Reputation", + "THE_DESTROYERS", + "THE_CATS", + "ARCHANGELS", + "DEVILS", + "GUARDIANS", + "VALLONS_SEVEN", + "BROTHERHOOD", + "SOLIDER_FORCES", + "X_SOLIDER_FORCES", + "MAGI_ORDER", + "MAGI_ORDER_ELDERS", + "CATCHERS", + "REBEL", + "ORIGINALS", + "OCEANIA", + "EURASIA", + "BARBARIANS", + "NINJA", + "LEGION_SEE", + "FLIPPING_PROFITEERS" + ], + "default": "THE_CATS" + }, + "value": { "type": "number" }, + "operation": { + "type": "string", + "enum": ["ADD", "REMOVE"], + "default": "ADD" + } + }, + "required": ["characterSheetId", "affiliationId", "value", "operation"] } } } diff --git a/services/character-sheet/src/app.module.ts b/services/character-sheet/src/app.module.ts index d3df1e26c..ec08f3745 100644 --- a/services/character-sheet/src/app.module.ts +++ b/services/character-sheet/src/app.module.ts @@ -7,6 +7,7 @@ import { HealthModule } from './modules/health/health.module'; import { SkillModule } from './modules/skill/skill.module'; import { NpcModule } from './modules/npc/npc.module'; import { ItemModule } from './modules/item/item.module'; +import { AffiliationModule } from './modules/affiliation/affiliation.module'; @Module({ imports: [ @@ -15,9 +16,10 @@ import { ItemModule } from './modules/item/item.module'; ArchetypeModule, GearModule, SkillModule, + ItemModule, CharacterSheetModule, NpcModule, - ItemModule, + AffiliationModule, ], providers: [], exports: [], diff --git a/services/character-sheet/src/database.module.ts b/services/character-sheet/src/database.module.ts new file mode 100644 index 000000000..a6728dae1 --- /dev/null +++ b/services/character-sheet/src/database.module.ts @@ -0,0 +1,15 @@ +import { MongooseModule, MongooseModuleOptions } from '@nestjs/mongoose'; +import { ConfigService } from '@nestjs/config'; + +// eslint-disable max-len +export const databaseModule = (options: MongooseModuleOptions = {}) => MongooseModule.forRootAsync({ + inject: [ConfigService], + useFactory: (configService: ConfigService) => ({ + uri: `${configService.get('MONGO_DATABASE_URI')}/${configService.get( + 'STAGE', + )}-${configService.get('APP_NAME')}`, + user: configService.get('MONGO_DATABASE_USER'), + pass: configService.get('MONGO_DATABASE_PASSWORD'), + ...options, + }), +}); diff --git a/services/character-sheet/src/modules/affilation/affiliation.controller.ts b/services/character-sheet/src/modules/affilation/affiliation.controller.ts deleted file mode 100644 index 1a20f741f..000000000 --- a/services/character-sheet/src/modules/affilation/affiliation.controller.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - Controller, Get, Param, VERSION_NEUTRAL, -} from '@nestjs/common'; -import { AffiliationService } from './affiliation.service'; -import { QueryAffiliationDto } from './query-affiliation.dto'; -import { CharacterSheetRepository } from '../../models/character-sheet.repository'; - -@Controller({ path: '/affiliations', version: [VERSION_NEUTRAL, '1'] }) -export class AffiliationController { - constructor( - private _affiliationService: AffiliationService, - private _characterSheetRepository: CharacterSheetRepository, - ) {} - - @Get('/:id') - async find(@Param() param: QueryAffiliationDto): Promise { - const result = await this._characterSheetRepository.findOne({ - id: param.characterSheetId, - }); - - if (!result) { - return []; - } - - return result.affiliation ?? []; - } - - // :id/:id - // POST :id/:id +2 -2 -} diff --git a/services/character-sheet/src/modules/affiliation/affiliation.controller.ts b/services/character-sheet/src/modules/affiliation/affiliation.controller.ts new file mode 100644 index 000000000..ee54fd1b4 --- /dev/null +++ b/services/character-sheet/src/modules/affiliation/affiliation.controller.ts @@ -0,0 +1,71 @@ +import { + BadRequestException, + Body, + Controller, + Get, + Param, + Post, + Req, + VERSION_NEUTRAL, +} from '@nestjs/common'; +import { AffiliationService } from './affiliation.service'; +import { QueryAffiliationDto } from './query-affiliation.dto'; +import { Operation, UpdateAffiliationDto } from './update-affiliation.dto'; +import { CharacterSheetRepository } from '../../models/character-sheet.repository'; +import { AffiliationEmbeddable } from '../../models/affiliation-embeddable.schema'; + +@Controller({ path: '/affiliations', version: [VERSION_NEUTRAL, '1'] }) +export class AffiliationController { + constructor( + private _affiliationService: AffiliationService, + private _characterSheetRepository: CharacterSheetRepository, + ) {} + + @Get('/:id') + async find(@Param() param: QueryAffiliationDto): Promise { + const result = await this._characterSheetRepository.findOne({ + id: param.characterSheetId, + }); + + if (!result) { + return []; + } + + return result.affiliation ?? []; + } + + @Post() + async update(@Body() body: UpdateAffiliationDto): Promise { + const characterSheet = await this._characterSheetRepository.findOne({ + id: body.characterSheetId, + }); + + if (!characterSheet) { + throw new BadRequestException('Not found'); + } + + characterSheet.affiliation.forEach( + (affiliation: AffiliationEmbeddable, index: number) => { + if (affiliation.affiliationId !== body.affiliationId) { + return; + } + + switch (body.operation) { + case Operation.ADD: + characterSheet.affiliation[index].amount += body.value; + break; + case Operation.REMOVE: + characterSheet.affiliation[index].amount -= body.value; + break; + default: + throw new BadRequestException('Invalid operation'); + } + }, + ); + + return this._characterSheetRepository.updateOne( + { id: body.characterSheetId }, + characterSheet, + ); + } +} diff --git a/services/character-sheet/src/modules/affilation/affiliation.e2e-spec.ts b/services/character-sheet/src/modules/affiliation/affiliation.e2e-spec.ts similarity index 64% rename from services/character-sheet/src/modules/affilation/affiliation.e2e-spec.ts rename to services/character-sheet/src/modules/affiliation/affiliation.e2e-spec.ts index 0d1ab7dac..74576834c 100644 --- a/services/character-sheet/src/modules/affilation/affiliation.e2e-spec.ts +++ b/services/character-sheet/src/modules/affiliation/affiliation.e2e-spec.ts @@ -10,14 +10,18 @@ import { import { AffiliationController } from './affiliation.controller'; import { AffiliationService } from './affiliation.service'; import { CharacterSheetRepository } from '../../models/character-sheet.repository'; -import { CharacterSheet, CharacterSheetSchema } from '../../models/character-sheet.schema'; +import { + CharacterSheet, + CharacterSheetSchema, +} from '../../models/character-sheet.schema'; +import { Operation, UpdateAffiliationDto } from './update-affiliation.dto'; describe('/affiliations', () => { let app: INestApplication; let affiliationService: AffiliationService; let characterSheetRepository: CharacterSheetRepository; - beforeEach(async () => { + beforeAll(async () => { const moduleRef: TestingModule = await Test.createTestingModule({ imports: [ rootMongooseTestModule(), @@ -59,4 +63,38 @@ describe('/affiliations', () => { expect(response.body).toEqual(characterSheet.affiliation); }); + + it('/POST /affiliations/:id', async () => { + const characterSheet = await FakerFactory.create( + CharacterSheet, + { + archetypeId: 'MEEKU_ONI', + affiliation: [{ affiliationId: 'THE_CATS', amount: 10 }], + }, + ); + await characterSheetRepository.create(characterSheet); + + const body = await FakerFactory.create( + UpdateAffiliationDto, + { + characterSheetId: characterSheet._id, + affiliationId: 'THE_CATS', + value: 10, + operation: Operation.ADD, + }, + ); + + const response = await supertest(app.getHttpServer()) + .post('/affiliations') + .send(body) + .expect(201); + + expect(response.body).toEqual({ + acknowledged: true, + matchedCount: 1, + modifiedCount: 1, + upsertedCount: 0, + upsertedId: null, + }); + }); }); diff --git a/services/character-sheet/src/modules/affilation/affiliation.module.ts b/services/character-sheet/src/modules/affiliation/affiliation.module.ts similarity index 54% rename from services/character-sheet/src/modules/affilation/affiliation.module.ts rename to services/character-sheet/src/modules/affiliation/affiliation.module.ts index 1e6bbe403..8a3701abd 100644 --- a/services/character-sheet/src/modules/affilation/affiliation.module.ts +++ b/services/character-sheet/src/modules/affiliation/affiliation.module.ts @@ -1,10 +1,19 @@ import { Module } from '@nestjs/common'; +import { MongooseModule } from '@cats-cradle/nestjs-modules'; +import { databaseModule } from '../../database.module'; import { AffiliationService } from './affiliation.service'; import { AffiliationController } from './affiliation.controller'; import { CharacterSheetRepository } from '../../models/character-sheet.repository'; +import { CharacterSheetSchema } from '../../models/character-sheet.schema'; @Module({ controllers: [AffiliationController], providers: [AffiliationService, CharacterSheetRepository], + imports: [ + databaseModule(), + MongooseModule.forFeature([ + { name: 'CharacterSheet', schema: CharacterSheetSchema }, + ]), + ], }) export class AffiliationModule {} diff --git a/services/character-sheet/src/modules/affilation/affiliation.service.ts b/services/character-sheet/src/modules/affiliation/affiliation.service.ts similarity index 85% rename from services/character-sheet/src/modules/affilation/affiliation.service.ts rename to services/character-sheet/src/modules/affiliation/affiliation.service.ts index d6fd7b2e9..fc15e36c2 100644 --- a/services/character-sheet/src/modules/affilation/affiliation.service.ts +++ b/services/character-sheet/src/modules/affiliation/affiliation.service.ts @@ -26,8 +26,4 @@ export class AffiliationService { return Reputation.NEUTRAL; } - - async find(instanceId: string, characterId: string): Promise { - // get from mongo - } } diff --git a/services/character-sheet/src/modules/affilation/query-affiliation.dto.ts b/services/character-sheet/src/modules/affiliation/query-affiliation.dto.ts similarity index 100% rename from services/character-sheet/src/modules/affilation/query-affiliation.dto.ts rename to services/character-sheet/src/modules/affiliation/query-affiliation.dto.ts diff --git a/services/character-sheet/src/modules/affiliation/update-affiliation.dto.ts b/services/character-sheet/src/modules/affiliation/update-affiliation.dto.ts new file mode 100644 index 000000000..0891cdfe5 --- /dev/null +++ b/services/character-sheet/src/modules/affiliation/update-affiliation.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { v4 } from 'uuid'; +import { IsEnum, IsInt, IsPositive } from 'class-validator'; +import { AffiliationId, AffiliationIds } from '../../data/affiliations'; + +export enum Operation { + ADD = 'ADD', + REMOVE = 'REMOVE', +} + +export class UpdateAffiliationDto { + @ApiProperty({ + default: v4(), + }) + characterSheetId: string; + + @ApiProperty({ enum: AffiliationIds, default: 'THE_CATS' }) + @IsEnum(AffiliationIds) + affiliationId: AffiliationId; + + @ApiProperty({ type: Number }) + @IsInt() + @IsPositive() + value: number; + + @ApiProperty({ enum: Operation, default: Operation.ADD }) + @IsEnum(Operation) + operation: Operation; +} diff --git a/services/character-sheet/src/modules/character-sheet/character-sheet.e2e-spec.ts b/services/character-sheet/src/modules/character-sheet/character-sheet.e2e-spec.ts index 2a16b96a1..9d1dbba9d 100644 --- a/services/character-sheet/src/modules/character-sheet/character-sheet.e2e-spec.ts +++ b/services/character-sheet/src/modules/character-sheet/character-sheet.e2e-spec.ts @@ -83,8 +83,6 @@ describe('/character-sheets', () => { .get(`/character-sheets/${characterSheet._id}`) .expect(200); - console.log(JSON.stringify(result.body, null, 2)); - expect(result.body).toEqual( expect.objectContaining({ id: characterSheet._id, diff --git a/services/character-sheet/src/modules/character-sheet/character-sheet.module.ts b/services/character-sheet/src/modules/character-sheet/character-sheet.module.ts index 4e23d51bf..6af2883cf 100644 --- a/services/character-sheet/src/modules/character-sheet/character-sheet.module.ts +++ b/services/character-sheet/src/modules/character-sheet/character-sheet.module.ts @@ -1,8 +1,6 @@ import { Module } from '@nestjs/common'; -import { - rootMongooseModule, - MongooseModule, -} from '@cats-cradle/nestjs-modules'; +import { MongooseModule } from '@cats-cradle/nestjs-modules'; +import { databaseModule } from '../../database.module'; import { CharacterSheetService } from './character-sheet.service'; import { CharacterSheetController } from './character-sheet.controller'; import { CharacterSheetRepository } from '../../models/character-sheet.repository'; @@ -13,7 +11,7 @@ import { PlaceService } from '../place/place.service'; controllers: [CharacterSheetController], providers: [PlaceService, CharacterSheetService, CharacterSheetRepository], imports: [ - rootMongooseModule(), + databaseModule(), MongooseModule.forFeature([ { name: 'CharacterSheet', schema: CharacterSheetSchema }, ]), diff --git a/services/character-sheet/src/modules/npc/npc.module.ts b/services/character-sheet/src/modules/npc/npc.module.ts index a98f6a383..ac0fd4ada 100644 --- a/services/character-sheet/src/modules/npc/npc.module.ts +++ b/services/character-sheet/src/modules/npc/npc.module.ts @@ -1,9 +1,6 @@ import { Module } from '@nestjs/common'; -import { - rootMongooseTestModule, - rootMongooseModule, - MongooseModule, -} from '@cats-cradle/nestjs-modules'; +import { MongooseModule } from '@cats-cradle/nestjs-modules'; +import { databaseModule } from '../../database.module'; import { CharacterSheetService } from '../character-sheet/character-sheet.service'; import { NpcController } from './npc.controller'; import { CharacterSheetRepository } from '../../models/character-sheet.repository'; @@ -20,7 +17,7 @@ import { SpawnService } from './spawn.service'; SpawnService, ], imports: [ - rootMongooseTestModule(), + databaseModule(), MongooseModule.forFeature([ { name: 'CharacterSheet', schema: CharacterSheetSchema }, ]),