Skip to content
This repository was archived by the owner on Apr 23, 2024. It is now read-only.

Commit c65b41b

Browse files
author
André Scheibel de Almada
committed
feat: add initial swagger docs
1 parent a0455c4 commit c65b41b

15 files changed

+1341
-169
lines changed

Diff for: package.json

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@nestjs/core": "^10.0.0",
2525
"@nestjs/mongoose": "^10.0.0",
2626
"@nestjs/platform-express": "^10.0.0",
27+
"@nestjs/swagger": "7.1.1",
2728
"csv-parse": "5.4.0",
2829
"mongoose": "^7.3.1",
2930
"reflect-metadata": "^0.1.13",
@@ -44,6 +45,7 @@
4445
"eslint-config-prettier": "^8.8.0",
4546
"eslint-plugin-prettier": "^4.2.1",
4647
"jest": "^29.5.0",
48+
"mongodb-memory-server": "8.13.0",
4749
"prettier": "^2.8.8",
4850
"source-map-support": "^0.5.21",
4951
"supertest": "^6.3.3",

Diff for: src/app.controller.spec.ts

-22
This file was deleted.

Diff for: src/app.controller.ts

-12
This file was deleted.

Diff for: src/app.module.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ import { MongooseModule } from '@nestjs/mongoose';
44

55
import { FishesModule } from './fishes/fishes.module';
66

7-
import { AppController } from './app.controller';
8-
import { AppService } from './app.service';
9-
107
@Module({
118
imports: [
129
ConfigModule.forRoot(),
@@ -16,7 +13,7 @@ import { AppService } from './app.service';
1613
),
1714
FishesModule,
1815
],
19-
controllers: [AppController],
20-
providers: [AppService],
16+
controllers: [],
17+
providers: [],
2118
})
2219
export class AppModule {}

Diff for: src/app.service.ts

-8
This file was deleted.

Diff for: src/fishes/dto/fish.dto.ts

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { IFishLocation } from '../interfaces/fish.interface';
3+
4+
const monthProperty = {
5+
oneOf: [
6+
{
7+
type: 'array',
8+
items: {
9+
type: 'string',
10+
enum: [
11+
'All day',
12+
'9 AM - 4 PM',
13+
'4 PM - 9 AM',
14+
'9 PM - 4 AM',
15+
'4 AM - 9 PM',
16+
],
17+
},
18+
},
19+
{ type: 'undefined' },
20+
],
21+
};
22+
const availabilityProperties = {
23+
january: monthProperty,
24+
february: monthProperty,
25+
march: monthProperty,
26+
april: monthProperty,
27+
may: monthProperty,
28+
june: monthProperty,
29+
july: monthProperty,
30+
august: monthProperty,
31+
september: monthProperty,
32+
october: monthProperty,
33+
november: monthProperty,
34+
december: monthProperty,
35+
};
36+
37+
class Availability {
38+
@ApiProperty({
39+
type: 'object',
40+
properties: availabilityProperties,
41+
})
42+
northern_hemisphere: Object;
43+
44+
@ApiProperty({
45+
type: 'object',
46+
properties: availabilityProperties,
47+
})
48+
southern_hemisphere: Object;
49+
}
50+
51+
export class FishDto {
52+
@ApiProperty()
53+
name: string;
54+
55+
@ApiProperty()
56+
icon_image: string;
57+
58+
@ApiProperty()
59+
critterpedia_image: string;
60+
61+
@ApiProperty()
62+
furniture_image: string;
63+
64+
@ApiProperty()
65+
minimum_catches_to_spawn: number;
66+
67+
@ApiProperty()
68+
sale_price: number;
69+
70+
@ApiProperty()
71+
spawn_frequency: string;
72+
73+
@ApiProperty({
74+
enum: [
75+
'River',
76+
'Pond',
77+
'River (clifftop)',
78+
'River (mouth)',
79+
'Sea',
80+
'Pier',
81+
'Sea (rainy days)',
82+
],
83+
})
84+
location: IFishLocation;
85+
86+
@ApiProperty()
87+
shadow_size: string;
88+
89+
@ApiProperty()
90+
blathers_description: string;
91+
92+
@ApiProperty()
93+
catch_phrase: string;
94+
95+
@ApiProperty()
96+
availability: Availability;
97+
}

Diff for: src/fishes/fishes.controller.spec.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { Test, TestingModule } from '@nestjs/testing';
2+
import { getModelToken } from '@nestjs/mongoose';
3+
import { MongoMemoryServer } from 'mongodb-memory-server';
4+
import { Connection, connect, Model } from 'mongoose';
5+
6+
import { FishesController } from './fishes.controller';
7+
import { FishesService } from './fishes.service';
8+
import { Fish, FishSchema } from './schemas/fish.schema';
9+
import { FishDtoStub } from '../../test/stubs/fish.dto.stub';
10+
11+
describe('FishesController', () => {
12+
let fishesController: FishesController;
13+
let mongodb: MongoMemoryServer;
14+
let mongoConnection: Connection;
15+
let fishModel: Model<Fish>;
16+
17+
beforeAll(async () => {
18+
mongodb = await MongoMemoryServer.create();
19+
const uri = mongodb.getUri();
20+
mongoConnection = (await connect(uri)).connection;
21+
fishModel = mongoConnection.model('Fish', FishSchema);
22+
const app: TestingModule = await Test.createTestingModule({
23+
controllers: [FishesController],
24+
providers: [
25+
FishesService,
26+
{ provide: getModelToken('Fish'), useValue: fishModel },
27+
],
28+
}).compile();
29+
fishesController = app.get<FishesController>(FishesController);
30+
});
31+
32+
afterAll(async () => {
33+
await mongoConnection.dropDatabase();
34+
await mongoConnection.close();
35+
await mongodb.stop();
36+
});
37+
38+
afterEach(async () => {
39+
const collections = mongoConnection.collections;
40+
for (const key in collections) {
41+
const collection = collections[key];
42+
await collection.deleteMany({});
43+
}
44+
});
45+
46+
describe('getFishes', () => {
47+
it('should return all fishes in database', async () => {
48+
await new fishModel(FishDtoStub()).save();
49+
const fishes = await fishesController.getFishes({});
50+
expect(fishes).toHaveLength(1);
51+
});
52+
});
53+
});

Diff for: src/fishes/fishes.controller.ts

+87-43
Original file line numberDiff line numberDiff line change
@@ -6,68 +6,107 @@ import {
66
HttpException,
77
HttpStatus,
88
} from '@nestjs/common';
9+
import {
10+
ApiOperation,
11+
ApiTags,
12+
ApiOkResponse,
13+
ApiQuery,
14+
ApiBadRequestResponse,
15+
} from '@nestjs/swagger';
916

1017
import { FishesService } from './fishes.service';
11-
import { Fish, IMonth } from './schemas/fish.schema';
12-
import { IQueryParams } from './interfaces/fish.interface';
1318

19+
import { Fish } from './schemas/fish.schema';
20+
import { IQueryParams, IMonth } from './interfaces/fish.interface';
21+
import { FishDto } from './dto/fish.dto';
22+
23+
const hemispheres = ['northern_hemisphere', 'southern_hemisphere'];
24+
const months = [
25+
'january',
26+
'february',
27+
'march',
28+
'april',
29+
'may',
30+
'june',
31+
'july',
32+
'august',
33+
'september',
34+
'october',
35+
'november',
36+
'december',
37+
];
38+
const times = [
39+
'12 AM',
40+
'1 AM',
41+
'2 AM',
42+
'3 AM',
43+
'4 AM',
44+
'5 AM',
45+
'6 AM',
46+
'7 AM',
47+
'8 AM',
48+
'9 AM',
49+
'10 AM',
50+
'11 AM',
51+
'12 PM',
52+
'1 PM',
53+
'2 PM',
54+
'3 PM',
55+
'4 PM',
56+
'5 PM',
57+
'6 PM',
58+
'7 PM',
59+
'8 PM',
60+
'9 PM',
61+
'10 PM',
62+
'11 PM',
63+
];
64+
65+
@ApiTags('Fishes')
1466
@Controller('fishes')
1567
export class FishesController {
1668
constructor(private fishesService: FishesService) {}
1769

1870
// Query params verifiers
1971
private isOfTypeIMonth(month: string): month is IMonth {
20-
return [
21-
'january',
22-
'february',
23-
'march',
24-
'april',
25-
'may',
26-
'june',
27-
'july',
28-
'august',
29-
'september',
30-
'october',
31-
'november',
32-
'december',
33-
].includes(month);
72+
return months.includes(month);
3473
}
3574

3675
private isValidHemisphere(hemisphere: string): boolean {
37-
return ['northern_hemisphere', 'southern_hemisphere'].includes(hemisphere);
76+
return hemispheres.includes(hemisphere);
3877
}
3978

4079
private isValidTime(time: string): boolean {
41-
return [
42-
'12 AM',
43-
'1 AM',
44-
'2 AM',
45-
'3 AM',
46-
'4 AM',
47-
'5 AM',
48-
'6 AM',
49-
'7 AM',
50-
'8 AM',
51-
'9 AM',
52-
'10 AM',
53-
'11 AM',
54-
'12 PM',
55-
'1 PM',
56-
'2 PM',
57-
'3 PM',
58-
'4 PM',
59-
'5 PM',
60-
'6 PM',
61-
'7 PM',
62-
'8 PM',
63-
'9 PM',
64-
'10 PM',
65-
'11 PM',
66-
].includes(time);
80+
return times.includes(time);
6781
}
6882

6983
// Routes
7084
@Get()
85+
@ApiOperation({ summary: 'Obtain all fishes' })
86+
@ApiQuery({
87+
name: 'hemisphere',
88+
enum: hemispheres,
89+
required: false,
90+
})
91+
@ApiQuery({
92+
name: 'month',
93+
enum: months,
94+
required: false,
95+
})
96+
@ApiQuery({
97+
name: 'time',
98+
enum: times,
99+
required: false,
100+
})
101+
@ApiOkResponse({
102+
description: "The found fish records by request's criteria",
103+
type: FishDto,
104+
isArray: true,
105+
})
106+
@ApiBadRequestResponse({
107+
description:
108+
'Invalid query param informed or searching by active hours without informing a month',
109+
})
71110
public getFishes(@Query() query: IQueryParams): Promise<Fish[]> {
72111
if (query.month && !this.isOfTypeIMonth(query.month))
73112
throw new HttpException(
@@ -97,6 +136,11 @@ export class FishesController {
97136
}
98137

99138
@Get(':name')
139+
@ApiOkResponse({
140+
description: 'The found fish record, or null, if not found',
141+
type: FishDto,
142+
})
143+
@ApiOperation({ summary: 'Obtain all information from a specific fish' })
100144
public getFish(@Param('name') name: string): Promise<Fish | null> {
101145
return this.fishesService.findOne(name);
102146
}

0 commit comments

Comments
 (0)