Initial commit

This commit is contained in:
NortPerm
2023-10-30 15:21:12 +03:00
committed by GitHub
commit 13e2575b4e
149 changed files with 3961 additions and 0 deletions

2
hw12_13_14_15_calendar/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
logs/
bin/

View File

View File

@@ -0,0 +1,34 @@
BIN := "./bin/calendar"
DOCKER_IMG="calendar:develop"
GIT_HASH := $(shell git log --format="%h" -n 1)
LDFLAGS := -X main.release="develop" -X main.buildDate=$(shell date -u +%Y-%m-%dT%H:%M:%S) -X main.gitHash=$(GIT_HASH)
build:
go build -v -o $(BIN) -ldflags "$(LDFLAGS)" ./cmd/calendar
run: build
$(BIN) -config ./configs/config.toml
build-img:
docker build \
--build-arg=LDFLAGS="$(LDFLAGS)" \
-t $(DOCKER_IMG) \
-f build/Dockerfile .
run-img: build-img
docker run $(DOCKER_IMG)
version: build
$(BIN) version
test:
go test -race ./internal/... ./pkg/...
install-lint-deps:
(which golangci-lint > /dev/null) || curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.50.1
lint: install-lint-deps
golangci-lint run ./...
.PHONY: build run build-img run-img version test lint

View File

@@ -0,0 +1,13 @@
#### Результатом выполнения следующих домашних заданий является сервис «Календарь»:
- [Домашнее задание №12 «Заготовка сервиса Календарь»](./docs/12_README.md)
- [Домашнее задание №13 «Внешние API от Календаря»](./docs/13_README.md)
- [Домашнее задание №14 «Кроликизация Календаря»](./docs/14_README.md)
- [Домашнее задание №15 «Докеризация и интеграционное тестирование Календаря»](./docs/15_README.md)
#### Ветки при выполнении
- `hw12_calendar` (от `master`) -> Merge Request в `master`
- `hw13_calendar` (от `hw12_calendar`) -> Merge Request в `hw12_calendar` (если уже вмержена, то в `master`)
- `hw14_calendar` (от `hw13_calendar`) -> Merge Request в `hw13_calendar` (если уже вмержена, то в `master`)
- `hw15_calendar` (от `hw14_calendar`) -> Merge Request в `hw14_calendar` (если уже вмержена, то в `master`)
**Домашнее задание не принимается, если не принято ДЗ, предшедствующее ему.**

View File

@@ -0,0 +1,7 @@
syntax = "proto3";
package event;
message Event {
// TODO
}

View File

@@ -0,0 +1,36 @@
# Собираем в гошке
FROM golang:1.19 as build
ENV BIN_FILE /opt/calendar/calendar-app
ENV CODE_DIR /go/src/
WORKDIR ${CODE_DIR}
# Кэшируем слои с модулями
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . ${CODE_DIR}
# Собираем статический бинарник Go (без зависимостей на Си API),
# иначе он не будет работать в alpine образе.
ARG LDFLAGS
RUN CGO_ENABLED=0 go build \
-ldflags "$LDFLAGS" \
-o ${BIN_FILE} cmd/calendar/*
# На выходе тонкий образ
FROM alpine:3.9
LABEL ORGANIZATION="OTUS Online Education"
LABEL SERVICE="calendar"
LABEL MAINTAINERS="student@otus.ru"
ENV BIN_FILE "/opt/calendar/calendar-app"
COPY --from=build ${BIN_FILE} ${BIN_FILE}
ENV CONFIG_FILE /etc/calendar/config.toml
COPY ./configs/config.toml ${CONFIG_FILE}
CMD ${BIN_FILE} -config ${CONFIG_FILE}

View File

@@ -0,0 +1,20 @@
package main
// При желании конфигурацию можно вынести в internal/config.
// Организация конфига в main принуждает нас сужать API компонентов, использовать
// при их конструировании только необходимые параметры, а также уменьшает вероятность циклической зависимости.
type Config struct {
Logger LoggerConf
// TODO
}
type LoggerConf struct {
Level string
// TODO
}
func NewConfig() Config {
return Config{}
}
// TODO

View File

@@ -0,0 +1,61 @@
package main
import (
"context"
"flag"
"os"
"os/signal"
"syscall"
"time"
"github.com/fixme_my_friend/hw12_13_14_15_calendar/internal/app"
"github.com/fixme_my_friend/hw12_13_14_15_calendar/internal/logger"
internalhttp "github.com/fixme_my_friend/hw12_13_14_15_calendar/internal/server/http"
memorystorage "github.com/fixme_my_friend/hw12_13_14_15_calendar/internal/storage/memory"
)
var configFile string
func init() {
flag.StringVar(&configFile, "config", "/etc/calendar/config.toml", "Path to configuration file")
}
func main() {
flag.Parse()
if flag.Arg(0) == "version" {
printVersion()
return
}
config := NewConfig()
logg := logger.New(config.Logger.Level)
storage := memorystorage.New()
calendar := app.New(logg, storage)
server := internalhttp.NewServer(logg, calendar)
ctx, cancel := signal.NotifyContext(context.Background(),
syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
defer cancel()
go func() {
<-ctx.Done()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
if err := server.Stop(ctx); err != nil {
logg.Error("failed to stop http server: " + err.Error())
}
}()
logg.Info("calendar is running...")
if err := server.Start(ctx); err != nil {
logg.Error("failed to start http server: " + err.Error())
cancel()
os.Exit(1) //nolint:gocritic
}
}

View File

@@ -0,0 +1,27 @@
package main
import (
"encoding/json"
"fmt"
"os"
)
var (
release = "UNKNOWN"
buildDate = "UNKNOWN"
gitHash = "UNKNOWN"
)
func printVersion() {
if err := json.NewEncoder(os.Stdout).Encode(struct {
Release string
BuildDate string
GitHash string
}{
Release: release,
BuildDate: buildDate,
GitHash: gitHash,
}); err != nil {
fmt.Printf("error while decode version info: %v\n", err)
}
}

View File

@@ -0,0 +1,5 @@
[logger]
level = "INFO"
# TODO
# ...

View File

@@ -0,0 +1 @@
version: "3"

View File

@@ -0,0 +1,160 @@
## Домашнее задание №12 «Заготовка сервиса Календарь»
Необходимо реализовать скелет сервиса «Календарь», который будет дорабатываться в дальнейшем.
Описание того, к чему мы должны придти, представлено [в техническом задании](./CALENDAR.MD).
---
В репозитории представлена заготовка сервиса, позволяющая понять, что вообще происходит и получить вектор для дальнейшей доработки.
Этот код можно менять/удалять/добавлять каким-угодно способом по усмотрению разработчика.
---
На данный момент сервис будет состоять из следующих логически выделенных частей:
### 0) «Точка входа», запускающая сервис
Обычно располагается в `cmd/`. В `main()` происходит инициализация компонентов сервиса
(клиент к хранилищу, логгер, конфигурация и пр.), конструирование главного объекта-сервиса на их
основе и его запуск. Допускается использование https://github.com/spf13/cobra.
### 1) Конфигурирование сервиса
При необходимости создать отдельный пакет, отвечающий за работу с конфигом.
Сервис должен иметь конфиг, считываемый из файла. Для этого потребуется:
* обработка аргументов командной строки;
* чтение файла конфигурации формата json, yaml и пр. на выбор разработчика;
* заполнение программной структуры конфига из файла (Unmarshal и пр. способы);
* допустимо, если все действия выше за вас делает сторонний модуль.
Конфигурация понадобится для инициализации различных компонентов системы,
её возможные поля будут рассмотрены далее.
Соответственно сервис будет запускаться командой вида
```bash
./calendar --config=/path/to/config.yaml
```
где `--config` - путь к файлу конфигурации.
В репозитории должен присутствовать образец конфига.
### 2) Логирование в сервисе
При необходимости создать отдельный пакет, отвечающий за работу с логером.
Параметры, которыми необходимо инициализировать логгер:
* log_level - уровень логирования (error / warn / info / debug);
* любые другие на усмотрение разработчика.
Логгер может быть как глобальной переменной, так и компонентом сервиса.
### 3) Работа с хранилищем
Создать отдельный пакет, отвечающий за работу с хранилищем.
Создать интерфейс хранилища событий, состоящий из методов для работы с ним:
* добавление события в хранилище;
* изменение события в хранилище;
* удаление события из хранилища;
* листинг событий;
* пр. на усмотрение разработчика.
Описание сущности и методов представлено в [ТЗ](./CALENDAR.MD).
Создать объекты ошибок, соответствующие бизнес ошибкам, которые необходимо выделить.
Например, `ErrDateBusy` - данное время уже занято другим событием.
Создать две реализации интерфейса выше:
* **in-memory**: храним события в памяти (т.е. просто складываем объекты в словари/слайсы, не забывая про критические секции);
* **sql**: храним события в полноценной СУБД путем использования SQL-запросов в соответствующих методах.
Вынести в конфиг параметр, отвечающий за то, какую из реализаций использовать при старте.
Для работоспособности второй реализации необходимо:
* установить СУБД (например PostgreSQL) локально (или сразу через Docker, если знаете как);
* создать базу данных и пользователя для проекта календарь;
* реализовать схему данных (таблицы, индексы) в виде отдельного SQL или go-файла (файл миграции)
и сохранить его в репозиторий;
* применять миграции руками или на старте сервиса;
* вынести настройки подключения к БД в конфиг проекта.
Полезные библиотеки:
* https://github.com/jmoiron/sqlx
* https://github.com/pressly/goose#go-migrations
**Использовать ORM (например, gorm) не допускается**.
Календарь должен использовать хранилище через интерфейс.
### 4) Запуск простого HTTP-сервера
Запуск календаря должен стартовать HTTP-сервер. `host` и `port` сервера вынести в конфиг.
На данном этапе сервер не должен быть связан с бизнес логикой приложения и должен иметь
только один "hello-world" endpoint ("/", "/hello", etc.).
Информация об обработанном запросе должна выводиться в log-файл:
* IP клиента;
* дата и время запроса;
* метод, path и версия HTTP;
* код ответа;
* latency (время обработки запроса, посчитанное, например, с помощью middleware);
* user agent, если есть.
Пример лога:
```text
66.249.65.3 [25/Feb/2020:19:11:24 +0600] GET /hello?q=1 HTTP/1.1 200 30 "Mozilla/5.0"
```
### 5) Юнит-тесты
Минимальный обязательный набор - тесты на **in-memory** реализацию хранилища (на основную логику, бизнес-ошибки и конкуррентно-безопасность).
Остальные тесты на усмотрение разработчика.
### 6) Makefile
Проект должен иметь в корне файлы go.mod и Makefile, последний должен описывать команды:
* `make build` - скомпилировать бинарный файл сервиса;
* [`make run`] - опционально, собрать и запустить сервис с конфигом по умолчанию;
* `make test` - запустить юнит-тесты (с флагом -race);
* `make lint` - запустить golangci-lint (при необходимости добавить свой `.golangci.yml`);
* [`make migrate`] - опционально, если миграции применяются руками;
* пр. на усмотрение разработчика
### Об архитектуре
Проект должен следовать:
* https://github.com/golang-standards/project-layout
* https://golang.org/doc/effective_go.html#package-names
* https://rakyll.org/style-packages/
* https://en.wikipedia.org/wiki/Dependency_injection
Важно понять, что в Go нет серебряной пули по архитектуре.
Ссылки ниже могут дать полезные концепции, но не стоит слепо следовать им:
* https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1
* https://www.ardanlabs.com/blog/2017/02/package-oriented-design.html
* https://github.com/marcusolsson/goddd
* чистая архитектура (clean architecture):
- https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
- https://medium.com/@zhashkevych/чистая-архитектура-на-golang-cccbfdc95eba
Используйте стандартный layout, соблюдайте направление зависимостей, выделяйте пакеты по концепции,
не забывайте про внедрение зависимостей через интерфейсы и у вас всё получится!
Не забываем про стайл гайд, например:
* https://github.com/uber-go/guide/blob/master/style.md
* https://github.com/cristaloleg/go-advice
### В данном ДЗ не нужно
* Реализовывать HTTP, GRPC и пр. API к микросервису.
* Писать .proto-файлы.
Это всё будет позже.
### Критерии оценки
- Makefile заполнен и пайплайн зеленый - 1 балл
- Понятность и чистота кода (включая факт, что проект разбит
на пакеты по определенной логике) - до 2 баллов
- Реализовано конфигурирование сервиса - 1 балл
- Используется логгер и он настраивается из конфига - 1 балл
- Реализовано хранилище:
- in-memory - 1 балл
- sql + миграции - 2 балла
- Запускается простой HTTP-сервер, мидлвара удовлетворяет ТЗ - 1 балл
- Присутствуют юнит-тесты - 1 балл
#### Зачёт от 7 баллов

View File

@@ -0,0 +1,36 @@
## Домашнее задание №13 «API к Календарю»
Необходимо реализовать HTTP и GRPC API для сервиса календаря.
Методы API в принципе идентичны методам хранилища и [описаны в ТЗ](./CALENDAR.MD).
Для GRPC API необходимо:
* создать отдельную директорию для Protobuf спецификаций;
* создать Protobuf файлы с описанием всех методов API, объектов запросов и ответов (
т.к. объект Event будет использоваться во многих ответах разумно выделить его в отдельный message);
* создать отдельный пакет для кода GRPC сервера;
* добавить в Makefile команду `generate`; `make generate` - вызывает `go generate`, которая в свою очередь
генерирует код GRPC сервера на основе Protobuf спецификаций;
* написать код, связывающий GRPC сервер с методами доменной области (бизнес логикой);
* логировать каждый запрос по аналогии с HTTP API.
Для HTTP API необходимо:
* расширить "hello-world" сервер из [ДЗ №12](./12_README.md) до полноценного API;
* создать отдельный пакет для кода HTTP сервера;
* реализовать хэндлеры, при необходимости выделив структуры запросов и ответов;
* сохранить логирование запросов, реализованное в [ДЗ №12](./12_README.md).
Общие требования:
* должны быть реализованы все методы;
* календарь не должен зависеть от кода серверов;
* сервера должны запускаться на портах, указанных в конфиге сервиса.
**Можно использовать https://grpc-ecosystem.github.io/grpc-gateway/.**
### Критерии оценки
- Makefile заполнен и пайплайн зеленый - 1 балл
- Реализовано GRPC API и `make generate` - 3 балла
- Реализовано HTTP API - 2 балла
- Написаны юнит-тесты на API - до 2 баллов
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@@ -0,0 +1,48 @@
## Домашнее задание №14 «Кроликизация Календаря»
Необходимо реализовать "напоминания" о событиях с помощью RabbitMQ (кролика).
Общая концепция описана в [техническом задании](./CALENDAR.MD).
Порядок выполнения ДЗ:
* установить локально очередь сообщений RabbitMQ (или сразу через Docker, если знаете как);
* создать процесс Планировщик (`scheduler`), который периодически сканирует основную базу данных,
выбирая события о которых нужно напомнить:
- при запуске процесс должен подключаться к RabbitMQ и создавать все необходимые структуры
(топики и пр.) в ней;
- процесс должен выбирать сообытия для которых следует отправить уведомление (у события есть соотв. поле),
создавать для каждого Уведомление (описание сущности см. в [ТЗ](./CALENDAR.MD)),
сериализовать его (например, в JSON) и складывать в очередь;
- процесс должен очищать старые (произошедшие более 1 года назад) события.
* создать процесс Рассыльщик (`sender`), который читает сообщения из очереди и шлёт уведомления;
непосредственно отправку делать не нужно - достаточно логировать сообщения / выводить в STDOUT.
* настройки подключения к очереди, периодичность запуска и пр. настройки процессов вынести в конфиг проекта;
* работу с кроликом вынести в отдельный пакет, который будут использовать пакеты, реализующие процессы выше.
Процессы не должны зависеть от конкретной реализации RMQ-клиента.
В результате компиляции проекта (`make build`) должно получаться 3 отдельных исполняемых файла
(по одному на микросервис):
- API (`calendar`);
- Планировщик (`calendar_scheduler`);
- Рассыльщик (`calendar_sender`).
Каждый из сервисов должен принимать путь файлу конфигурации:
```bash
./calendar --config=/path/to/calendar_config.yaml
./calendar_scheduler --config=/path/to/scheduler_config.yaml
./calendar_sender --config=/path/to/sender_config.yaml
```
После запуска RabbitMQ и PostgreSQL процессы `calendar_scheduler` и `calendar_sender`
должны запускаться без дополнительных действий.
### Критерии оценки
- Makefile заполнен и пайплайн зеленый - 1 балл
- Работа с RMQ выделена в отдельный пакет, код не дублируется - 1 балл
- Реализован Планировщик:
- отсылает уведомления о выбранных событиях - 2 балла
- удаляет старые события - 1 балл
- Реализован Рассыльщик - 2 балла
- Можно собрать сервисы одной командой (`make build`) - 1 балл
- Понятность и чистота кода - до 2 баллов
#### Зачёт от 7 баллов

View File

@@ -0,0 +1,53 @@
## Домашнее задание №15 «Докеризация и интеграционное тестирование Календаря»
Данное задание состоит из двух частей.
Не забываем про https://github.com/golang-standards/project-layout.
### 1) Докеризация сервиса
Необходимо:
* создать Dockerfile для каждого из процессов (Календарь, Рассыльщик, Планировщик);
* собрать образы и проверить их локальный запуск;
* создать docker-compose файл, который запускает PostgreSQL, RabbitMQ и все микросервисы вместе
(для "неродных" сервисов использовать официальные образы из Docker Hub);
* при желании доработать конфигурацию так, чтобы она поддерживала переменные окружения
(если вы используете библиотеку, то скорее всего она уже это умеет); в противном случае
придется "подкладывать" конфиг сервису с помощью Dockerfile / docker-compose -
при этом можно "заполнять" конфигурационный файл из переменных окружения, например
```bash
$ envsubst < config_template.json > config.json
```
* если миграции выполняются руками, а не на старте сервиса, то также в docker-compose
должен запускаться one-shot скрипт, который делает это (применяет SQL миграции,
создавая структуру БД).
* порты серверов, предоставляющих API, пробросить на host.
У преподавателя должна быть возможность запустить весь проект с помощью команды
`make up` (внутри `docker-compose up`) и погасить с помощью `make down`.
HTTP API, например, после запуска должно быть доступно по URL **http://localhost:8888/**.
### 2) Интеграционное тестирование
Необходимо:
* создать отдельный пакет для интеграционных тестов.
* реализовать интеграционные тесты на языке Go; при желании можно использовать
[godog](https://github.com/cucumber/godog) / [ginkgo](https://github.com/onsi/ginkgo), но
обязательным требованием это **не является**.
* создать docker-compose файл, поднимающий все сервисы проекта + контейнер с интеграционными тестами;
* расширить Makefile командой `integration-tests`, `make integration-tests` будет запускать интеграционные тесты;
**не стоит смешивать это с `make test`, иначе CI-пайплайн не пройдёт.**
* прикрепить в Merge Request вывод команды `make integration-tests`.
Преподаватель может запустить интеграционные тесты с помощью команды `make integration-tests`:
- команда должна поднять окружение (`docker-compose`), прогнать тесты и подчистить окружение за собой;
- в случае успешного выполнения команда должна возвращать 0, иначе 1.
### Критерии оценки
- Проект полностью запускается и останавливается с помощью `make up` / `make down` - 3 балла
- Интеграционные тесты запускаются с помощью `make integration-tests`. Команда возвращает верный код ответа - 1 балл
- Интеграционные тесты покрывают бизнес сценарии:
- добавление события и обработка бизнес ошибок - 2 балла
- получение листинга событий на день/неделю/месяц - 2 балла
- отправка уведомлений (необходимо доработать sender так, чтобы он информировал куда-то о статусе уведомления (БД/кролик)) - 2 балла
#### Зачёт от 7 баллов

View File

@@ -0,0 +1,96 @@
## Техническое задание на сервис «Календарь»
### Общее описание
Сервис "Календарь" представляет собой максимально упрощенный сервис для хранения календарных событий и отправки уведомлений.
Сервис предполагает возможность:
* добавить/обновить событие;
* получить список событий на день/неделю/месяц;
* получить уведомление за N дней до события.
Сервис НЕ предполагает:
* авторизации;
* разграничения доступа;
* web-интерфейса.
### Архитектура
Полностью завершенный сервис состоит из 5 процессов.
#### 1) API
API предоставляет собой GRPC и HTTP интерфейсы для пользователей. API реализуют основные методы сервиса.
Так как авторизация и разделение доступа выходят за рамки сервиса, мы предполагаем, что ID пользователя
просто передается через параметры / метаданные / заголовок запроса.
#### 2) Планировщик
Планировщик - это фоновый процесс, который не взаимодействует с пользователем и выполняет периодические задания:
* выбор событий, требующих уведомления и отправка уведомлений в очередь рассыльщику;
* очистка старых (более 1 года назад) событий.
#### 3) Рассыльщик
Рассыльщик - это фоновый процесс, занимающийся отправкой уведомлений.
В рамках задания нет необходимости реальной отправки сообщений, достаточно, если рассыльщик просто будет
записывать их в лог / выводить в STDOUT.
#### 4) СУБД
Реляционная СУБД (например, PostgreSQL) - хранит информацию о событиях.
#### 5) Очередь сообщений
Очередь сообщений (RabbitMQ) - используется для передачи уведомлений от Планировщика Рассыльщику.
### Описание сущностей
#### Событие
Событие - основная сущность, содержит в себе поля:
* ID - уникальный идентификатор события (можно воспользоваться UUID);
* Заголовок - короткий текст;
* Дата и время события;
* Длительность события (или дата и время окончания);
* Описание события - длинный текст, опционально;
* ID пользователя, владельца события;
* За сколько времени высылать уведомление, опционально.
#### Уведомление
Уведомление - временная сущность, в БД не хранится, складывается в очередь для рассыльщика, содержит поля:
* ID события;
* Заголовок события;
* Дата события;
* Пользователь, которому отправлять.
### Описание методов
* Создать (событие);
* Обновить (ID события, событие);
* Удалить (ID события);
* СписокСобытийНаДень (дата);
* СписокСобытийНаНеделю (дата начала недели);
* СписокСобытийНaМесяц (дата начала месяца).
### Конфигурация, логирование, контейнеризация
Рекомендуется собирать проект в виде трех бинарных файлов, по одному на каждый микросервис.
Каждый из сервисов должен принимать путь файлу конфигурации:
```text
./calendar --config=/path/to/calendar_config.yaml
./calendar_scheduler --config=/path/to/scheduler_config.yaml
./calendar_sender --config=/path/to/sender_config.yaml
```
Проект следует оформить в виде набора контейнеров для каждого из микросервисов.
Поскольку запуск будет осуществлять в контейнерах - логи нужно печатать в STDOUT.
Сервисы должны получать настройки (например, адрес и логин в СУБД) через переменные окружения.
При желании можно использовать конфиг viper, с поддержкой переменных окружения.
Развертывание микросервиса должно осуществляться командой `docker-compose up` в директории с проектом.
### Тестирование
Для проекта необходимо реализовать интеграционные тесты, т.е. тесты проверяющие работу на уровне API.
Интеграционные тесты можно создать с помощью BDD, а можно просто с помощью стандартного пакета `testing`
и запускать их в отдельном контейнере в том же compose окружении, что и остальные микросервисы.
В таком случае тесты смогут получать адрес работающего сервиса через переменные окружения, выполнять запросы к нему,
а также проверять состояние базы данных и пр.
### Этапы разработки
Разработка разделена на следующие этапы (ДЗ):
* Разрабатываем скелет сервиса: конфигурирование, работа с базой, простой web-сервер.
* Добавляем HTTP и GRPC API.
* Выделяем Планировщик и Рассыльщик. Интегрируемся с очередью сообщений.
* Создаем Docker-образы, docker-compose конфигурации, пишем интеграционные тесты.
Таким образом, на каждом этапе мы получаем осмысленный проект.

View File

@@ -0,0 +1,3 @@
module github.com/fixme_my_friend/hw12_13_14_15_calendar
go 1.19

View File

View File

@@ -0,0 +1,26 @@
package app
import (
"context"
)
type App struct { // TODO
}
type Logger interface { // TODO
}
type Storage interface { // TODO
}
func New(logger Logger, storage Storage) *App {
return &App{}
}
func (a *App) CreateEvent(ctx context.Context, id, title string) error {
// TODO
return nil
// return a.storage.CreateEvent(storage.Event{ID: id, Title: title})
}
// TODO

View File

@@ -0,0 +1,20 @@
package logger
import "fmt"
type Logger struct { // TODO
}
func New(level string) *Logger {
return &Logger{}
}
func (l Logger) Info(msg string) {
fmt.Println(msg)
}
func (l Logger) Error(msg string) {
// TODO
}
// TODO

View File

@@ -0,0 +1,7 @@
package logger
import "testing"
func TestLogger(t *testing.T) {
// TODO
}

View File

@@ -0,0 +1,11 @@
package internalhttp
import (
"net/http"
)
func loggingMiddleware(next http.Handler) http.Handler { //nolint:unused
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// TODO
})
}

View File

@@ -0,0 +1,31 @@
package internalhttp
import (
"context"
)
type Server struct { // TODO
}
type Logger interface { // TODO
}
type Application interface { // TODO
}
func NewServer(logger Logger, app Application) *Server {
return &Server{}
}
func (s *Server) Start(ctx context.Context) error {
// TODO
<-ctx.Done()
return nil
}
func (s *Server) Stop(ctx context.Context) error {
// TODO
return nil
}
// TODO

View File

@@ -0,0 +1,7 @@
package storage
type Event struct {
ID string
Title string
// TODO
}

View File

@@ -0,0 +1,14 @@
package memorystorage
import "sync"
type Storage struct {
// TODO
mu sync.RWMutex //nolint:unused
}
func New() *Storage {
return &Storage{}
}
// TODO

View File

@@ -0,0 +1,7 @@
package memorystorage
import "testing"
func TestStorage(t *testing.T) {
// TODO
}

View File

@@ -0,0 +1,20 @@
package sqlstorage
import "context"
type Storage struct { // TODO
}
func New() *Storage {
return &Storage{}
}
func (s *Storage) Connect(ctx context.Context) error {
// TODO
return nil
}
func (s *Storage) Close(ctx context.Context) error {
// TODO
return nil
}