diff --git a/README.md b/README.md index 2e252ffe..1cef5baf 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@

Cats Cradle :yarn:

-Get ready to embark on an adventure with **Cats Cradle** - the upcoming co-op -RPG that will transport you to a nostalgic world filled with wonder. Join us on -this exciting journey as we continue to develop and bring this game to life. +Embark on an nostalgic adventure with **Cats Cradle** - the upcoming co-op RPG +that will transport you to a world filled with wonder. Join us on this journey +as we flesh in the game and bring it to life. -This repo houses the game's code. This enterprise architecture monorepo includes -numerous microservices built on a PaaS with IaC for streamlined DevOps CI/CD. +This repo houses the game's code in a enterprise architecture monorepo. It +includes numerous microservices built on a PaaS with IaC for streamlined DevOps +CI/CD. -Although each package is maintained primarily for the game, most stable packages -are individually published to +Although each package is maintained primarily for the game, most of the stable +packages are individually published to [NPM](https://www.npmjs.com/search?q=%40cats-cradle) and available under open-source software license. diff --git a/services/character-sheet/openapi-spec.json b/services/character-sheet/openapi-spec.json index 34732f45..115be073 100644 --- a/services/character-sheet/openapi-spec.json +++ b/services/character-sheet/openapi-spec.json @@ -149,7 +149,7 @@ "SNOW_LEOPARD", "CHIMERA", "SLOW_MOVER", - "SNAKE_OF_WATER", + "SNAKE_OF_THE_WATER", "BANDIT", "ROWAN", "CHUBBS" @@ -547,7 +547,7 @@ "required": true, "in": "path", "schema": { - "default": "1f0fb038-21f8-47ff-9de1-010502a010d3", + "default": "9201a8eb-e105-4760-938f-0afc6166834c", "type": "string" } } @@ -586,11 +586,11 @@ "properties": { "_id": { "type": "string", - "default": "de2e5e17-70ee-4c27-b01a-c38bc6c3e4e2" + "default": "afdd27df-e56f-42b5-a76b-668bb791375b" }, "instanceId": { "type": "string", - "default": "26be8bd5-5d6f-4bbd-8878-b14675fc8dd5" + "default": "f1641d0f-9595-4677-9949-b29dbceaf3e7" }, "archetypeId": { "type": "string", @@ -630,7 +630,7 @@ "SNOW_LEOPARD", "CHIMERA", "SLOW_MOVER", - "SNAKE_OF_WATER", + "SNAKE_OF_THE_WATER", "BANDIT", "ROWAN", "CHUBBS" @@ -647,11 +647,11 @@ "properties": { "id": { "type": "string", - "default": "be51385e-9c08-40ba-b92c-d6b3790db555" + "default": "1077d648-a6aa-468b-86f7-788e42421f90" }, "instanceId": { "type": "string", - "default": "2e308a56-a26d-4513-ba79-2a412f5c113b" + "default": "7b6744b0-f364-4833-9c5d-2273361f32b1" }, "place": { "type": "string", @@ -674,7 +674,7 @@ "properties": { "characterSheetId": { "type": "string", - "default": "8eddefd6-612c-421b-a033-ac5234a330e4" + "default": "88623540-0638-4e3d-8e15-a86792290835" }, "affiliationId": { "type": "string", diff --git a/services/character-sheet/package.json b/services/character-sheet/package.json index f2f62562..c079132c 100644 --- a/services/character-sheet/package.json +++ b/services/character-sheet/package.json @@ -40,7 +40,6 @@ "aws-lambda": "1.0.7", "aws-serverless-express": "~3.4.0", "class-transformer": "0.5.1", - "class-validator": "0.13.2", "express": "4.18.2", "node-cache": "5.1.2", "reflect-metadata": "0.1.13", diff --git a/services/weather-control/src/module/weather/query.dto.ts b/services/weather-control/src/module/weather/query.dto.ts index ee831106..fd5a4f76 100644 --- a/services/weather-control/src/module/weather/query.dto.ts +++ b/services/weather-control/src/module/weather/query.dto.ts @@ -1,7 +1,16 @@ import { IsLongitude, IsLatitude } from '@cats-cradle/validation-schemas'; import { ApiProperty } from '@nestjs/swagger'; +import { IsDate } from 'class-validator'; export class QueryDto { + @IsDate() + @ApiProperty({ + description: 'Date', + default: new Date(), + type: String, + }) + date: string; + @IsLatitude() @ApiProperty({ description: 'Latitude', diff --git a/services/weather-control/src/module/weather/weather.controller.ts b/services/weather-control/src/module/weather/weather.controller.ts index 4765e600..c61fa40f 100644 --- a/services/weather-control/src/module/weather/weather.controller.ts +++ b/services/weather-control/src/module/weather/weather.controller.ts @@ -8,16 +8,18 @@ import { Query, } from '@nestjs/common'; import { WeatherService } from './weather.service'; +import { QueryDto } from './query.dto'; @Controller({ path: 'weather', version: ['1', VERSION_NEUTRAL] }) export class WeatherController { constructor(private readonly weatherService: WeatherService) {} @Post() - async fetch( - @Query('latitude') latitude: number, - @Query('longitude') longitude: number, - ) { - return this.weatherService.get(latitude, longitude); + async fetch(@Body() body: QueryDto) { + return this.weatherService.get( + new Date(body.date), + body.latitude, + body.longitude, + ); } } diff --git a/services/weather-control/src/module/weather/weather.e2e-spec.ts b/services/weather-control/src/module/weather/weather.e2e-spec.ts index 2b389b78..6f94ee3c 100644 --- a/services/weather-control/src/module/weather/weather.e2e-spec.ts +++ b/services/weather-control/src/module/weather/weather.e2e-spec.ts @@ -1,11 +1,12 @@ import supertest from 'supertest'; import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication, Injectable } from '@nestjs/common'; +import { INestApplication } from '@nestjs/common'; import { FakerFactory } from '@cats-cradle/faker-factory'; import { WeatherService } from './weather.service'; import { WeatherController } from './weather.controller'; import { ClimateType } from './climates.type'; import { QueryDto } from './query.dto'; +import { TimeOfDayType } from './time-of-day.type'; describe('/weather', () => { let app: INestApplication; @@ -30,21 +31,63 @@ describe('/weather', () => { }); describe('POST /weather', () => { - it('should determine climate', async () => { - const body = await FakerFactory.create(QueryDto, { - // longitude: '0', - }); - console.log(body); - console.log(body); - - const response = await supertest(app.getHttpServer()) - .post('/weather') - .send(body) - .expect(201); - - expect(response.body).toMatchObject({ - climate: ClimateType.POLAR, - }); - }); + it.each([ + [0, TimeOfDayType.MIDNIGHT], + [2, TimeOfDayType.MIDNIGHT], + [5, TimeOfDayType.DAWN], + [8, TimeOfDayType.MORNING], + [12, TimeOfDayType.NOON], + [17, TimeOfDayType.EVENING], + [20, TimeOfDayType.DUSK], + [23, TimeOfDayType.MIDNIGHT], + ])( + 'should determine hour %i as %s', + async (hour: number, timeOfDay: TimeOfDayType) => { + const now = new Date(); + now.setHours(hour); + + const body = await FakerFactory.create(QueryDto, { + date: now.toISOString(), + }); + + const response = await supertest(app.getHttpServer()) + .post('/weather') + .send(body) + .expect(201); + + expect(response.body).toMatchObject({ + timeOfDay, + }); + }, + ); + + it.each([ + [60, ClimateType.POLAR], + [40, ClimateType.CONTINENTAL], + [25, ClimateType.TEMPERATE], + [20, ClimateType.DRY], + [-15, ClimateType.TROPICAL], + [-20, ClimateType.DRY], + [-25, ClimateType.TEMPERATE], + [-40, ClimateType.CONTINENTAL], + [-60, ClimateType.POLAR], + [-90, ClimateType.POLAR], + ])( + 'should determine climate %i as %s', + async (latitude: number, climate: ClimateType) => { + const body = await FakerFactory.create(QueryDto, { + latitude: latitude.toString(), + }); + + const response = await supertest(app.getHttpServer()) + .post('/weather') + .send(body) + .expect(201); + + expect(response.body).toMatchObject({ + climate, + }); + }, + ); }); }); diff --git a/services/weather-control/src/module/weather/weather.service.ts b/services/weather-control/src/module/weather/weather.service.ts index ca15d011..b8586fff 100644 --- a/services/weather-control/src/module/weather/weather.service.ts +++ b/services/weather-control/src/module/weather/weather.service.ts @@ -1,37 +1,70 @@ /* eslint @typescript-eslint/no-var-requires: "off" */ import { Injectable } from '@nestjs/common'; import { ClimateType } from './climates.type'; +import { TimeOfDayType } from './time-of-day.type'; @Injectable() export class WeatherService { - async get(longitude: number, latitude: number): Promise { - const climate = this.getClimate(latitude); + async get( + date: Date, + latitude: number | string, + longitude: number | string, + ): Promise { return { - climate, + timeOfDay: this.getTimeOfDay(date), + climate: this.getClimate(latitude), }; } + // time of day effects are based on time + getTimeOfDay(date?: Date): TimeOfDayType { + if (date === undefined) { + date = new Date(); + } + const hour = date.getHours(); + + switch (true) { + case hour >= 0 && hour < 4: + return TimeOfDayType.MIDNIGHT; + case hour >= 4 && hour < 6: + return TimeOfDayType.DAWN; + case hour >= 6 && hour < 10: + return TimeOfDayType.MORNING; + case hour >= 10 && hour < 16: + return TimeOfDayType.NOON; + case hour >= 16 && hour < 20: + return TimeOfDayType.EVENING; + case hour >= 20 && hour < 21: + return TimeOfDayType.DUSK; + case hour >= 21 && hour < 24: + default: + return TimeOfDayType.MIDNIGHT; + } + } + // ranges from 90, -90 angle (180 degrees mirrored on both sides of world) // pattern is generally polar climate, temperate and continental, dry climates, tropical climate - getClimate(latitude: number) { + getClimate(latitude: number | string): ClimateType { + const value = Number(latitude); + switch (true) { - case latitude > 60: + case value >= 60: return ClimateType.POLAR; - case latitude > 40: + case value >= 40: return ClimateType.CONTINENTAL; - case latitude > 25: + case value >= 25: return ClimateType.TEMPERATE; - case latitude > 20: + case value >= 20: return ClimateType.DRY; - case latitude > -15: + case value >= -15: return ClimateType.TROPICAL; - case latitude > -20: + case value >= -20: return ClimateType.DRY; - case latitude > -25: + case value >= -25: return ClimateType.TEMPERATE; - case latitude > -40: + case value >= -40: return ClimateType.CONTINENTAL; - case latitude < -60: + case value <= -60: default: return ClimateType.POLAR; }