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

Add documentation (#15) #16

Merged
merged 1 commit into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
63 changes: 61 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,69 @@

## How to launch

First of all, you need to launch PostgreSQL and Redis:

```bash
docker compose start
```
```

Then, start the REST-api:

```bash
sbt "project stub" run
sbt "project service" run
```

And finally, run the telegram bot:

```bash
sbt "project bot" run
```

## Internals

### Actual database schema

```postgresql
create table offers
(
id uuid primary key,
name text not null,
price integer not null,
description text not null,
status text not null
);

create table users
(
id uuid primary key,
name text not null,
login text not null unique,
status text not null,
password_hash text not null,
password_salt text not null
);

create table user_offers
(
offer_id uuid primary key,
user_id uuid not null references users(id)
);
```

### Authorization

Each user has login (telegram @login) and password, and, in order to use the service, he should get a session and then attach it to each request

Creating a session:

<img src="docs/create-session.png" width="600" alt="create-session"/>

Use session in some request:

<img src="docs/use-session.png" width="600" alt="use-session"/>

### Telegram-bot

State machine diagram:

<img src="docs/bot-state-machine.png" alt="bot-state-machine"/>
Binary file added docs/bot-state-machine.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/create-session.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/use-session.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ class AuthHandler[F[_]: Monad](override val authService: AuthService[F], userSer

private val signUp: ServerEndpoint[Any, F] =
endpoint.withApiErrors.post
.summary("Зарегистрировать нового пользователя")
.summary("Register a new user")
.in("api" / "v1" / "auth" / "sign-up")
.in(jsonBody[SignUpRequest])
.out(jsonBody[UserResponse])
.serverLogic(userService.createUser(_).value)

private val signIn: ServerEndpoint[Any, F] =
endpoint.withApiErrors.withLoginPassword.post
.summary("Обменять логин и пароль на сессию")
.summary("Get session by login and password")
.in("api" / "v1" / "auth" / "sign-in")
.out(jsonBody[SessionResponse])
.serverLogic(userId => _ => authService.getSession(userId).value)

private val whoAmI: ServerEndpoint[Any, F] =
endpoint.withApiErrors.get
.summary("Получить ID пользователя по сессии")
.summary("Get user ID by their session")
.in("api" / "v1" / "auth" / "whoami" / path[Session]("session"))
.out(jsonBody[UserIdResponse])
.serverLogic(authService.whoami(_).value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,29 @@ class OfferHandler[F[_]: Applicative](offerService: OfferService[F], override va

private val getOffer: ServerEndpoint[Any, F] =
endpoint.withApiErrors.get
.summary("Получить объявление по его id")
.summary("Get the offer by its ID")
.in("api" / "v1" / "offer" / path[OfferID]("offer-id"))
.out(jsonBody[OfferResponse])
.serverLogic(offerService.getOffer(_).value)

private val getOffers: ServerEndpoint[Any, F] =
endpoint.withApiErrors.get
.summary("Получить все объявления данного пользователя")
.summary("Get all offers of the specified user")
.in("api" / "v1" / "offer" / "user" / path[UserID]("user-id"))
.out(jsonBody[OffersResponse])
.serverLogic(offerService.getOffers(_).value)

private val createOffer: ServerEndpoint[Any, F] =
endpoint.withApiErrors.withSession.post
.summary("Создать объявление")
.summary("Create an offer")
.in("api" / "v1" / "offer")
.in(jsonBody[CreateOfferRequest])
.out(jsonBody[OfferResponse])
.serverLogic(userId => request => offerService.createOffer(userId, request).value)

private val updateOffer: ServerEndpoint[Any, F] =
endpoint.withApiErrors.withSession.put
.summary("Изменить объявление")
.summary("Update the offer")
.in("api" / "v1" / "offer" / path[OfferID]("offer-id"))
.in(jsonBody[UpdateOfferRequest])
.out(jsonBody[OfferResponse])
Expand All @@ -52,7 +52,7 @@ class OfferHandler[F[_]: Applicative](offerService: OfferService[F], override va

private val deleteOffer: ServerEndpoint[Any, F] =
endpoint.withApiErrors.withSession.delete
.summary("Удалить объявление")
.summary("Delete the offer")
.in("api" / "v1" / "offer" / path[OfferID]("offer-id"))
.out(jsonBody[OkResponse])
.serverLogic(userId => offerId => offerService.deleteOffer(userId, offerId).value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ class UserHandler[F[_]: Functor](userService: UserService[F], override val authS

private val getUser: ServerEndpoint[Any, F] =
endpoint.withApiErrors.get
.summary("Получить пользователя по его id")
.summary("Get the user by their ID")
.in("api" / "v1" / "user" / path[UserID]("user-id"))
.out(jsonBody[UserResponse])
.serverLogic(userService.getUser(_).value)

private val deleteUser: ServerEndpoint[Any, F] =
endpoint.withApiErrors.withSession.delete
.summary("Удалить свой профиль")
.summary("Delete own profile")
.in("api" / "v1" / "user")
.out(jsonBody[OkResponse])
.serverLogic(userId => _ => userService.deleteUser(userId).value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,48 @@ object SchemaInstances {
// model

implicit val schemaOfferStatus: Schema[OfferStatus] =
Schema.derivedEnumerationValue.description("Статус объявления")
Schema.derivedEnumerationValue.description("Offer status")

implicit val schemaOfferDescription: Schema[OfferDescription] =
Schema.derived.description("Задаваемые пользователем поля объявления")
Schema.derived.description("User-defined offer fields")

implicit val schemaOffer: Schema[Offer] =
Schema.derived.description("Объявление")
Schema.derived.description("Offer")

implicit val schemaUserStatus: Schema[UserStatus] =
Schema.derivedEnumerationValue.description("Статус пользователя")
Schema.derivedEnumerationValue.description("User status")

implicit val schemaUser: Schema[User] =
Schema.derived.description("Пользователь")
Schema.derived.description("User")

// requests

implicit val schemaSignUpRequest: Schema[SignUpRequest] =
Schema.derived.description("Запрос на регистрацию пользователя")
Schema.derived.description("Request to register a new user")

implicit val schemaUpdateOfferRequest: Schema[UpdateOfferRequest] =
Schema.derived.description("Запрос на изменение объявления")
Schema.derived.description("Request to update the offer")

implicit val schemaCreateOfferRequest: Schema[CreateOfferRequest] =
Schema.derived.description("Запрос на создание объявления")
Schema.derived.description("Request to create an offer")

// responses

implicit val schemaOkResponse: Schema[OkResponse] =
Schema.derived.description("Успех")
Schema.derived.description("Success")

implicit val schemaSessionResponse: Schema[SessionResponse] =
Schema.derived.description("Сессия пользователя")
Schema.derived.description("User session")

implicit val schemaUserResponse: Schema[UserResponse] =
Schema.derived.description("Пользователь")
Schema.derived.description("User")

implicit val schemaUserIdResponse: Schema[UserIdResponse] =
Schema.derived.description("ID пользователя")
Schema.derived.description("User ID")

implicit val schemaOfferResponse: Schema[OfferResponse] =
Schema.derived.description("Объявление")
Schema.derived.description("Offer")

implicit val schemaOffersResponse: Schema[OffersResponse] =
Schema.derived.description("Список объявлений")
Schema.derived.description("List of offers")
}
Loading