Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] [backend] Set up Swagger #25

Merged
merged 8 commits into from
Nov 4, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
"@nestjs/core": "^10.0.0",
"@nestjs/mapped-types": "*",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.1.14",
"@prisma/client": "5.5.0",
"bcrypt": "^5.1.1",
"pg-promise": "^11.5.4",
"prisma": "^5.5.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1"
"rxjs": "^7.8.1",
"swagger-ui-express": "^5.0.0"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
Expand Down
3 changes: 2 additions & 1 deletion backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { PrismaModule } from './prisma/prisma.module';

@Module({
imports: [UserModule],
imports: [UserModule, PrismaModule],
controllers: [AppController],
providers: [AppService],
})
Expand Down
11 changes: 11 additions & 0 deletions backend/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors(); // enable CORS
app.setGlobalPrefix('api');

const config = new DocumentBuilder()
.setTitle('Pong API')
.setDescription('The Pong API description')
.setVersion('0.1')
.build();

const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);

await app.listen(process.env.PORT);
}
bootstrap();
Comment on lines 5 to 21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bootstrap function is correctly set up and the Swagger documentation is correctly configured and set up. However, it's a good practice to add error handling for the app.listen() function. This will help catch and handle any errors that might occur when the application tries to listen on the specified port.

- await app.listen(process.env.PORT);
+ try {
+   await app.listen(process.env.PORT);
+ } catch (error) {
+   console.error(`Failed to bind port ${process.env.PORT}`, error);
+ }

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors(); // enable CORS
app.setGlobalPrefix('api');
const config = new DocumentBuilder()
.setTitle('Pong API')
.setDescription('The Pong API description')
.setVersion('0.1')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(process.env.PORT);
}
bootstrap();
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors(); // enable CORS
app.setGlobalPrefix('api');
const config = new DocumentBuilder()
.setTitle('Pong API')
.setDescription('The Pong API description')
.setVersion('0.1')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
try {
await app.listen(process.env.PORT);
} catch (error) {
console.error(`Failed to bind port ${process.env.PORT}`, error);
}
}
bootstrap();

9 changes: 0 additions & 9 deletions backend/src/prisma.service.ts

This file was deleted.

8 changes: 8 additions & 0 deletions backend/src/prisma/prisma.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
18 changes: 18 additions & 0 deletions backend/src/prisma/prisma.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { PrismaService } from './prisma.service';

describe('PrismaService', () => {
let service: PrismaService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PrismaService],
}).compile();

service = module.get<PrismaService>(PrismaService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});
});
5 changes: 5 additions & 0 deletions backend/src/prisma/prisma.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient {}
13 changes: 12 additions & 1 deletion backend/src/user/dto/create-user.dto.ts
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
export class CreateUserDto {}
import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
@ApiProperty()
email: string;

@ApiProperty({ required: false })
name?: string;

@ApiProperty()
password: string;
}
17 changes: 16 additions & 1 deletion backend/src/user/entities/user.entity.ts
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
export class User {}
import { User } from '@prisma/client';
import { ApiProperty } from '@nestjs/swagger';

export class UserEntity implements User {
@ApiProperty()
id: number;

@ApiProperty()
email: string;

@ApiProperty({ required: false, nullable: true })
name: string | null;

@ApiProperty()
password: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UserEntity class is correctly implementing the User interface and the ApiProperty decorators are correctly used to provide API documentation. However, it's not a good practice to expose sensitive data like passwords in the API documentation. Consider removing the ApiProperty decorator from the password field.

-  @ApiProperty()
  password: string;

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
import { User } from '@prisma/client';
import { ApiProperty } from '@nestjs/swagger';
export class UserEntity implements User {
@ApiProperty()
id: number;
@ApiProperty()
email: string;
@ApiProperty({ required: false, nullable: true })
name: string | null;
@ApiProperty()
password: string;
}
import { User } from '@prisma/client';
import { ApiProperty } from '@nestjs/swagger';
export class UserEntity implements User {
@ApiProperty()
id: number;
@ApiProperty()
email: string;
@ApiProperty({ required: false, nullable: true })
name: string | null;
- // @ApiProperty()
password: string;
}

2 changes: 1 addition & 1 deletion backend/src/user/user.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { PrismaService } from 'src/prisma.service';
import { PrismaService } from 'src/prisma/prisma.service';

describe('UserController', () => {
let controller: UserController;
Expand Down
27 changes: 18 additions & 9 deletions backend/src/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,48 @@ import {
Delete,
} from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User as UserModel } from '@prisma/client';
import {
ApiCreatedResponse,
ApiOkResponse,
ApiNoContentResponse,
ApiTags,
} from '@nestjs/swagger';
import { UserEntity } from './entities/user.entity';

@Controller('user')
@ApiTags('user')
export class UserController {
constructor(private readonly userService: UserService) {}

@Post()
create(
@Body() userData: { name?: string; email: string; password: string },
): Promise<UserModel> {
return this.userService.create(userData);
@ApiCreatedResponse({ type: UserEntity })
create(@Body() createUserDto: CreateUserDto): Promise<UserModel> {
return this.userService.create(createUserDto);
}

@Get()
@ApiOkResponse({ type: [UserEntity] })
findAll() {
return this.userService.findAll();
}

@Get(':id')
@ApiOkResponse({ type: UserEntity })
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}
Comment on lines 39 to 43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The findOne method is correctly decorated with @ApiOkResponse({ type: UserEntity }) to indicate the response schema in the Swagger UI. However, the +id operation is used to convert the id from a string to a number. If the id is not a number, this operation will return NaN, which could lead to unexpected behavior. If the id is expected to be a number, it would be better to parse it with parseInt(id, 10) and handle the case where id is not a number. If the id is not expected to be a number, the + operation should be removed.

- return this.userService.findOne(+id);
+ return this.userService.findOne(parseInt(id, 10));

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
@Get(':id')
@ApiOkResponse({ type: UserEntity })
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}
@Get(':id')
@ApiOkResponse({ type: UserEntity })
findOne(@Param('id') id: string) {
return this.userService.findOne(parseInt(id, 10));
}

Comment on lines 41 to 43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The +id operation is used to convert the id from a string to a number. If the id is not a number, this operation will return NaN, which could lead to unexpected behavior. If the id is expected to be a number, it would be better to parse it with parseInt(id, 10) and handle the case where id is not a number. If the id is not expected to be a number, the + operation should be removed.

- return this.userService.findOne(+id);
+ return this.userService.findOne(parseInt(id, 10));

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}
findOne(@Param('id') id: string) {
return this.userService.findOne(parseInt(id, 10));
}


@Patch(':id')
update(
@Param('id') id: string,
@Body() userData: { name?: string; email?: string; password?: string },
) {
return this.userService.update(+id, userData);
@ApiOkResponse({ type: UserEntity })
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(+id, updateUserDto);
}
Comment on lines 45 to 49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update method is correctly decorated with @ApiOkResponse({ type: UserEntity }) to indicate the response schema in the Swagger UI. The method signature is updated to accept updateUserDto of type UpdateUserDto. The same issue with the +id operation as in the findOne method applies here.

- return this.userService.update(+id, updateUserDto);
+ return this.userService.update(parseInt(id, 10), updateUserDto);

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
@Patch(':id')
update(
@Param('id') id: string,
@Body() userData: { name?: string; email?: string; password?: string },
) {
return this.userService.update(+id, userData);
@ApiOkResponse({ type: UserEntity })
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(+id, updateUserDto);
}
@Patch(':id')
@ApiOkResponse({ type: UserEntity })
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(parseInt(id, 10), updateUserDto);
}

Comment on lines +47 to 49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same issue with the +id operation as in the findOne method applies here.

- return this.userService.update(+id, updateUserDto);
+ return this.userService.update(parseInt(id, 10), updateUserDto);

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(+id, updateUserDto);
}
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(parseInt(id, 10), updateUserDto);
}


@Delete(':id')
@ApiNoContentResponse()
remove(@Param('id') id: string) {
return this.userService.remove(+id);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The +id operation is used to convert the id from a string to a number. If the id is not a number, this operation will return NaN, which could lead to unexpected behavior. If the id is expected to be a number, it would be better to parse it with parseInt(id, 10) and handle the case where id is not a number. If the id is not expected to be a number, the + operation should be removed.

- return this.userService.findOne(+id);
+ return this.userService.findOne(parseInt(id, 10));

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
return this.userService.remove(+id);
return this.userService.findOne(parseInt(id, 10));

}
Comment on lines 51 to 55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remove method is correctly decorated with @ApiNoContentResponse() to indicate that no content is returned in the response. The same issue with the +id operation as in the findOne method applies here.

- return this.userService.remove(+id);
+ return this.userService.remove(parseInt(id, 10));

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
@Delete(':id')
@ApiNoContentResponse()
remove(@Param('id') id: string) {
return this.userService.remove(+id);
}
@Delete(':id')
@ApiNoContentResponse()
remove(@Param('id') id: string) {
return this.userService.remove(parseInt(id, 10));
}

Comment on lines 53 to 55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same issue with the +id operation as in the findOne method applies here.

- return this.userService.remove(+id);
+ return this.userService.remove(parseInt(id, 10));

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
remove(@Param('id') id: string) {
return this.userService.remove(+id);
}
remove(@Param('id') id: string) {
return this.userService.remove(parseInt(id, 10));
}

Expand Down
5 changes: 3 additions & 2 deletions backend/src/user/user.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { PrismaService } from 'src/prisma.service';
import { PrismaModule } from 'src/prisma/prisma.module';

@Module({
controllers: [UserController],
providers: [UserService, PrismaService],
providers: [UserService],
imports: [PrismaModule],
})
export class UserModule {}
2 changes: 1 addition & 1 deletion backend/src/user/user.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
import { PrismaService } from 'src/prisma.service';
import { PrismaService } from 'src/prisma/prisma.service';

describe('UserService', () => {
let service: UserService;
Expand Down
19 changes: 10 additions & 9 deletions backend/src/user/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { PrismaService } from 'src/prisma.service';
import { PrismaService } from 'src/prisma/prisma.service';
import { User, Prisma } from '@prisma/client';
import bcrypt from 'bcrypt';
import { hash } from 'bcrypt';

@Injectable()
export class UserService {
constructor(private prisma: PrismaService) {}

async create(data: Prisma.UserCreateInput): Promise<User> {
async create(createUserDto: CreateUserDto): Promise<User> {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(data.password, saltRounds);
data.password = hashedPassword;
return this.prisma.user.create({ data });
const hashedPassword = await hash(createUserDto.password, saltRounds);
createUserDto.password = hashedPassword;
return this.prisma.user.create({ data: createUserDto });
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The create function now accepts a CreateUserDto object and uses the hash function to hash the password. This is a good security practice. However, it's important to note that the createUserDto object is being mutated here. If this object is used elsewhere after this function call, it might lead to unexpected behavior. Consider creating a copy of the object before mutating it.

  async create(createUserDto: CreateUserDto): Promise<User> {
    const saltRounds = 10;
    const hashedPassword = await hash(createUserDto.password, saltRounds);
-   createUserDto.password = hashedPassword;
+   const userDtoWithHashedPassword = { ...createUserDto, password: hashedPassword };
-   return this.prisma.user.create({ data: createUserDto });
+   return this.prisma.user.create({ data: userDtoWithHashedPassword });
  }

Commitable suggestion

[!IMPORTANT]
Carefully review the code before committing. Make sure it correctly replaces the highlighted code, has no missing lines and indentaion issues.

Suggested change
async create(createUserDto: CreateUserDto): Promise<User> {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(data.password, saltRounds);
data.password = hashedPassword;
return this.prisma.user.create({ data });
const hashedPassword = await hash(createUserDto.password, saltRounds);
createUserDto.password = hashedPassword;
return this.prisma.user.create({ data: createUserDto });
}
async create(createUserDto: CreateUserDto): Promise<User> {
const saltRounds = 10;
const hashedPassword = await hash(createUserDto.password, saltRounds);
const userDtoWithHashedPassword = { ...createUserDto, password: hashedPassword };
return this.prisma.user.create({ data: userDtoWithHashedPassword });
}


findAll() {
return this.prisma.user.findMany();
}

findOne(id: number) {
return this.prisma.user.findFirst({ where: { id: id } });
return this.prisma.user.findUnique({ where: { id: id } });
}

update(id: number, data: Prisma.UserUpdateInput) {
update(id: number, updateUserDto: UpdateUserDto) {
return this.prisma.user.update({
data,
where: { id: id },
data: updateUserDto,
});
}

Expand Down
46 changes: 37 additions & 9 deletions backend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@
path-to-regexp "3.2.0"
tslib "2.6.2"

"@nestjs/mapped-types@*":
"@nestjs/mapped-types@*", "@nestjs/[email protected]":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz#c8a090a8d22145b85ed977414c158534210f2e4f"
integrity sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg==
Expand All @@ -770,6 +770,17 @@
jsonc-parser "3.2.0"
pluralize "8.0.0"

"@nestjs/swagger@^7.1.14":
version "7.1.14"
resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-7.1.14.tgz#492b3816308264472b3619f5c0336f378f1c9995"
integrity sha512-2Ol4S6qHeYVVmkshkWBM8E/qkmEqEOUj2QIewr0jLSyo30H7f3v81pJyks6pTLy4PK0LGUXojMvIfFIE3mmGQQ==
dependencies:
"@nestjs/mapped-types" "2.0.2"
js-yaml "4.1.0"
lodash "4.17.21"
path-to-regexp "3.2.0"
swagger-ui-dist "5.9.0"

"@nestjs/testing@^10.0.0":
version "10.2.7"
resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-10.2.7.tgz#50408ccb4c809d216a12d60ac7932fd6ad7fedf4"
Expand Down Expand Up @@ -3589,6 +3600,13 @@ js-tokens@^4.0.0:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==

[email protected], js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"

js-yaml@^3.13.1:
version "3.14.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
Expand All @@ -3597,13 +3615,6 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"

js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
dependencies:
argparse "^2.0.1"

jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
Expand Down Expand Up @@ -3712,7 +3723,7 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==

lodash@^4.17.21:
lodash@4.17.21, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
Expand Down Expand Up @@ -4955,6 +4966,23 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==

[email protected]:
version "5.9.0"
resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.9.0.tgz#d52b6cf52fd0a8e6930866c402aaa793fe4e3f76"
integrity sha512-NUHSYoe5XRTk/Are8jPJ6phzBh3l9l33nEyXosM17QInoV95/jng8+PuSGtbD407QoPf93MH3Bkh773OgesJpA==

swagger-ui-dist@>=5.0.0:
version "5.9.1"
resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.9.1.tgz#d0bcd614e3752da02df141846348f84468ae815e"
integrity sha512-5zAx+hUwJb9T3EAntc7TqYkV716CMqG6sZpNlAAMOMWkNXRYxGkN8ADIvD55dQZ10LxN90ZM/TQmN7y1gpICnw==

swagger-ui-express@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz#7a00a18dd909574cb0d628574a299b9ba53d4d49"
integrity sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==
dependencies:
swagger-ui-dist ">=5.0.0"

[email protected]:
version "4.0.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
Expand Down