Skip to content

Commit

Permalink
Add specification
Browse files Browse the repository at this point in the history
  • Loading branch information
Karlen-ll committed Oct 31, 2024
1 parent 84848f4 commit cb88ad1
Show file tree
Hide file tree
Showing 20 changed files with 640 additions and 2 deletions.
339 changes: 339 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.2.1",
"json-server": "0.17.3",
"nodemon": "3.1.7",
"pino-pretty": "11.2.2",
"prettier": "3.3.3",
"rimraf": "5.0.1",
Expand Down
101 changes: 101 additions & 0 deletions specification/specification.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
openapi: 3.1.0

info:
title: API-сервер для демо-проекта «Шесть городов»
description: Список ресурсов и маршрутов сервера «Шесть городов»
license:
name: MIT
url: https://opensource.org/licenses/MIT
version: 2.0.0

tags:
- name: offers
description: Действия с объявлениями.
- name: categories
description: Действия с категориями.
- name: comments
description: Действия с комментариями.
- name: users
description: Действия с пользователем.

paths:
/users/register:
post:
tags:
- users
summary: Регистрация пользователя
description: Регистрирует нового пользователя.

requestBody:
description: Информация для создания нового пользователя.
content:
application/json:
schema:
$ref: '#/components/schemas/createUser'
required: true

responses:
"201":
description: Пользователь зарегистрирован. Объект пользователя.
content:
application/json:
schema:
$ref: '#/components/schemas/user'

"409":
description: Пользователь с таким email уже существует.

/users/login:
post:
tags:
- users
summary: Авторизация пользователя
description: Авторизует пользователя на основе логина и пароля

get:
tags:
- users
summary: Проверка состояния пользователя
description: Возвращает информацию по авторизованному пользователю

/users/{userId}/avatar:
post:
tags:
- users
summary: Загрузить изображение аватара
description: Загружает изображение аватара пользователя.
Изображение аватара должно быть в формате `png` или `jpg`.

components:
schemas:
createUser:
type: object

properties:
email:
type: string
example: [email protected]

firstname:
type: string
example: Keks

lastname:
type: string
example: Cat

password:
type: string
example: 123456

user:
type: object

properties:
id:
type: string
example: 6329c3d6a04ab1061c6425ea

email:
type: string
example: [email protected]
14 changes: 14 additions & 0 deletions src/shared/modules/category/category-service.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { DocumentType } from '@typegoose/typegoose';
import { CreateCategoryDto } from './dto/create-category.dto.js';
import { CategoryEntity } from './category.entity.js';

export interface CategoryService {
create(dto: CreateCategoryDto): Promise<DocumentType<CategoryEntity>>;
findByCategoryId(categoryId: string): Promise<DocumentType<CategoryEntity> | null>;
findByCategoryName(categoryName: string): Promise<DocumentType<CategoryEntity> | null>;
findByCategoryNameOrCreate(
categoryName: string,
dto: CreateCategoryDto
): Promise<DocumentType<CategoryEntity>>;
find(): Promise<DocumentType<CategoryEntity>[]>;
}
18 changes: 18 additions & 0 deletions src/shared/modules/category/category.container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Container } from 'inversify';
import { types } from '@typegoose/typegoose';

import { Component } from '../../types/index.js';
import { CategoryService } from './category-service.interface.js';
import { DefaultCategoryService } from './default-category.service.js';
import { CategoryEntity, CategoryModel } from './category.entity.js';

export function createCategoryContainer() {
const categoryContainer = new Container();

categoryContainer.bind<CategoryService>(Component.CategoryService).to(DefaultCategoryService);
categoryContainer
.bind<types.ModelType<CategoryEntity>>(Component.CategoryModel)
.toConstantValue(CategoryModel);

return categoryContainer;
}
14 changes: 14 additions & 0 deletions src/shared/modules/category/category.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { defaultClasses, getModelForClass, modelOptions, prop } from '@typegoose/typegoose';
import { Category } from '../../types/index.js';

export interface CategoryEntity extends defaultClasses.Base {}

@modelOptions({
schemaOptions: { collection: 'categories', timestamps: true },
})
export class CategoryEntity extends defaultClasses.TimeStamps implements Category {
@prop({ required: true, trim: true })
public name!: string;
}

export const CategoryModel = getModelForClass(CategoryEntity);
49 changes: 49 additions & 0 deletions src/shared/modules/category/default-category.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { inject, injectable } from 'inversify';
import { DocumentType, types } from '@typegoose/typegoose';

import { CategoryService } from './category-service.interface.js';
import { Component } from '../../types/index.js';
import { Logger } from '../../libs/logger/index.js';
import { CategoryEntity } from './category.entity.js';
import { CreateCategoryDto } from './dto/create-category.dto.js';

@injectable()
export class DefaultCategoryService implements CategoryService {
constructor(
@inject(Component.Logger) private readonly logger: Logger,
@inject(Component.CategoryModel) private readonly categoryModel: types.ModelType<CategoryEntity>
) {}

public async create(dto: CreateCategoryDto): Promise<DocumentType<CategoryEntity>> {
const result = await this.categoryModel.create(dto);
this.logger.info(`Новая категория создана: ${dto.name}`);
return result;
}

public async findByCategoryId(categoryId: string): Promise<DocumentType<CategoryEntity> | null> {
return this.categoryModel.findById(categoryId).exec();
}

public async findByCategoryName(
categoryName: string
): Promise<DocumentType<CategoryEntity> | null> {
return this.categoryModel.findOne({ name: categoryName }).exec();
}

public async findByCategoryNameOrCreate(
categoryName: string,
dto: CreateCategoryDto
): Promise<DocumentType<CategoryEntity>> {
const existedCategory = await this.findByCategoryName(categoryName);

if (existedCategory) {
return existedCategory;
}

return this.create(dto);
}

public async find(): Promise<DocumentType<CategoryEntity>[]> {
return this.categoryModel.find();
}
}
3 changes: 3 additions & 0 deletions src/shared/modules/category/dto/create-category.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export class CreateCategoryDto {
public name: string;
}
5 changes: 5 additions & 0 deletions src/shared/modules/category/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './category.entity.js';
export * from './dto/create-category.dto.js';
export * from './category-service.interface.js';
export * from './default-category.service.js';
export * from './category.container.js';
7 changes: 7 additions & 0 deletions src/shared/modules/comment/comment-service.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DocumentType } from '@typegoose/typegoose';
import { CreateCommentDto } from './dto/create-comment.dto.js';
import { CommentEntity } from './comment.entity.js';

export interface CommentService {
create(dto: CreateCommentDto): Promise<DocumentType<CommentEntity>>;
}
18 changes: 18 additions & 0 deletions src/shared/modules/comment/comment.container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Container } from 'inversify';
import { types } from '@typegoose/typegoose';

import { Component } from '../../types/index.js';
import { CommentService } from './comment-service.interface.js';
import { DefaultCommentService } from './default-comment.service.js';
import { CommentEntity, CommentModel } from './comment.entity.js';

export function createCommentContainer() {
const commentContainer = new Container();

commentContainer.bind<CommentService>(Component.CommentService).to(DefaultCommentService);
commentContainer
.bind<types.ModelType<CommentEntity>>(Component.CommentModel)
.toConstantValue(CommentModel);

return commentContainer;
}
23 changes: 23 additions & 0 deletions src/shared/modules/comment/comment.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { defaultClasses, getModelForClass, modelOptions, prop } from '@typegoose/typegoose';
import { Comment } from '../../types/index.js';

export interface CommentEntity extends defaultClasses.Base {}

@modelOptions({
schemaOptions: { collection: 'comment', timestamps: true },
})
export class CommentEntity extends defaultClasses.TimeStamps implements Comment {
@prop({ required: true, trim: true })
public text!: string;

@prop({ required: true })
public publishedAt!: string;

@prop({ required: true })
public rating!: number;

@prop({ required: true })
public authorId!: string;
}

export const CommentModel = getModelForClass(CommentEntity);
23 changes: 23 additions & 0 deletions src/shared/modules/comment/default-comment.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { inject, injectable } from 'inversify';
import { DocumentType, types } from '@typegoose/typegoose';

import { CommentService } from './comment-service.interface.js';
import { Component } from '../../types/index.js';
import { Logger } from '../../libs/logger/index.js';
import { CommentEntity } from './comment.entity.js';
import { CreateCommentDto } from './dto/create-comment.dto.js';

@injectable()
export class DefaultCommentService implements CommentService {
constructor(
@inject(Component.Logger) private readonly logger: Logger,
@inject(Component.CommentModel) private readonly CommentModel: types.ModelType<CommentEntity>
) {}

public async create(dto: CreateCommentDto): Promise<DocumentType<CommentEntity>> {
const result = await this.CommentModel.create(dto);

this.logger.info(`Новый комментарий создан: ${dto.text}`);
return result;
}
}
5 changes: 5 additions & 0 deletions src/shared/modules/comment/dto/create-comment.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class CreateCommentDto {
public text: string;
public offerId: string;
public userId: string;
}
5 changes: 5 additions & 0 deletions src/shared/modules/comment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './comment.entity.js';
export * from './dto/create-comment.dto.js';
export * from './comment-service.interface.js';
export * from './default-comment.service.js';
export * from './comment.container.js';
2 changes: 1 addition & 1 deletion src/shared/modules/user/default-user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class DefaultUserService implements UserService {
user.setPassword(dto.password, salt);

const result = await this.userModel.create(user);
this.logger.info(`New user created: ${result.email}`);
this.logger.info(`Новый пользователь создан: ${result.email}`);

return result;
}
Expand Down
3 changes: 3 additions & 0 deletions src/shared/types/category.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type Category = {
name: string;
}

Check failure on line 3 in src/shared/types/category.type.ts

View workflow job for this annotation

GitHub Actions / Check

Insert `;`
6 changes: 5 additions & 1 deletion src/shared/types/component.enum.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
export const Component = {
RestApplication: Symbol.for('RestApplication'),
DatabaseClient: Symbol.for('DatabaseClient'),
Logger: Symbol.for('Logger'),
Config: Symbol.for('Config'),

UserModel: Symbol.for('UserModel'),
UserService: Symbol.for('UserService'),
OfferModel: Symbol.for('OfferModel'),
OfferService: Symbol.for('OfferService'),
DatabaseClient: Symbol.for('DatabaseClient'),
CommentModel: Symbol.for('CommentModel'),
CommentService: Symbol.for('CommentService'),
CategoryModel: Symbol.for('CategoryModel'),
CategoryService: Symbol.for('CategoryService'),
} as const;
2 changes: 2 additions & 0 deletions src/shared/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
export * from './category.type.js';
export * from './comment.type.js';
export * from './common.type.js';
export * from './offer.type.js';
export * from './user.type.js';

export * from './component.enum.js';
export * from './sort-type.enum.js';

export * from './mock-server-data.type.js';
4 changes: 4 additions & 0 deletions src/shared/types/sort-type.enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum SortType {
Down = -1,
Up = 1,
}

0 comments on commit cb88ad1

Please sign in to comment.