-
Notifications
You must be signed in to change notification settings - Fork 0
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
release/38851105: Validation server side and docker-compose #2
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# [Задача №00000000](https://bigbosop215.kaiten.ru/space/439413/card/00000000) | ||
|
||
### Что было не так: | ||
<описание проблемы/причины заведения новой фичи> | ||
### Что было сделано: | ||
* слой repository; | ||
* добавлена валидация на уникальность email; | ||
* ручка на получение подборок фильмов; | ||
* написаны тесты и моки. | ||
### Чек лист до ревью : | ||
- [ ] PR разбит на логические коммиты (если нет, то нужно разбить); | ||
- [ ] PR не содержит секретов (пароли, токены, ключи); | ||
- [ ] PR не содержит лишних файлов (например, `.env`); | ||
- [ ] PR не содержит коммитов, которые не относятся к задаче; | ||
|
||
**Перед тем, как отдать на ревью нужно убедиться, что все пункты выполнены** | ||
|
||
### как вливать ветку | ||
Если это обычная задача, то просто `squash merge` в `develop` | ||
Если это хотфикс, то обычный `merge` в `master` и `develop` (должно быть два `pull request`). При этом в ветке должен быть один коммит | ||
Если это релиз, то обычный `merge` и в `master` и в `develop`. Там будет несколько коммитов |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
/validation-service/bin/ | ||
/validation-service/.env |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
version: '3.9' | ||
|
||
volumes: | ||
postgres_volume: | ||
redis: | ||
|
||
services: | ||
validation_ms: | ||
build: | ||
context: ./validation-service/ | ||
dockerfile: Dockerfile | ||
restart: always | ||
ports: | ||
- 5050:5050 | ||
networks: | ||
- netflix | ||
redis: | ||
image: 'redis:latest' | ||
command: redis-server | ||
volumes: | ||
- redis:/data | ||
networks: | ||
- netflix | ||
db: | ||
image: postgres:latest | ||
environment: | ||
- "POSTGRES_DB=netflix" | ||
- "POSTGRES_USER=test" | ||
- "POSTGRES_PASSWORD=test" | ||
volumes: | ||
- "postgres_volume:/var/lib/postgresql/data" | ||
networks: | ||
- netflix | ||
|
||
|
||
networks: | ||
netflix: | ||
default: | ||
external: | ||
name: netflix # Не забыть создать сеть перед деплоем |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,17 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
|
||
"github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/app" | ||
) | ||
|
||
func main() { | ||
|
||
a, ctx, err := app.New() | ||
|
||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
a.Run(ctx) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package config | ||
|
||
type Config struct { | ||
Listener Listener `yaml:"listener"` | ||
} | ||
|
||
type Listener struct { | ||
Address string `yaml:"address"` | ||
Port int `yaml:"port"` | ||
} | ||
|
||
type ConfigContextKey struct { | ||
Address string | ||
Port int | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package config | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/joho/godotenv" | ||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
func GetConfigFromContext(ctx context.Context) *Config { | ||
value, ok := ctx.Value(ConfigContextKey{}).(*Config) | ||
if !ok { | ||
return nil | ||
} | ||
|
||
return value | ||
} | ||
|
||
func CreateConfigContext() (context.Context, error) { | ||
ctx := context.Background() | ||
config, err := readConf() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return context.WithValue(ctx, ConfigContextKey{}, config), nil | ||
} | ||
|
||
func readConf() (*Config, error) { | ||
err := godotenv.Load() | ||
|
||
if err != nil { | ||
log.Fatalf("Error loading .env file", err) | ||
} | ||
|
||
cfg := &Config{} | ||
|
||
filename, _ := filepath.Abs(os.Getenv("CFG_PATH")) | ||
data, err := os.ReadFile(filename) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = yaml.Unmarshal(data, cfg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return cfg, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,18 @@ | ||
module github.com/go-park-mail-ru/2024_2_GOATS | ||
module github.com/go-park-mail-ru/2024_2_GOATS/validation-service | ||
|
||
go 1.22.2 | ||
|
||
require ( | ||
google.golang.org/grpc v1.65.0 | ||
google.golang.org/protobuf v1.34.2 | ||
) | ||
|
||
require ( | ||
github.com/joho/godotenv v1.5.1 // indirect | ||
golang.org/x/net v0.26.0 // indirect | ||
golang.org/x/sys v0.21.0 // indirect | ||
golang.org/x/text v0.16.0 // indirect | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect | ||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 // indirect | ||
google.golang.org/protobuf v1.34.2 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,21 @@ | ||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= | ||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= | ||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= | ||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= | ||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= | ||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= | ||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= | ||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= | ||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= | ||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= | ||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= | ||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= | ||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= | ||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,27 @@ | ||
package converter | ||
|
||
import ( | ||
"github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/app/model" | ||
desc "github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/pb/validation" | ||
"google.golang.org/protobuf/types/known/timestamppb" | ||
) | ||
|
||
func ToUserRegisterDataFromDesc(data *desc.ValidateRegistrationRequest) *model.UserRegisterData { | ||
birthday := data.GetBitrhdate().AsTime() | ||
ts := timestamppb.New(birthday) | ||
|
||
return &model.UserRegisterData{ | ||
Email: data.GetEmail(), | ||
Password: data.GetPassword(), | ||
PasswordConfirm: data.GetPasswordConfirm(), | ||
Sex: data.GetSex(), | ||
Birthday: int(ts.Seconds), | ||
} | ||
} | ||
|
||
func ToErrorsFromServ(data *model.ErrorResponse) *desc.ErrorMessage { | ||
return &desc.ErrorMessage{ | ||
Code: data.Code, | ||
Error: data.ErrorObj.Error(), | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,25 @@ | ||
package handler | ||
package validation | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/app/model" | ||
desc "github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/pb/validation" | ||
) | ||
|
||
type ValidationService interface { | ||
ValidateRegistration(ctx context.Context, userData *model.UserRegisterData) *model.ValidationResponse | ||
} | ||
|
||
type Implementation struct { | ||
desc.UnimplementedValidationServer | ||
ctx context.Context | ||
validationService ValidationService | ||
} | ||
|
||
func NewImplementation(ctx context.Context, validationService ValidationService) *Implementation { | ||
return &Implementation{ | ||
ctx: ctx, | ||
validationService: validationService, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package validation | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/app/api/converter" | ||
desc "github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/pb/validation" | ||
) | ||
|
||
func (i *Implementation) ValidateRegistration(ctx context.Context, req *desc.ValidateRegistrationRequest) (*desc.ValidationResponse, error) { | ||
validData := i.validationService.ValidateRegistration(i.ctx, converter.ToUserRegisterDataFromDesc(req)) | ||
descErrors := make([]*desc.ErrorMessage, 0) | ||
for _, errData := range validData.Errors { | ||
descErrors = append(descErrors, converter.ToErrorsFromServ(&errData)) | ||
} | ||
|
||
return &desc.ValidationResponse{ | ||
Success: validData.Success, | ||
Errors: descErrors, | ||
}, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,49 @@ | ||
package main | ||
package app | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"net" | ||
|
||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/reflection" | ||
|
||
"github.com/go-park-mail-ru/2024_2_GOATS/validation-service/config" | ||
validationAPI "github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/app/api/validation" | ||
validationService "github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/app/service/validation" | ||
desc "github.com/go-park-mail-ru/2024_2_GOATS/validation-service/internal/pb/validation" | ||
) | ||
|
||
type App struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. хочу видеть сущность конфиг, где будет listener, он из ямлика будет читать адрес и порт. Рут контекст будет обогощаться этим конфигом, потом вы из него сможете достать конфиг где угодно There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Что-то типо? type Config struct {
listenerAddress string `yaml:"listener_address"`
listenerPort int `yaml:"listener_port"`
}
type App struct {
serverPort int
validationService validationAPI.ValidationService
config Config
} В конструкторе App читаю конфиг из ямла и добавляю в контекст |
||
validationService validationAPI.ValidationService | ||
} | ||
|
||
func New() (*App, context.Context, error) { | ||
ctx, err := config.CreateConfigContext() | ||
if err != nil { | ||
log.Fatal("Failed to read config: %v", err) | ||
} | ||
|
||
return &App{ | ||
validationService: validationService.NewService(ctx), | ||
}, ctx, nil | ||
} | ||
|
||
func (a *App) Run(ctx context.Context) { | ||
cfg := config.GetConfigFromContext(ctx) | ||
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Listener.Port)) | ||
if err != nil { | ||
log.Fatal("Failed to listen: %v", err) | ||
} | ||
|
||
s := grpc.NewServer() | ||
reflection.Register(s) | ||
desc.RegisterValidationServer(s, validationAPI.NewImplementation(ctx, a.validationService)) | ||
|
||
log.Printf("Server listening at %v:%d", cfg.Listener.Address, cfg.Listener.Port) | ||
|
||
if err = s.Serve(lis); err != nil { | ||
log.Fatalf("failed to serve: %v", err) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package errors | ||
|
||
import "errors" | ||
|
||
var ( | ||
ErrInvalidEmailCode = "invalid_email" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. А почему не сделать обычные ошибки через errors.New |
||
ErrInvalidEmailText = errors.New("email is incorrect") | ||
ErrInvalidPasswordCode = "invalid_password" | ||
ErrInvalidPasswordText = errors.New("password is too short. The minimal len is 8") | ||
ErrInvalidSexCode = "invalid_sex" | ||
ErrInvalidSexText = errors.New("only male or female allowed") | ||
ErrInvalidBirthdateCode = "invalid_birthdate" | ||
ErrInvalidBirthdateText = errors.New("bithdate should be before current time") | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,19 @@ | ||
package model | ||
|
||
type UserRegisterData struct { | ||
Email string | ||
Password string | ||
PasswordConfirm string | ||
Sex int32 | ||
Birthday int | ||
Starlexxx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
type ErrorResponse struct { | ||
Code string | ||
ErrorObj error | ||
} | ||
|
||
type ValidationResponse struct { | ||
Success bool | ||
Errors []ErrorResponse | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Вот пока не знаю, как избавиться от контекста в аргументах функции. Типо это же из pb файла такой интерфейс
И вообще надо ли избавляться?