pull/15/merge
Andrey Ivanov 2021-05-23 13:28:52 +00:00 committed by GitHub
commit 331ec5dba5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 3168 additions and 1057 deletions

View File

@ -16,4 +16,4 @@ linters:
- gofumpt
- gosec
- nlreturn
- gocritic
- exhaustive

12
.run/calendar.run.xml Normal file
View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="calendar" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="HW_OTUS" />
<working_directory value="$PROJECT_DIR$/hw12_13_14_15_calendar" />
<parameters value="-config=configs/calendar.conf" />
<kind value="PACKAGE" />
<package value="github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/cmd/calendar" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$/hw12_13_14_15_calendar/cmd/calendar/main.go" />
<method v="2" />
</configuration>
</component>

12
.run/integration.run.xml Normal file
View File

@ -0,0 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="integration" type="GoTestRunConfiguration" factoryName="Go Test">
<module name="HW_OTUS" />
<working_directory value="$PROJECT_DIR$/hw12_13_14_15_calendar/test" />
<kind value="PACKAGE" />
<package value="github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/test" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<framework value="gotest" />
<method v="2" />
</configuration>
</component>

13
.run/scheduler.run.xml Normal file
View File

@ -0,0 +1,13 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="scheduler" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="HW_OTUS" />
<working_directory value="$PROJECT_DIR$/hw12_13_14_15_calendar" />
<go_parameters value="-i" />
<parameters value="-config=configs/scheduler.conf" />
<kind value="PACKAGE" />
<package value="github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/cmd/scheduler" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>

13
.run/sender.run.xml Normal file
View File

@ -0,0 +1,13 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="sender" type="GoApplicationRunConfiguration" factoryName="Go Application">
<module name="HW_OTUS" />
<working_directory value="$PROJECT_DIR$/hw12_13_14_15_calendar" />
<go_parameters value="-i" />
<parameters value="-config=configs/sender.conf" />
<kind value="PACKAGE" />
<package value="github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/cmd/sender" />
<directory value="$PROJECT_DIR$" />
<filePath value="$PROJECT_DIR$" />
<method v="2" />
</configuration>
</component>

View File

@ -2,4 +2,4 @@ module github.com/tiburon-777/HW_OTUS/hw08_zenv_to_structure
go 1.14
require github.com/stretchr/testify v1.6.1
require github.com/stretchr/testify v1.6.1

View File

@ -0,0 +1,6 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,22 +1,64 @@
cdir = $(shell pwd)
build:
go build -o ./bin/calendar ./cmd/calendar/main.go
inplace-integration-test:
go test -count 10 -race ./cmd/calendar/... --config ./../../configs/calendar.conf
run:
go run ./cmd/calendar/main.go -config ./configs/config.toml
bdd:
docker build -t bdd --network host -f test/Dockerfile .
integration-test:
set -e ;\
docker-compose -f ./cicd/dc_sup.yml up -d --build ;\
docker-compose -f ./cicd/dc_calendar.yml up -d --build ;\
docker-compose -f ./cicd/dc_sender.yml up -d --build ;\
docker-compose -f ./cicd/dc_sheduler.yml up -d --build ;\
test_status_code=0 ;\
docker build -t bdd --network host -f test/Dockerfile . || test_status_code=$$? ;\
docker-compose -f ./cicd/dc_sup.yml down --rmi local --volumes --remove-orphans --timeout 60 ;\
docker-compose -f ./cicd/dc_calendar.yml down --rmi local --volumes --remove-orphans --timeout 60 ;\
docker-compose -f ./cicd/dc_sender.yml down --rmi local --volumes --remove-orphans --timeout 60 ;\
docker-compose -f ./cicd/dc_sheduler.yml down --rmi local --volumes --remove-orphans --timeout 60 ;\
exit $$test_status_code
test:
go test -race ./internal/...
go test -race ./internal/... ./pkg/...
lint: install-lint-deps
golangci-lint run .cmd/... ./internal/...
golangci-lint run .cmd/... ./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.30.0
generate:
protoc -I ./grpcserver --go_out=plugins=grpc:./internal/grpcserver --grpc-gateway_out=logtostderr=true:./internal/grpcserver ./grpcserver/grpcserver.proto
protoc -I ./api/public --go_out=plugins=grpc:./pkg/api/public ./api/public/public.proto
protoc -I ./api/private --go_out=plugins=grpc:./internal/api/private ./api/private/private.proto
up: sup-up calendar-up sender-up sheduler-up
down: calendar-down sender-down sheduler-down sup-down clean
calendar-up:
docker-compose -f ./cicd/dc_calendar.yml up -d --build
calendar-down:
docker-compose -f ./cicd/dc_calendar.yml down
sender-up:
docker-compose -f ./cicd/dc_sender.yml up -d --build
sender-down:
docker-compose -f ./cicd/dc_sender.yml down
scheduler-up:
docker-compose -f ./cicd/dc_sheduler.yml up -d --build
scheduler-down:
docker-compose -f ./cicd/dc_sheduler.yml down
sup-up:
docker-compose -f ./cicd/dc_sup.yml up -d --build
sup-down:
docker-compose -f ./cicd/dc_sup.yml down --rmi local -v
debug: sup-up calendar-up sender-up
kill: sender-down calendar-down sup-down
.PHONY: build run test lint

View File

@ -0,0 +1,37 @@
syntax = "proto3";
package private;
option go_package = "private";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
service grpc {
rpc GetNotifications(google.protobuf.Empty) returns (GetRsp) {}
rpc SetNotified(SetReq) returns (google.protobuf.Empty) {}
rpc PurgeOldEvents(PurgeReq) returns (PurgeResp) {}
}
message GetRsp {
repeated Event Events = 1;
}
message SetReq {
int64 ID = 1;
}
message PurgeReq {
int64 OlderThenDays = 1;
}
message PurgeResp {
int64 Qty = 1;
}
message Event {
int64 ID = 1;
string Title = 2;
google.protobuf.Timestamp Date = 3;
int64 UserID = 4;
}

View File

@ -1,8 +1,8 @@
syntax = "proto3";
package grpcserver;
package public;
option go_package = "grpcserver";
option go_package = "public";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
@ -58,6 +58,7 @@ message Event {
string Note = 5;
int64 UserID = 6;
google.protobuf.Duration NotifyTime = 7;
bool notified = 8;
}
message CreateReq {

View File

@ -0,0 +1,17 @@
FROM golang:1.14 as builder
RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN go get -d ./cmd/calendar/.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o calendar ./cmd/calendar/.
FROM alpine:latest
ENV APP_GRPC_PORT $APP_GRPC_PORT
ENV APP_HTTP_PORT $APP_HTTP_PORT
ENV APP_API_PORT $APP_API_PORT
WORKDIR /
COPY --from=builder /app/calendar ./sbin
EXPOSE ${APP_GRPC_PORT}
EXPOSE ${APP_HTTP_PORT}
EXPOSE ${APP_API_PORT}
ENTRYPOINT ["calendar"]

View File

@ -0,0 +1,25 @@
version: '3'
services:
calendar:
build:
context: ..
dockerfile: ./cicd/calendar/Dockerfile
volumes:
- ../logs:/logs
restart: on-failure
environment:
APP_GRPC_ADDRESS: 0.0.0.0
APP_GRPC_PORT: 50051
APP_HTTP_ADDRESS: 0.0.0.0
APP_HTTP_PORT: 50052
APP_API_ADDRESS: 0.0.0.0
APP_API_PORT: 50053
APP_LOGGER_FILE: /logs/calendar.log
APP_LOGGER_LEVEL: INFO
APP_STORAGE_INMEMORY: "false"
APP_STORAGE_SQLHOST: localhost
APP_STORAGE_SQLPORT: 5432
APP_STORAGE_SQLDBASE: calendar
APP_STORAGE_SQLUSER: calendar
APP_STORAGE_SQLPASS: 12345678
network_mode: host

View File

@ -0,0 +1,22 @@
version: '3'
services:
sender:
build:
context: ..
dockerfile: ./cicd/sender/Dockerfile
volumes:
- ../logs:/logs
restart: on-failure
environment:
APP_RABBITMQ_ADDRESS: localhost
APP_RABBITMQ_PORT: 5672
APP_RABBITMQ_LOGIN: rabbit
APP_RABBITMQ_PASS: rabbit
APP_RABBITMQ_EXCHANGE: calendar
APP_RABBITMQ_QUEUE: notifications
APP_RABBITMQ_KEY: events
APP_API_ADDRESS: localhost
APP_API_PORT: 50053
APP_LOGGER_FILE: /logs/calendar.log
APP_LOGGER_LEVEL: INFO
network_mode: host

View File

@ -0,0 +1,22 @@
version: '3'
services:
scheduler:
build:
context: ..
dockerfile: ./cicd/scheduler/Dockerfile
volumes:
- ../logs:/logs
restart: on-failure
environment:
APP_RABBITMQ_ADDRESS: localhost
APP_RABBITMQ_PORT: 5672
APP_RABBITMQ_LOGIN: rabbit
APP_RABBITMQ_PASS: rabbit
APP_RABBITMQ_EXCHANGE: calendar
APP_RABBITMQ_QUEUE: notifications
APP_RABBITMQ_KEY: events
APP_API_ADDRESS: localhost
APP_API_PORT: 50053
APP_LOGGER_FILE: /logs/calendar.log
APP_LOGGER_LEVEL: INFO
network_mode: host

View File

@ -0,0 +1,34 @@
version: '3'
services:
goose:
build:
context: ..
dockerfile: ./cicd/goose/Dockerfile
restart: on-failure
environment:
GOOSE_DRIVER: "postgres"
GOOSE_DBSTRING: "host=localhost port=5432 user=calendar password=12345678 dbname=calendar sslmode=disable"
depends_on:
- psql
network_mode: host
psql:
image: postgres:11-alpine
hostname: "psql"
container_name: psql
environment:
POSTGRES_USER: calendar
POSTGRES_PASSWORD: 12345678
POSTGRES_DB: calendar
network_mode: host
rabbitmq:
image: rabbitmq:3-management-alpine
hostname: "rabbitmq"
container_name: rabbitmq
environment:
RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG"
RABBITMQ_DEFAULT_USER: "rabbit"
RABBITMQ_DEFAULT_PASS: "rabbit"
RABBITMQ_DEFAULT_VHOST: "/"
network_mode: host

View File

@ -0,0 +1,5 @@
FROM golang:1.14
WORKDIR /
RUN go get -u github.com/pressly/goose/cmd/goose
COPY . .
ENTRYPOINT ["/go/bin/goose", "-dir", "migrations", "up"]

View File

@ -0,0 +1,11 @@
FROM golang:1.14 as builder
RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN go get -d ./cmd/scheduler/.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o scheduler ./cmd/scheduler/.
FROM alpine:latest
WORKDIR /
COPY --from=builder /app/scheduler ./sbin
ENTRYPOINT ["scheduler"]

View File

@ -0,0 +1,11 @@
FROM golang:1.14 as builder
RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN go get -d ./cmd/sender/.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o sender ./cmd/sender/.
FROM alpine:latest
WORKDIR /
COPY --from=builder /app/sender ./sbin
ENTRYPOINT ["sender"]

View File

@ -3,8 +3,8 @@ package main
import (
"context"
"flag"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/api/private"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/rest"
oslog "log"
"net"
"net/http"
@ -12,43 +12,37 @@ import (
"os/signal"
"syscall"
_ "github.com/go-sql-driver/mysql"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/app"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/grpcserver"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/logger"
store "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage"
"github.com/gorilla/mux"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/logger"
store "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage"
)
var configFile string
func init() {
flag.StringVar(&configFile, "config", "/etc/calendar/config.toml", "Path to configuration file")
flag.StringVar(&configFile, "config", "", "Path to configuration file")
flag.Parse()
}
func main() {
conf, err := config.NewConfig(configFile)
var conf config.Calendar
err := config.New(configFile, &conf)
if err != nil {
oslog.Fatal("не удалось открыть файл конфигурации:", err.Error())
oslog.Fatal("can't get config:", err.Error())
}
log, err := logger.New(conf)
log, err := logger.New(logger.Config(conf.Logger))
if err != nil {
oslog.Fatal("не удалось запустить логер:", err.Error())
oslog.Fatal("can't start logger:", err.Error())
}
storeConf := store.Config{
InMemory: conf.Storage.InMemory,
SQLHost: conf.Storage.SQLHost,
SQLPort: conf.Storage.SQLPort,
SQLDbase: conf.Storage.SQLDbase,
SQLUser: conf.Storage.SQLUser,
SQLPass: conf.Storage.SQLPass,
}
st := store.NewStore(storeConf)
calendar := app.New(log, st)
st := store.NewStore(store.Config(conf.Storage))
serverGRPC := grpcserver.New(calendar)
calendar := calendar.New(log, st)
serverGRPC := public.New(calendar)
go func() {
if err := serverGRPC.Start(conf); err != nil {
log.Errorf("failed to start grpc server: " + err.Error())
@ -56,33 +50,37 @@ func main() {
}
}()
grpcDiler, err := grpc.Dial(net.JoinHostPort(conf.HTTP.Address, conf.HTTP.Port), grpc.WithInsecure())
if err != nil {
log.Errorf("can't dial grpc server: " + err.Error())
os.Exit(1)
}
defer grpcDiler.Close()
grpcGwRouter := runtime.NewServeMux()
if err = grpcserver.RegisterGrpcHandler(context.Background(), grpcGwRouter, grpcDiler); err != nil {
log.Errorf("can't register handlers for grpc-gateway: " + err.Error())
os.Exit(1)
}
mux := http.NewServeMux()
mux.Handle("/", grpcGwRouter)
serverAPI := private.New(calendar)
go func() {
log.Infof("start webAPI server")
if err := http.ListenAndServe(net.JoinHostPort(conf.HTTP.Address, conf.HTTP.Port), mux); err != nil {
if err := serverAPI.Start(private.Config(conf.API)); err != nil {
log.Errorf("failed to start API server: " + err.Error())
os.Exit(1)
}
}()
_, cancel := context.WithCancel(context.Background())
m := mux.NewRouter()
m.HandleFunc("/events", rest.FromRESTCreate(calendar)).Methods("POST")
m.HandleFunc("/events/{ID}", rest.FromRESTUpdate(calendar)).Methods("PUT")
m.HandleFunc("/events/{ID}", rest.FromRESTDelete(calendar)).Methods("DELETE")
m.HandleFunc("/events", rest.FromRESTList(calendar)).Methods("GET")
m.HandleFunc("/events/{ID}", rest.FromRESTGetByID(calendar)).Methods("GET")
m.HandleFunc("/events/{Range}/{Date}", rest.FromRESTGetByDate(calendar)).Methods("GET")
go func() {
log.Infof("webAPI server starting")
if err := http.ListenAndServe(net.JoinHostPort(conf.HTTP.Address, conf.HTTP.Port), m); err != nil {
log.Errorf("failed to start webAPI server: " + err.Error())
os.Exit(1)
}
}()
signals := make(chan os.Signal, 1)
signal.Notify(signals,syscall.SIGINT)
signal.Notify(signals, syscall.SIGINT)
<-signals
signal.Stop(signals)
serverGRPC.Stop()
serverAPI.Stop()
cancel()
}

View File

@ -0,0 +1,161 @@
package main
import (
"context"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/duration"
"github.com/golang/protobuf/ptypes/empty"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/stretchr/testify/require"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"log"
"os"
"sync"
"testing"
"time"
)
var _ = func() bool {
testing.Init()
return true
}()
const testPortBase = 3000
var testEvent01 = public.CreateReq{
Title: "Test event 01",
Date: time2pbtimestamp(time.Now().Add(30 * time.Second)),
Latency: dur2pbduration(24 * time.Hour),
Note: "Note of test event 01",
NotifyTime: dur2pbduration(5 * time.Minute),
UserID: 1111,
}
var testEvent02 = public.CreateReq{
Title: "Test event 02",
Date: time2pbtimestamp(time.Now().Add(60 * time.Second)),
Latency: dur2pbduration(2 * 24 * time.Hour),
Note: "Note of test event 02",
NotifyTime: dur2pbduration(5 * time.Minute),
UserID: 2222,
}
func TestMain(m *testing.M) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
go func(ctx context.Context) {
main()
}(ctx)
time.Sleep(1 * time.Second)
c := m.Run()
cancel()
os.Exit(c)
}
func TestPublicGRPCEndpoint(t *testing.T) {
wg := sync.WaitGroup{}
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
publicAPI, err := public.NewClient(ctx, "localhost", "50051")
require.NoError(t, err)
wg.Add(5)
// Реализовать тесты логики приложения:
t.Run("test public GRPC.Create and GRPC.GetById", func(t *testing.T) {
defer wg.Done()
resp1, err := publicAPI.Create(ctx, &testEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
resp2, err := publicAPI.GetByID(ctx, &public.GetByIDReq{ID: resp1.ID})
require.NoError(t, err)
require.Equal(t, 1, len(resp2.Events))
require.Equal(t, testEvent01.Title, resp2.Events[0].Title)
require.Equal(t, testEvent01.UserID, resp2.Events[0].UserID)
require.Equal(t, testEvent01.Date.Seconds, resp2.Events[0].Date.Seconds)
require.Equal(t, testEvent01.Note, resp2.Events[0].Note)
})
t.Run("test public GRPC.Create, GRPC.Update and GRPC.GetById", func(t *testing.T) {
defer wg.Done()
resp1, err := publicAPI.Create(ctx, &testEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
_, err = publicAPI.Update(ctx, &public.UpdateReq{ID: resp1.ID, Event: &public.Event{ID: resp1.ID, Title: testEvent02.Title, Date: testEvent02.Date, Latency: testEvent02.Latency, Note: testEvent02.Note, UserID: testEvent02.UserID, NotifyTime: testEvent02.NotifyTime}})
require.NoError(t, err)
resp2, err := publicAPI.GetByID(ctx, &public.GetByIDReq{ID: resp1.ID})
require.NoError(t, err)
require.Equal(t, 1, len(resp2.Events))
require.Equal(t, testEvent02.Title, resp2.Events[0].Title)
require.Equal(t, testEvent02.UserID, resp2.Events[0].UserID)
require.Equal(t, testEvent02.Date.Seconds, resp2.Events[0].Date.Seconds)
require.Equal(t, testEvent02.Note, resp2.Events[0].Note)
})
t.Run("test public GRPC.Create, GRPC.Delete and GRPC.GetById", func(t *testing.T) {
defer wg.Done()
resp1, err := publicAPI.Create(ctx, &testEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
_, err = publicAPI.Delete(ctx, &public.DeleteReq{ID: resp1.ID})
require.NoError(t, err)
resp2, err := publicAPI.GetByID(ctx, &public.GetByIDReq{ID: resp1.ID})
require.Error(t, err)
require.Nil(t, resp2)
})
t.Run("test public GRPC.Create and GRPC.List", func(t *testing.T) {
defer wg.Done()
resp1, err := publicAPI.Create(ctx, &testEvent01)
require.NoError(t, err)
resp2, err := publicAPI.Create(ctx, &testEvent02)
require.NoError(t, err)
require.NotEqual(t, resp1.ID, resp2.ID)
list, err := publicAPI.List(ctx, &empty.Empty{})
require.NoError(t, err)
require.GreaterOrEqual(t, len(list.Events), 2)
var e1, e2 bool
for _, v := range list.Events {
if v.ID == resp1.ID {
e1 = true
}
if v.ID == resp2.ID {
e2 = true
}
}
require.True(t, e1)
require.True(t, e2)
})
t.Run("test public GRPC.Create and GRPC.GetByDate", func(t *testing.T) {
defer wg.Done()
resp1, err := publicAPI.Create(ctx, &testEvent01)
require.NoError(t, err)
list, err := publicAPI.GetByDate(ctx, &public.GetByDateReq{Date: testEvent01.Date, Range: public.QueryRange_DAY})
require.NoError(t, err)
require.GreaterOrEqual(t, len(list.Events), 2)
var e1 bool
for _, v := range list.Events {
if v.ID == resp1.ID {
e1 = true
}
}
require.True(t, e1)
})
wg.Wait()
}
func time2pbtimestamp(t time.Time) *timestamp.Timestamp {
r, err := ptypes.TimestampProto(t)
if err != nil {
log.Fatalf("cant convert Time to Timestamp: %s", err.Error())
}
return r
}
func dur2pbduration(t time.Duration) *duration.Duration {
return ptypes.DurationProto(t)
}

View File

@ -0,0 +1,42 @@
package main
import (
"flag"
"log"
"os"
"os/signal"
"syscall"
_ "github.com/go-sql-driver/mysql"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/sheduler"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
)
var configFile string
func init() {
flag.StringVar(&configFile, "config", "", "Path to configuration file")
flag.Parse()
}
func main() {
var conf sheduler.Config
err := config.New(configFile, &conf)
if err != nil {
log.Fatal("can't get config:", err.Error())
}
app := sheduler.New(conf)
if err = app.Start(); err != nil {
app.Logger.Errorf("failed to start scheduler: ", err.Error())
os.Exit(1)
}
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT)
<-signals
signal.Stop(signals)
app.Stop()
log.Println("scheduler shutdown gracefully")
}

View File

@ -0,0 +1,40 @@
package main
import (
"flag"
_ "github.com/go-sql-driver/mysql"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/sender"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"log"
"os"
"os/signal"
"syscall"
)
var configFile string
func init() {
flag.StringVar(&configFile, "config", "", "Path to configuration file")
flag.Parse()
}
func main() {
var conf sender.Config
err := config.New(configFile, &conf)
if err != nil {
log.Fatal("can't get config:", err.Error())
}
app := sender.New(conf)
if err = app.Start(); err != nil {
app.Logger.Errorf("failed to start sender: ", err.Error())
os.Exit(1)
}
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT)
<-signals
signal.Stop(signals)
app.Stop()
log.Println("sender shutdown gracefully")
}

View File

@ -6,8 +6,12 @@ Port = "50051"
Address = "localhost"
Port = "50052"
[API]
Address = "localhost"
Port = "50053"
[Logger]
File = "./calendar.log"
File = "./logs/calendar.log"
Level = "INFO"
[Storage]

View File

@ -0,0 +1,16 @@
[Rabbitmq]
Address = "localhost"
Port = "5672"
Login = "rabbit"
Pass = "rabbit"
Exchange = "calendar"
Queue = "notifications"
Key = "events"
[Logger]
File = "./logs/calendar.log"
Level = "INFO"
[CalendarAPI]
Address = "localhost"
Port = "50053"

View File

@ -0,0 +1,16 @@
[Rabbitmq]
Address = "localhost"
Port = "5672"
Login = "rabbit"
Pass = "rabbit"
Exchange = "calendar"
Queue = "notifications"
Key = "events"
[Logger]
File = "./logs/calendar.log"
Level = "INFO"
[CalendarAPI]
Address = "localhost"
Port = "50053"

View File

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

View File

@ -5,18 +5,20 @@ go 1.14
require (
github.com/BurntSushi/toml v0.3.1
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec
github.com/daixiang0/gci v0.2.4 // indirect
github.com/dmitryt/otus-golang-hw/hw12_13_14_15_calendar v0.0.0-20200916093948-5ca8860569b6
github.com/daixiang0/gci v0.2.8 // indirect
github.com/go-sql-driver/mysql v1.5.0
github.com/golang/protobuf v1.4.2
github.com/grpc-ecosystem/grpc-gateway v1.15.0
github.com/rs/zerolog v1.20.0
github.com/gorilla/mux v1.8.0
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.10.0
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sirupsen/logrus v1.4.2 // indirect
github.com/streadway/amqp v1.0.0
github.com/stretchr/testify v1.6.1
go.uber.org/zap v1.15.0 // indirect
golang.org/x/net v0.0.0-20200625001655-4c5254603344
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305 // indirect
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70
google.golang.org/grpc v1.32.0
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
)

View File

@ -1,49 +1,29 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec h1:tDOPo9NAXCjvoK35HgZyzQSNLmb3chZqN2tnO273Bro=
github.com/amitrai48/logger v0.0.0-20190214092904-448001c055ec/go.mod h1:RZEHP3cxXvQlMuMjkpdh6qXA4b0CpjxnUBNxOpR0r30=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/daixiang0/gci v0.2.4/go.mod h1:+AV8KmHTGxxwp/pY84TLQfFKp2vuKXXJVzF3kD/hfR4=
github.com/daixiang0/gci v0.2.8 h1:1mrIGMBQsBu0P7j7m1M8Lb+ZeZxsZL+jyGX4YoMJJpg=
github.com/daixiang0/gci v0.2.8/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dmitryt/otus-golang-hw/hw12_13_14_15_calendar v0.0.0-20200916093948-5ca8860569b6 h1:kbX6KBmjfEYzBbEk2/bfAXgocxhzFQXH+5Tpr7gNg0k=
github.com/dmitryt/otus-golang-hw/hw12_13_14_15_calendar v0.0.0-20200916093948-5ca8860569b6/go.mod h1:f//zKg1isd70tsJVPWphzKLXd7pvW4Owmtobh4uhuCQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o=
github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
@ -54,88 +34,55 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.1 h1:V59tBiPuMkySHwJkuq/OYkK0WnOLwCwD3UkTbEMr12U=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
github.com/grpc-ecosystem/grpc-gateway v1.14.7/go.mod h1:oYZKL012gGh6LMyg/xA7Q2yq6j8bu0wa+9w14EEthWU=
github.com/grpc-ecosystem/grpc-gateway v1.15.0 h1:ntPNC9TD/6l2XDenJZe6T5lSMg95thpV9sGAqHX4WU8=
github.com/grpc-ecosystem/grpc-gateway v1.15.0/go.mod h1:vO11I9oWA+KsxmfFQPhLnnIb1VDE24M+pdxZFiuZcA8=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ilyakaznacheev/cleanenv v1.2.5 h1:/SlcF9GaIvefWqFJzsccGG/NJdoaAwb7Mm7ImzhO3DM=
github.com/ilyakaznacheev/cleanenv v1.2.5/go.mod h1:/i3yhzwZ3s7hacNERGFwvlhwXMDcaqwIzmayEhbRplk=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pressly/goose v2.6.0+incompatible/go.mod h1:m+QHWCqxR3k8D9l7qfzuC/djtlfzxr34mozWDYEu1z8=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tiburon-777/HW_OTUS v0.0.0-20200927064133-fc19ac2e8966 h1:LjBeW5xHnZQP7ZlViMBSaKP+HJhLSkOdSj7K7lXdbzg=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@ -144,148 +91,127 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM=
go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e h1:JgcxKXxCjrA2tyDP/aNU9K0Ck5Czfk6C7e2tMw7+bSI=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM=
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c=
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305 h1:yaM5S0KcY0lIoZo7Fl+oi91b/DdlU2zuWpfHrpWbCS0=
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70 h1:wboULUXGF3c5qdUnKp+6gLAccE6PRpa/czkYvQ4UXv8=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210504143626-3b2ad6ccc450 h1:iSifhRHb9+Pi325BWlAfpJbuG2YXlBoHE2aEFJY/Pg8=
google.golang.org/genproto v0.0.0-20210504143626-3b2ad6ccc450/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
@ -294,5 +220,3 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
olympos.io/encoding/edn v0.0.0-20200308123125-93e3b8dd0e24 h1:sreVOrDp0/ezb0CHKVek/l7YwpxPJqv+jT3izfSphA4=
olympos.io/encoding/edn v0.0.0-20200308123125-93e3b8dd0e24/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw=

View File

@ -0,0 +1,24 @@
package private
import (
"fmt"
"github.com/golang/protobuf/ptypes"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
)
func (s Service) buildEventList(evtMap map[event.ID]event.Event) ([]*Event, error) {
events := make([]*Event, len(evtMap))
var err error
var i int
for k, v := range evtMap {
evt := Event{ID: int64(k), Title: v.Title, UserID: v.UserID}
evt.Date, err = ptypes.TimestampProto(v.Date)
if err != nil {
return nil, fmt.Errorf("can't convert date: %w", err)
}
events[i] = &evt
i++
}
return events, nil
}

View File

@ -0,0 +1,17 @@
package private
import (
"fmt"
"net"
"google.golang.org/grpc"
)
func NewClient(addr, port string) (GrpcClient, error) {
conn, err := grpc.Dial(net.JoinHostPort(addr, port), grpc.WithInsecure())
if err != nil {
return nil, fmt.Errorf("can't dial GRPC server: %w", err)
}
client := NewGrpcClient(conn)
return client, nil
}

View File

@ -0,0 +1,68 @@
package private
import (
"context"
"fmt"
"net"
"github.com/golang/protobuf/ptypes/empty"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
googrpc "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type Service struct {
S *googrpc.Server
App calendar.App
}
type Config struct {
Address string
Port string
}
func New(app *calendar.App) Service {
return Service{S: googrpc.NewServer(), App: *app}
}
func (s *Service) Start(conf Config) error {
s.App.Logger.Infof("private GRPC server starting")
listnGrpc, err := net.Listen("tcp", net.JoinHostPort(conf.Address, conf.Port))
RegisterGrpcServer(s.S, s)
if err != nil {
return fmt.Errorf("can't start private GRPC service: %w", err)
}
return s.S.Serve(listnGrpc)
}
func (s *Service) Stop() {
s.S.GracefulStop()
}
func (s Service) GetNotifications(ctx context.Context, e *empty.Empty) (*GetRsp, error) {
tmp, err := s.App.Storage.GetNotifications()
if err != nil {
s.App.Logger.Errorf("storage error: can't get list of events: %w", err)
return nil, status.Errorf(codes.Internal, "storage error: can't get list of events")
}
l, err := s.buildEventList(tmp)
if err != nil {
s.App.Logger.Errorf("inconvertible: %w", err)
return nil, status.Errorf(codes.Internal, "inconvertible")
}
return &GetRsp{Events: l}, nil
}
func (s Service) SetNotified(ctx context.Context, r *SetReq) (*empty.Empty, error) {
return nil, s.App.Storage.SetNotified(event.ID(r.ID))
}
func (s Service) PurgeOldEvents(ctx context.Context, r *PurgeReq) (*PurgeResp, error) {
q, err := s.App.Storage.PurgeOldEvents(r.OlderThenDays)
if err != nil {
return &PurgeResp{}, fmt.Errorf("can't purge old events from storage: %w", err)
}
return &PurgeResp{Qty: q}, nil
}

View File

@ -0,0 +1,84 @@
package private
import (
"github.com/stretchr/testify/suite"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/logger"
store "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
oslog "log"
"testing"
"time"
)
var conf = config.Calendar{
HTTP: config.Server{Address: "localhost", Port: "50511"},
GRPC: config.Server{Address: "localhost", Port: "50512"},
API: config.Server{Address: "localhost", Port: "50513"},
Logger: config.Logger{File: "calendar.log", Level: "INFO", MuteStdout: false},
Storage: config.Storage{InMemory: true, SQLHost: "", SQLPort: "", SQLDbase: "", SQLUser: "", SQLPass: ""}}
var storeConf = store.Config(conf.Storage)
var testEvt = event.Event{Title: "Test event 1", Date: time.Now(), Latency: time.Hour * 24, Note: "First gen", UserID: 1111, NotifyTime: 0, Notified: false}
func TestCalendarSuite(t *testing.T) {
suite.Run(t, new(TestSuite))
}
type TestSuite struct {
suite.Suite
srv Service
}
func (suite *TestSuite) SetupTest() {
log, err := logger.New(logger.Config(conf.Logger))
if err != nil {
oslog.Fatal("can't init logger")
}
suite.srv = Service{App: *calendar.New(log, store.NewStore(storeConf))}
}
func (s *TestSuite) TestGetNotifications() {
controlEvent := testEvt
s.createEvent(controlEvent)
returnedEvent := s.getNotifications()
s.Equal(controlEvent.Title, returnedEvent.Title)
s.Equal(controlEvent.UserID, returnedEvent.UserID)
}
func (s *TestSuite) TestSetNotified() {
controlEvent := testEvt
id := s.createEvent(controlEvent)
s.setNotified(id)
returnedEvent := s.getEventByID(id)
s.Equal(true, returnedEvent.Notified)
}
func (s *TestSuite) createEvent(ev event.Event) event.ID {
createdEvent, err := s.srv.App.Storage.Create(ev)
s.NoError(err, "can,t create event")
s.NotEqual(createdEvent, 0, `message ID may not be a "0"`)
return createdEvent
}
func (s *TestSuite) getNotifications() Event {
evs, err := s.srv.GetNotifications(nil, nil)
s.NoError(err, "can't get events")
s.Equal(1, len(evs.Events), "length of slice in responce must be a \"1\"")
return *evs.Events[0]
}
func (s *TestSuite) setNotified(id event.ID) {
_, err := s.srv.SetNotified(nil, &SetReq{ID: int64(id)})
s.NoError(err, "can't mark event as notified")
}
func (s *TestSuite) getEventByID(id event.ID) event.Event {
r, ok := s.srv.App.Storage.GetByID(id)
s.Equal(true, ok, "can't get event by id")
return r
}

View File

@ -0,0 +1,434 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: private.proto
package private
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
empty "github.com/golang/protobuf/ptypes/empty"
timestamp "github.com/golang/protobuf/ptypes/timestamp"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type GetRsp struct {
Events []*Event `protobuf:"bytes,1,rep,name=Events,proto3" json:"Events,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GetRsp) Reset() { *m = GetRsp{} }
func (m *GetRsp) String() string { return proto.CompactTextString(m) }
func (*GetRsp) ProtoMessage() {}
func (*GetRsp) Descriptor() ([]byte, []int) {
return fileDescriptor_d2a91b51c7bdc125, []int{0}
}
func (m *GetRsp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetRsp.Unmarshal(m, b)
}
func (m *GetRsp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GetRsp.Marshal(b, m, deterministic)
}
func (m *GetRsp) XXX_Merge(src proto.Message) {
xxx_messageInfo_GetRsp.Merge(m, src)
}
func (m *GetRsp) XXX_Size() int {
return xxx_messageInfo_GetRsp.Size(m)
}
func (m *GetRsp) XXX_DiscardUnknown() {
xxx_messageInfo_GetRsp.DiscardUnknown(m)
}
var xxx_messageInfo_GetRsp proto.InternalMessageInfo
func (m *GetRsp) GetEvents() []*Event {
if m != nil {
return m.Events
}
return nil
}
type SetReq struct {
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SetReq) Reset() { *m = SetReq{} }
func (m *SetReq) String() string { return proto.CompactTextString(m) }
func (*SetReq) ProtoMessage() {}
func (*SetReq) Descriptor() ([]byte, []int) {
return fileDescriptor_d2a91b51c7bdc125, []int{1}
}
func (m *SetReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SetReq.Unmarshal(m, b)
}
func (m *SetReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SetReq.Marshal(b, m, deterministic)
}
func (m *SetReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_SetReq.Merge(m, src)
}
func (m *SetReq) XXX_Size() int {
return xxx_messageInfo_SetReq.Size(m)
}
func (m *SetReq) XXX_DiscardUnknown() {
xxx_messageInfo_SetReq.DiscardUnknown(m)
}
var xxx_messageInfo_SetReq proto.InternalMessageInfo
func (m *SetReq) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
type PurgeReq struct {
OlderThenDays int64 `protobuf:"varint,1,opt,name=OlderThenDays,proto3" json:"OlderThenDays,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PurgeReq) Reset() { *m = PurgeReq{} }
func (m *PurgeReq) String() string { return proto.CompactTextString(m) }
func (*PurgeReq) ProtoMessage() {}
func (*PurgeReq) Descriptor() ([]byte, []int) {
return fileDescriptor_d2a91b51c7bdc125, []int{2}
}
func (m *PurgeReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PurgeReq.Unmarshal(m, b)
}
func (m *PurgeReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PurgeReq.Marshal(b, m, deterministic)
}
func (m *PurgeReq) XXX_Merge(src proto.Message) {
xxx_messageInfo_PurgeReq.Merge(m, src)
}
func (m *PurgeReq) XXX_Size() int {
return xxx_messageInfo_PurgeReq.Size(m)
}
func (m *PurgeReq) XXX_DiscardUnknown() {
xxx_messageInfo_PurgeReq.DiscardUnknown(m)
}
var xxx_messageInfo_PurgeReq proto.InternalMessageInfo
func (m *PurgeReq) GetOlderThenDays() int64 {
if m != nil {
return m.OlderThenDays
}
return 0
}
type PurgeResp struct {
Qty int64 `protobuf:"varint,1,opt,name=Qty,proto3" json:"Qty,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *PurgeResp) Reset() { *m = PurgeResp{} }
func (m *PurgeResp) String() string { return proto.CompactTextString(m) }
func (*PurgeResp) ProtoMessage() {}
func (*PurgeResp) Descriptor() ([]byte, []int) {
return fileDescriptor_d2a91b51c7bdc125, []int{3}
}
func (m *PurgeResp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PurgeResp.Unmarshal(m, b)
}
func (m *PurgeResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PurgeResp.Marshal(b, m, deterministic)
}
func (m *PurgeResp) XXX_Merge(src proto.Message) {
xxx_messageInfo_PurgeResp.Merge(m, src)
}
func (m *PurgeResp) XXX_Size() int {
return xxx_messageInfo_PurgeResp.Size(m)
}
func (m *PurgeResp) XXX_DiscardUnknown() {
xxx_messageInfo_PurgeResp.DiscardUnknown(m)
}
var xxx_messageInfo_PurgeResp proto.InternalMessageInfo
func (m *PurgeResp) GetQty() int64 {
if m != nil {
return m.Qty
}
return 0
}
type Event struct {
ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
Title string `protobuf:"bytes,2,opt,name=Title,proto3" json:"Title,omitempty"`
Date *timestamp.Timestamp `protobuf:"bytes,3,opt,name=Date,proto3" json:"Date,omitempty"`
UserID int64 `protobuf:"varint,4,opt,name=UserID,proto3" json:"UserID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) {
return fileDescriptor_d2a91b51c7bdc125, []int{4}
}
func (m *Event) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Event.Unmarshal(m, b)
}
func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Event.Marshal(b, m, deterministic)
}
func (m *Event) XXX_Merge(src proto.Message) {
xxx_messageInfo_Event.Merge(m, src)
}
func (m *Event) XXX_Size() int {
return xxx_messageInfo_Event.Size(m)
}
func (m *Event) XXX_DiscardUnknown() {
xxx_messageInfo_Event.DiscardUnknown(m)
}
var xxx_messageInfo_Event proto.InternalMessageInfo
func (m *Event) GetID() int64 {
if m != nil {
return m.ID
}
return 0
}
func (m *Event) GetTitle() string {
if m != nil {
return m.Title
}
return ""
}
func (m *Event) GetDate() *timestamp.Timestamp {
if m != nil {
return m.Date
}
return nil
}
func (m *Event) GetUserID() int64 {
if m != nil {
return m.UserID
}
return 0
}
func init() {
proto.RegisterType((*GetRsp)(nil), "private.GetRsp")
proto.RegisterType((*SetReq)(nil), "private.SetReq")
proto.RegisterType((*PurgeReq)(nil), "private.PurgeReq")
proto.RegisterType((*PurgeResp)(nil), "private.PurgeResp")
proto.RegisterType((*Event)(nil), "private.Event")
}
func init() { proto.RegisterFile("private.proto", fileDescriptor_d2a91b51c7bdc125) }
var fileDescriptor_d2a91b51c7bdc125 = []byte{
// 343 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x4f, 0xc2, 0x50,
0x10, 0x84, 0x29, 0x85, 0x2a, 0x4b, 0x40, 0xdc, 0x18, 0xd2, 0xd4, 0x18, 0x9b, 0xc6, 0x98, 0x9e,
0x0a, 0xc1, 0x8b, 0x1e, 0xbc, 0x98, 0x12, 0xc2, 0x45, 0xb4, 0xe0, 0xc5, 0x5b, 0x81, 0xa5, 0x36,
0x29, 0xb4, 0xb6, 0x0b, 0x09, 0x7f, 0xcd, 0x5f, 0x67, 0xfa, 0x5e, 0x8b, 0x11, 0xe3, 0xad, 0x3b,
0x3b, 0x3b, 0x93, 0xf7, 0xa5, 0xd0, 0x4a, 0xd2, 0x70, 0xe7, 0x33, 0x39, 0x49, 0x1a, 0x73, 0x8c,
0x27, 0xc5, 0x68, 0x5c, 0x07, 0x71, 0x1c, 0x44, 0xd4, 0x13, 0xf2, 0x7c, 0xbb, 0xea, 0x71, 0xb8,
0xa6, 0x8c, 0xfd, 0x75, 0x22, 0x9d, 0xc6, 0xe5, 0xb1, 0x81, 0xd6, 0x09, 0xef, 0xe5, 0xd2, 0xea,
0x83, 0x36, 0x22, 0xf6, 0xb2, 0x04, 0x6f, 0x41, 0x1b, 0xee, 0x68, 0xc3, 0x99, 0xae, 0x98, 0xaa,
0xdd, 0x1c, 0xb4, 0x9d, 0xb2, 0x50, 0xc8, 0x5e, 0xb1, 0xb5, 0x74, 0xd0, 0xa6, 0xc4, 0x1e, 0x7d,
0x62, 0x1b, 0xaa, 0x63, 0x57, 0x57, 0x4c, 0xc5, 0x56, 0xbd, 0xea, 0xd8, 0xb5, 0xfa, 0x70, 0xfa,
0xb2, 0x4d, 0x03, 0xca, 0x77, 0x37, 0xd0, 0x9a, 0x44, 0x4b, 0x4a, 0x67, 0x1f, 0xb4, 0x71, 0xfd,
0x7d, 0x56, 0xd8, 0x7e, 0x8b, 0xd6, 0x15, 0x34, 0x8a, 0x8b, 0x2c, 0xc1, 0x0e, 0xa8, 0xaf, 0xbc,
0x2f, 0x8c, 0xf9, 0xa7, 0xb5, 0x85, 0xba, 0x28, 0x3d, 0x6e, 0xc2, 0x0b, 0xa8, 0xcf, 0x42, 0x8e,
0x48, 0xaf, 0x9a, 0x8a, 0xdd, 0xf0, 0xe4, 0x80, 0x0e, 0xd4, 0x5c, 0x9f, 0x49, 0x57, 0x4d, 0xc5,
0x6e, 0x0e, 0x0c, 0x47, 0xbe, 0xdb, 0x29, 0xdf, 0xed, 0xcc, 0x4a, 0x30, 0x9e, 0xf0, 0x61, 0x17,
0xb4, 0xb7, 0x8c, 0xd2, 0xb1, 0xab, 0xd7, 0x44, 0x72, 0x31, 0x0d, 0xbe, 0x14, 0xa8, 0x05, 0x69,
0xb2, 0xc0, 0x47, 0xe8, 0x8c, 0x88, 0x9f, 0x63, 0x0e, 0x57, 0xe1, 0xc2, 0xe7, 0x30, 0xde, 0x64,
0xd8, 0xfd, 0x13, 0x3b, 0xcc, 0x71, 0x1a, 0x67, 0x07, 0x5c, 0x92, 0xa7, 0x55, 0xc1, 0x7b, 0x68,
0x4e, 0xcb, 0x73, 0x5a, 0xe2, 0x8f, 0x43, 0xf2, 0x33, 0xfe, 0x89, 0xb2, 0x2a, 0xf8, 0x00, 0x6d,
0xc1, 0x65, 0x12, 0x2d, 0x25, 0x75, 0x3c, 0x3f, 0x1c, 0x97, 0x88, 0x0d, 0x3c, 0x96, 0xf2, 0xd2,
0xa7, 0xc6, 0x7b, 0xf9, 0x67, 0xcc, 0x35, 0x91, 0x7b, 0xf7, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x2d,
0x9f, 0xdc, 0xae, 0x3a, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// GrpcClient is the client API for Grpc service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type GrpcClient interface {
GetNotifications(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*GetRsp, error)
SetNotified(ctx context.Context, in *SetReq, opts ...grpc.CallOption) (*empty.Empty, error)
PurgeOldEvents(ctx context.Context, in *PurgeReq, opts ...grpc.CallOption) (*PurgeResp, error)
}
type grpcClient struct {
cc *grpc.ClientConn
}
func NewGrpcClient(cc *grpc.ClientConn) GrpcClient {
return &grpcClient{cc}
}
func (c *grpcClient) GetNotifications(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*GetRsp, error) {
out := new(GetRsp)
err := c.cc.Invoke(ctx, "/private.grpc/GetNotifications", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcClient) SetNotified(ctx context.Context, in *SetReq, opts ...grpc.CallOption) (*empty.Empty, error) {
out := new(empty.Empty)
err := c.cc.Invoke(ctx, "/private.grpc/SetNotified", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *grpcClient) PurgeOldEvents(ctx context.Context, in *PurgeReq, opts ...grpc.CallOption) (*PurgeResp, error) {
out := new(PurgeResp)
err := c.cc.Invoke(ctx, "/private.grpc/PurgeOldEvents", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// GrpcServer is the server API for Grpc service.
type GrpcServer interface {
GetNotifications(context.Context, *empty.Empty) (*GetRsp, error)
SetNotified(context.Context, *SetReq) (*empty.Empty, error)
PurgeOldEvents(context.Context, *PurgeReq) (*PurgeResp, error)
}
// UnimplementedGrpcServer can be embedded to have forward compatible implementations.
type UnimplementedGrpcServer struct {
}
func (*UnimplementedGrpcServer) GetNotifications(ctx context.Context, req *empty.Empty) (*GetRsp, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetNotifications not implemented")
}
func (*UnimplementedGrpcServer) SetNotified(ctx context.Context, req *SetReq) (*empty.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetNotified not implemented")
}
func (*UnimplementedGrpcServer) PurgeOldEvents(ctx context.Context, req *PurgeReq) (*PurgeResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method PurgeOldEvents not implemented")
}
func RegisterGrpcServer(s *grpc.Server, srv GrpcServer) {
s.RegisterService(&_Grpc_serviceDesc, srv)
}
func _Grpc_GetNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(empty.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcServer).GetNotifications(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/private.grpc/GetNotifications",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).GetNotifications(ctx, req.(*empty.Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Grpc_SetNotified_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcServer).SetNotified(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/private.grpc/SetNotified",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).SetNotified(ctx, req.(*SetReq))
}
return interceptor(ctx, in, info, handler)
}
func _Grpc_PurgeOldEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PurgeReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GrpcServer).PurgeOldEvents(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/private.grpc/PurgeOldEvents",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).PurgeOldEvents(ctx, req.(*PurgeReq))
}
return interceptor(ctx, in, info, handler)
}
var _Grpc_serviceDesc = grpc.ServiceDesc{
ServiceName: "private.grpc",
HandlerType: (*GrpcServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetNotifications",
Handler: _Grpc_GetNotifications_Handler,
},
{
MethodName: "SetNotified",
Handler: _Grpc_SetNotified_Handler,
},
{
MethodName: "PurgeOldEvents",
Handler: _Grpc_PurgeOldEvents_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "private.proto",
}

View File

@ -1,13 +1,12 @@
package app
package calendar
import (
"context"
"net/http"
"time"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/logger"
store "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/event"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/logger"
store "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage"
)
type Interface interface {
@ -25,20 +24,6 @@ func New(logger logger.Interface, storage store.StorageInterface) *App {
return &App{Logger: logger, Storage: storage}
}
func (a *App) CreateEvent(ctx context.Context, title string) (err error) {
_, err = a.Storage.Create(event.Event{Title: title})
if err != nil {
a.Logger.Errorf("can not create event")
}
a.Logger.Infof("event created")
return err
}
func (a *App) Handler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
_, _ = w.Write([]byte("Hello! I'm calendar app!"))
}
func (a *App) LoggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()

View File

@ -1,51 +0,0 @@
package config
import (
"io/ioutil"
"os"
"github.com/BurntSushi/toml"
)
type Config struct {
GRPC Server
HTTP Server
Logger Logger
Storage Storage
}
type Server struct {
Address string
Port string
}
type Logger struct {
File string
Level string
MuteStdout bool
}
type Storage struct {
InMemory bool
SQLHost string
SQLPort string
SQLDbase string
SQLUser string
SQLPass string
}
// Confita может быти и хороша, но она не возвращает ошибки, если не может распарсить файл в структуру. Мне не нравится такая "молчаливость".
func NewConfig(configFile string) (Config, error) {
f, err := os.Open(configFile)
if err != nil {
return Config{}, err
}
defer f.Close()
s, err := ioutil.ReadAll(f)
if err != nil {
return Config{}, err
}
var config Config
_, err = toml.Decode(string(s), &config)
return config, err
}

View File

@ -1,58 +0,0 @@
package config
import (
"github.com/stretchr/testify/require"
"io/ioutil"
oslog "log"
"os"
"testing"
)
var _ = func() bool {
testing.Init()
return true
}()
func TestNewConfig(t *testing.T) {
badfile, err := ioutil.TempFile("", "conf.")
if err != nil {
oslog.Fatal(err)
}
defer os.Remove(badfile.Name())
badfile.WriteString(`aefSD
sadfg
RFABND FYGUMG
V`)
badfile.Sync()
goodfile, err := ioutil.TempFile("", "conf.")
if err != nil {
oslog.Fatal(err)
}
defer os.Remove(goodfile.Name())
goodfile.WriteString(`[storage]
inMemory = true
SQLHost = "localhost"`)
goodfile.Sync()
t.Run("No such file", func(t *testing.T) {
c, e := NewConfig("adfergdth")
require.Equal(t, Config{}, c)
require.Error(t, e)
})
t.Run("Bad file", func(t *testing.T) {
c, e := NewConfig(badfile.Name())
require.Equal(t, Config{}, c)
require.Error(t, e)
})
t.Run("TOML reading", func(t *testing.T) {
c, e := NewConfig(goodfile.Name())
require.Equal(t, true, c.Storage.InMemory)
require.Equal(t, "localhost", c.Storage.SQLHost)
require.NoError(t, e)
})
}

View File

@ -1,375 +0,0 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: grpcserver.proto
/*
Package grpcserver is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package grpcserver
import (
"io"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/empty"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/status"
)
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
func request_Grpc_Create_0(ctx context.Context, marshaler runtime.Marshaler, client GrpcClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CreateReq
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Create(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_Grpc_Update_0(ctx context.Context, marshaler runtime.Marshaler, client GrpcClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq UpdateReq
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["ID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ID")
}
protoReq.ID, err = runtime.Int64(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ID", err)
}
msg, err := client.Update(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_Grpc_Delete_0(ctx context.Context, marshaler runtime.Marshaler, client GrpcClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteReq
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["ID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ID")
}
protoReq.ID, err = runtime.Int64(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ID", err)
}
msg, err := client.Delete(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_Grpc_List_0(ctx context.Context, marshaler runtime.Marshaler, client GrpcClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq empty.Empty
var metadata runtime.ServerMetadata
msg, err := client.List(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_Grpc_GetByID_0(ctx context.Context, marshaler runtime.Marshaler, client GrpcClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetByIDReq
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["ID"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "ID")
}
protoReq.ID, err = runtime.Int64(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "ID", err)
}
msg, err := client.GetByID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func request_Grpc_GetByDate_0(ctx context.Context, marshaler runtime.Marshaler, client GrpcClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetByDateReq
var metadata runtime.ServerMetadata
var (
val string
e int32
ok bool
err error
_ = err
)
val, ok = pathParams["Range"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "Range")
}
e, err = runtime.Enum(val, QueryRange_value)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "Range", err)
}
protoReq.Range = QueryRange(e)
val, ok = pathParams["Date"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "Date")
}
protoReq.Date, err = runtime.Timestamp(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "Date", err)
}
msg, err := client.GetByDate(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterGrpcHandlerFromEndpoint is same as RegisterGrpcHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterGrpcHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterGrpcHandler(ctx, mux, conn)
}
// RegisterGrpcHandler registers the http handlers for service Grpc to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterGrpcHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterGrpcHandlerClient(ctx, mux, NewGrpcClient(conn))
}
// RegisterGrpcHandlerClient registers the http handlers for service Grpc
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "GrpcClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "GrpcClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "GrpcClient" to call the correct interceptors.
func RegisterGrpcHandlerClient(ctx context.Context, mux *runtime.ServeMux, client GrpcClient) error {
mux.Handle("POST", pattern_Grpc_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Grpc_Create_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Grpc_Create_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_Grpc_Update_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Grpc_Update_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Grpc_Update_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_Grpc_Delete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Grpc_Delete_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Grpc_Delete_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Grpc_List_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Grpc_List_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Grpc_List_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Grpc_GetByID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Grpc_GetByID_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Grpc_GetByID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Grpc_GetByDate_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Grpc_GetByDate_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Grpc_GetByDate_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Grpc_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"events"}, ""))
pattern_Grpc_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"events", "ID"}, ""))
pattern_Grpc_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"events", "ID"}, ""))
pattern_Grpc_List_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"events"}, ""))
pattern_Grpc_GetByID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1}, []string{"events", "ID"}, ""))
pattern_Grpc_GetByDate_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 1, 0, 4, 1, 5, 1, 1, 0, 4, 1, 5, 2}, []string{"events", "Range", "Date"}, ""))
)
var (
forward_Grpc_Create_0 = runtime.ForwardResponseMessage
forward_Grpc_Update_0 = runtime.ForwardResponseMessage
forward_Grpc_Delete_0 = runtime.ForwardResponseMessage
forward_Grpc_List_0 = runtime.ForwardResponseMessage
forward_Grpc_GetByID_0 = runtime.ForwardResponseMessage
forward_Grpc_GetByDate_0 = runtime.ForwardResponseMessage
)

View File

@ -1,32 +0,0 @@
package grpcserver
import (
"net"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/app"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/config"
googrpc "google.golang.org/grpc"
)
type Server struct {
s *googrpc.Server
app app.App
}
func New(app *app.App) Server {
return Server{s: googrpc.NewServer(), app: *app}
}
func (s *Server) Start(conf config.Config) error {
s.app.Logger.Infof("GRPC server starting")
listnGrpc, err := net.Listen("tcp", net.JoinHostPort(conf.GRPC.Address, conf.GRPC.Port))
RegisterGrpcServer(s.s, &Service{})
if err != nil {
return err
}
return s.s.Serve(listnGrpc)
}
func (s *Server) Stop() {
s.s.GracefulStop()
}

View File

@ -0,0 +1,75 @@
package sender
import (
"context"
"encoding/json"
"fmt"
oslog "log"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/api/private"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/logger"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/rabbit"
)
type Sender struct {
GRPCAPI private.GrpcClient
Logger logger.Interface
Rabbit *rabbit.Rabbit
Queue string
Stop context.CancelFunc
}
type Config struct {
CalendarAPI config.Server
Rabbitmq config.Rabbit
Logger config.Logger
}
func New(conf Config) Sender {
log, err := logger.New(logger.Config(conf.Logger))
if err != nil {
oslog.Fatal("can't start logger:", err.Error())
}
rb, err := rabbit.Attach(rabbit.Config(conf.Rabbitmq))
if err != nil {
log.Fatalf("failed to connect to RabbitMQ:", err.Error())
}
cli, err := private.NewClient(conf.CalendarAPI.Address, conf.CalendarAPI.Port)
if err != nil {
log.Fatalf("can't get GRPC client: %w", err.Error())
}
return Sender{Logger: log, Rabbit: rb, Queue: conf.Rabbitmq.Queue, GRPCAPI: cli}
}
func (s *Sender) Start() error {
ctx, cancel := context.WithCancel(context.Background())
s.Stop = cancel
msg, err := s.Rabbit.Consume(ctx, s.Queue)
if err != nil {
return fmt.Errorf("can't consume RabbitMQ queue: %w", err)
}
go func() {
for {
select {
case m := <-msg:
if m.Data != nil {
var data private.Event
err := json.Unmarshal(m.Data, &data)
if err != nil {
s.Logger.Errorf("can`t unmarshal data %w", err)
}
_, err = s.GRPCAPI.SetNotified(ctx, &private.SetReq{ID: data.ID})
if err != nil {
s.Logger.Errorf("can`t mark event with ID %d as notified data %w", data.ID, err)
}
s.Logger.Infof("User %s notified about event %s", data.UserID, data.ID)
}
case <-ctx.Done():
return
}
}
}()
return nil
}

View File

@ -0,0 +1,57 @@
package sheduler
import (
"context"
oslog "log"
"time"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/logger"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/rabbit"
)
type Scheduler struct {
CalendarAPI config.Server
Logger logger.Interface
Rabbit *rabbit.Rabbit
Stop context.CancelFunc
}
type Config struct {
CalendarAPI config.Server
Rabbitmq config.Rabbit
Storage config.Storage
Logger config.Logger
}
func New(conf Config) Scheduler {
log, err := logger.New(logger.Config(conf.Logger))
if err != nil {
oslog.Fatal("can't start logger:", err.Error())
}
rb, err := rabbit.New(rabbit.Config(conf.Rabbitmq))
if err != nil {
log.Fatalf("failed to connect to RabbitMQ:", err.Error())
}
return Scheduler{CalendarAPI: conf.CalendarAPI, Logger: log, Rabbit: rb}
}
func (s *Scheduler) Start() error {
ctx, cancel := context.WithCancel(context.Background())
s.Stop = cancel
fetcher := riseOnTick(ctx, s.Logger, func() interface{} { return worker(ctx, s.CalendarAPI, s.Rabbit, s.Logger) }, 10*time.Second)
go func() {
for {
select {
case err := <-fetcher:
s.Logger.Infof("fetcher closed")
if err != nil {
s.Logger.Errorf("...with error:", err)
}
case <-ctx.Done():
return
}
}
}()
return nil
}

View File

@ -0,0 +1,67 @@
package sheduler
import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/golang/protobuf/ptypes/empty"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/api/private"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/logger"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/rabbit"
)
func riseOnTick(ctx context.Context, log logger.Interface, fn func() interface{}, interval time.Duration) <-chan interface{} {
valueStream := make(chan interface{})
go func() {
t := time.NewTicker(interval)
defer func() {
t.Stop()
close(valueStream)
}()
for {
select {
case <-ctx.Done():
return
case <-t.C:
select {
case <-ctx.Done():
return
default:
log.Infof("scheduler come to rabbit")
valueStream <- fn()
}
}
}
}()
return valueStream
}
func worker(ctx context.Context, calendarAPI config.Server, rb *rabbit.Rabbit, log logger.Interface) error {
cli, err := private.NewClient(calendarAPI.Address, calendarAPI.Port)
if err != nil {
return fmt.Errorf("can't get GRPC client: %w", err)
}
resp, err := cli.GetNotifications(ctx, &empty.Empty{})
if err != nil {
return fmt.Errorf("can't get events from GRPC endpoint: %w", err)
}
for _, event := range resp.Events {
b, err := json.Marshal(event)
if err != nil {
return fmt.Errorf("can't marshal events into JSON: %w", err)
}
err = rb.Publish(b)
if err != nil {
return fmt.Errorf("can't publish serialized data to RabbitMQ: %w", err)
}
}
resp2, err := cli.PurgeOldEvents(ctx, &private.PurgeReq{OlderThenDays: 365})
if err != nil {
return fmt.Errorf("can't purge old events: %w", err)
}
log.Infof("Scheduler successfully purges %s events from storage", resp2.Qty)
return nil
}

View File

@ -1,173 +0,0 @@
package sql
import (
"database/sql"
"time"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/event"
)
const dateTimeLayout = "2006-01-02 15:04:00 -0700"
type Config struct {
User string
Pass string
Host string
Port string
Dbase string
}
type Storage struct {
db *sql.DB
}
func New(conf Config) *Storage {
return &Storage{}
}
func (s *Storage) Connect(config Config) error {
var err error
s.db, err = sql.Open("mysql", config.User+":"+config.Pass+"@tcp("+config.Host+":"+config.Port+")/"+config.Dbase)
if err != nil {
return err
}
return nil
}
func (s *Storage) Close() error {
return s.db.Close()
}
func (s *Storage) Create(ev event.Event) (event.ID, error) {
res, err := s.db.Exec(
`INSERT INTO events
(title, date, latency, note, userID, notifyTime) VALUES
($1, $2, $3, $4, $5, $6)`,
ev.Title,
ev.Date.Format(dateTimeLayout),
ev.Latency,
ev.Note,
ev.UserID,
ev.NotifyTime,
)
if err != nil {
return -1, err
}
idint64, err := res.LastInsertId()
return event.ID(idint64), err
}
func (s *Storage) Update(id event.ID, event event.Event) error {
_, err := s.db.Exec(
`UPDATE events set
title=$1, date=$2, latency=$3, note=$4, userID=$5, notifyTime=$6
where id=$7`,
event.Title,
event.Date.Format(dateTimeLayout),
event.Latency,
event.Note,
event.UserID,
event.NotifyTime,
id,
)
return err
}
func (s *Storage) Delete(id event.ID) error {
_, err := s.db.Exec(
`DELETE from events where id=$1`,
id,
)
return err
}
func (s *Storage) List() (map[event.ID]event.Event, error) {
res := make(map[event.ID]event.Event)
results, err := s.db.Query(`SELECT (id,title,date,latency,note,userID,notifyTime) from events ORDER BY id`)
if err != nil {
return nil, err
}
defer results.Close()
for results.Next() {
var id event.ID
var evt event.Event
var dateRaw string
err = results.Scan(&id, &evt.Title, &dateRaw, &evt.Latency, &evt.Note, &evt.UserID, &evt.NotifyTime)
if err != nil {
return nil, err
}
evt.Date, err = time.Parse(dateTimeLayout, dateRaw)
if err != nil {
return nil, err
}
res[id] = evt
}
if results.Err() != nil {
return nil, results.Err()
}
return res, nil
}
func (s *Storage) GetByID(id event.ID) (event.Event, bool) {
var res event.Event
var dateRaw string
err := s.db.QueryRow(
`SELECT (id,title,date,latency,note,userID,notifyTime) from events where id=$1`, id).Scan(res.Title, dateRaw, res.Latency, res.Note, res.UserID, res.NotifyTime)
if err != nil {
return res, false
}
dateParced, err := time.Parse(dateTimeLayout, dateRaw)
if err != nil {
return res, false
}
res.Date = dateParced
return res, true
}
func (s *Storage) GetByDate(startDate time.Time, rng string) (map[event.ID]event.Event, error) {
res := make(map[event.ID]event.Event)
results, err := s.db.Query(
`SELECT (id,title,date,latency,note,userID,notifyTime)
from events
where (date>$1 AND date<$2) OR
(date+latency>$1 AND date+latency<$2) OR
(date<$1 AND date+latency>$2)
ORDER BY id`,
startDate,
getEndDate(startDate, rng))
if err != nil {
return nil, err
}
defer results.Close()
for results.Next() {
var id event.ID
var evt event.Event
var dateRaw string
err = results.Scan(&id, &evt.Title, &dateRaw, &evt.Latency, &evt.Note, &evt.UserID, &evt.NotifyTime)
if err != nil {
return nil, err
}
evt.Date, err = time.Parse(dateTimeLayout, dateRaw)
if err != nil {
return nil, err
}
res[id] = evt
}
if results.Err() != nil {
return nil, results.Err()
}
return res, nil
}
func getEndDate(startDate time.Time, rng string) time.Time {
switch rng {
case "DAY":
return startDate.AddDate(0, 0, 1)
case "WEEK":
return startDate.AddDate(0, 0, 7)
case "MONTH":
return startDate.AddDate(0, 1, 0)
default:
return startDate
}
}

View File

@ -1,18 +0,0 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE events (
id int(16) NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL,
date datetime NOT NULL,
latency int(16) NOT NULL,
note text,
userID int(16),
notifyTime int(16)
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;;
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE events;
-- +goose StatementEnd

View File

@ -0,0 +1,18 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE events (
id serial NOT NULL,
title varchar(255) NOT NULL,
date timestamp NOT NULL,
latency int8 NOT NULL,
note text NULL,
userID int8 NOT NULL,
notifyTime int8 NULL,
notified bool
);
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE events;
-- +goose StatementEnd

View File

@ -1,25 +1,26 @@
package grpcserver
package public
import (
"fmt"
"time"
"github.com/golang/protobuf/ptypes"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/event"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
)
func (s Service) buildStorageEvent(pbe *CreateReq) (res event.Event, err error) {
res = event.Event{Title: pbe.Title, Note: pbe.Note, UserID: pbe.UserID}
res.Date, err = ptypes.Timestamp(pbe.Date)
if err != nil {
return event.Event{}, err
return event.Event{}, fmt.Errorf("can't convert date: %w", err)
}
res.Latency, err = ptypes.Duration(pbe.Latency)
if err != nil {
return event.Event{}, err
return event.Event{}, fmt.Errorf("can't convert duration: %w", err)
}
res.NotifyTime, err = ptypes.Duration(pbe.NotifyTime)
if err != nil {
return event.Event{}, err
return event.Event{}, fmt.Errorf("can't convert duration: %w", err)
}
return res, nil
}
@ -28,15 +29,15 @@ func (s Service) buildStorageEventAndID(pbe *UpdateReq) (id event.ID, evt event.
evt = event.Event{Title: pbe.Event.Title, Note: pbe.Event.Note, UserID: pbe.Event.UserID}
evt.Date, err = ptypes.Timestamp(pbe.Event.Date)
if err != nil {
return 0, event.Event{}, err
return 0, event.Event{}, fmt.Errorf("can't convert time: %w", err)
}
evt.Latency, err = ptypes.Duration(pbe.Event.Latency)
if err != nil {
return 0, event.Event{}, err
return 0, event.Event{}, fmt.Errorf("can't convert duration: %w", err)
}
evt.NotifyTime, err = ptypes.Duration(pbe.Event.NotifyTime)
if err != nil {
return 0, event.Event{}, err
return 0, event.Event{}, fmt.Errorf("can't convert duration: %w", err)
}
return event.ID(pbe.ID), evt, nil
}
@ -49,15 +50,18 @@ func (s Service) buildEventList(evtMap map[event.ID]event.Event) ([]*Event, erro
evt := Event{ID: int64(k), Title: v.Title, Latency: ptypes.DurationProto(v.Latency), Note: v.Note, UserID: v.UserID, NotifyTime: ptypes.DurationProto(v.NotifyTime)}
evt.Date, err = ptypes.TimestampProto(v.Date)
if err != nil {
return nil, err
return nil, fmt.Errorf("can't convert date: %w", err)
}
events[i] = &evt
i++
}
return events, err
return events, nil
}
func (s Service) buildTimeAndRange(e *GetByDateReq) (start time.Time, qrange string, err error) {
date, err := ptypes.Timestamp(e.Date)
return date, e.Range.String(), err
if err != nil {
return time.Time{}, "", fmt.Errorf("can't convert date: %w", err)
}
return date, e.Range.String(), nil
}

View File

@ -0,0 +1,18 @@
package public
import (
"context"
"fmt"
"net"
"google.golang.org/grpc"
)
func NewClient(ctx context.Context, addr, port string) (GrpcClient, error) {
conn, err := grpc.DialContext(ctx, net.JoinHostPort(addr, port), grpc.WithInsecure())
if err != nil {
return nil, fmt.Errorf("can't dial GRPC server: %w", err)
}
client := NewGrpcClient(conn)
return client, nil
}

View File

@ -1,27 +1,52 @@
package grpcserver
package public
import (
"context"
"fmt"
"net"
"github.com/golang/protobuf/ptypes/empty"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/app"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/event"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
googrpc "google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type Service struct {
App app.App
S *googrpc.Server
App calendar.App
}
func New(app *calendar.App) Service {
return Service{S: googrpc.NewServer(), App: *app}
}
func (s *Service) Start(conf config.Calendar) error {
s.App.Logger.Infof("public GRPC server starting")
listnGrpc, err := net.Listen("tcp", net.JoinHostPort(conf.GRPC.Address, conf.GRPC.Port))
if err != nil {
return fmt.Errorf("can't start public GRPC service: %w", err)
}
RegisterGrpcServer(s.S, s)
return s.S.Serve(listnGrpc)
}
func (s *Service) Stop() {
s.S.GracefulStop()
}
func (s Service) Create(ctx context.Context, e *CreateReq) (*CreateRsp, error) {
var res CreateRsp
ce, err := s.buildStorageEvent(e)
if err != nil {
s.App.Logger.Errorf("inconvertible event: %w", err)
return nil, status.Errorf(codes.Internal, "inconvertible")
}
t, err := s.App.Storage.Create(ce)
if err != nil {
s.App.Logger.Errorf("can't create event in storage: %w", err)
return nil, status.Errorf(codes.Internal, "storage error: can't create event")
}
res.ID = int64(t)
@ -31,28 +56,33 @@ func (s Service) Create(ctx context.Context, e *CreateReq) (*CreateRsp, error) {
func (s Service) Update(ctx context.Context, e *UpdateReq) (*empty.Empty, error) {
cid, ce, err := s.buildStorageEventAndID(e)
if err != nil {
return nil, status.Errorf(codes.Internal, "inconvertible")
s.App.Logger.Errorf("inconvertible event: %w", err)
return &empty.Empty{}, status.Errorf(codes.Internal, "inconvertible")
}
if s.App.Storage.Update(cid, ce) != nil {
return nil, status.Errorf(codes.Internal, "storage error: can't update event")
s.App.Logger.Errorf("can't update event in storage: %w", err)
return &empty.Empty{}, status.Errorf(codes.Internal, "storage error: can't update event")
}
return nil, nil
return &empty.Empty{}, nil
}
func (s Service) Delete(ctx context.Context, e *DeleteReq) (*empty.Empty, error) {
if s.App.Storage.Delete(event.ID(e.ID)) != nil {
return nil, status.Errorf(codes.Internal, "storage error: can't update event")
if err := s.App.Storage.Delete(event.ID(e.ID)); err != nil {
s.App.Logger.Errorf("can't update event in storage: %w", err)
return &empty.Empty{}, status.Errorf(codes.Internal, "storage error: can't update event")
}
return nil, nil
return &empty.Empty{}, nil
}
func (s Service) List(ctx context.Context, e *empty.Empty) (*ListResp, error) {
tmp, err := s.App.Storage.List()
if err != nil {
s.App.Logger.Errorf("can't get list of events from storage: %w", err)
return nil, status.Errorf(codes.Internal, "storage error: can't get list of events")
}
l, err := s.buildEventList(tmp)
if err != nil {
s.App.Logger.Errorf("inconvertible list of events: %w", err)
return nil, status.Errorf(codes.Internal, "inconvertible")
}
return &ListResp{Events: l}, nil
@ -61,10 +91,12 @@ func (s Service) List(ctx context.Context, e *empty.Empty) (*ListResp, error) {
func (s Service) GetByID(ctx context.Context, e *GetByIDReq) (*GetByIDResp, error) {
tmp, ok := s.App.Storage.GetByID(event.ID(e.ID))
if !ok {
s.App.Logger.Errorf("event with ID %s not found in storage", e.ID)
return nil, status.Errorf(codes.NotFound, "event not found")
}
l, err := s.buildEventList(map[event.ID]event.Event{event.ID(e.ID): tmp})
if err != nil {
s.App.Logger.Errorf("inconvertible list of events: %w", err)
return nil, status.Errorf(codes.Internal, "inconvertible")
}
return &GetByIDResp{Events: l}, nil
@ -73,14 +105,17 @@ func (s Service) GetByID(ctx context.Context, e *GetByIDReq) (*GetByIDResp, erro
func (s Service) GetByDate(ctx context.Context, e *GetByDateReq) (*GetByDateResp, error) {
d, r, err := s.buildTimeAndRange(e)
if err != nil {
s.App.Logger.Errorf("inconvertible time range: %w", err)
return nil, status.Errorf(codes.Internal, "inconvertible")
}
tmp, err := s.App.Storage.GetByDate(d, r)
if err != nil {
s.App.Logger.Errorf("can't get list of events from storage: %w", err)
return nil, status.Errorf(codes.Internal, "storage error: can't get list of events")
}
l, err := s.buildEventList(tmp)
if err != nil {
s.App.Logger.Errorf("inconvertible list of events: %w", err)
return nil, status.Errorf(codes.Internal, "inconvertible")
}
return &GetByDateResp{Events: l}, nil

View File

@ -1,19 +1,24 @@
package grpcserver
package public
import (
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/stretchr/testify/suite"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/app"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/logger"
store "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/config"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/logger"
store "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage"
oslog "log"
"testing"
"time"
)
var conf = config.Config{HTTP: config.Server{Address: "localhost", Port: "50511"}, GRPC: config.Server{Address: "localhost", Port: "50512"}, Logger: config.Logger{File: "calendar.log", Level: "INFO", MuteStdout: false}, Storage: config.Storage{InMemory: true, SQLHost: "", SQLPort: "", SQLDbase: "", SQLUser: "", SQLPass: ""}}
var conf = config.Calendar{
HTTP: config.Server{Address: "localhost", Port: "50511"},
GRPC: config.Server{Address: "localhost", Port: "50512"},
API: config.Server{Address: "localhost", Port: "50513"},
Logger: config.Logger{File: "calendar.log", Level: "INFO", MuteStdout: false},
Storage: config.Storage{InMemory: true, SQLHost: "", SQLPort: "", SQLDbase: "", SQLUser: "", SQLPass: ""}}
var storeConf = store.Config(conf.Storage)
@ -31,11 +36,11 @@ type TestSuite struct {
}
func (suite *TestSuite) SetupTest() {
log, err := logger.New(conf)
log, err := logger.New(logger.Config(conf.Logger))
if err != nil {
oslog.Fatal("can't init logger")
}
suite.srv = Service{App: *app.New(log, store.NewStore(storeConf))}
suite.srv = Service{App: *calendar.New(log, store.NewStore(storeConf))}
}
func (s *TestSuite) TestCreateEvent() {
@ -74,7 +79,7 @@ func (s *TestSuite) TestListEvent() {
s.createEvent(&testEvent)
eventList := s.listEvents()
s.GreaterOrEqual(len(eventList),1)
s.GreaterOrEqual(len(eventList), 1)
s.Equal(testEvt1.Title, eventList[0].Title)
}
@ -91,7 +96,7 @@ func (s *TestSuite) TestGetEventByDate() {
s.createEvent(&testEvent)
eventList := s.getEventByDate(testEvent.Date)
s.GreaterOrEqual(len(eventList),1)
s.GreaterOrEqual(len(eventList), 1)
s.Equal(testEvent.Title, eventList[0].Title)
}

View File

@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: grpcserver.proto
// source: public.proto
package grpcserver
package public
import (
context "context"
@ -53,7 +53,7 @@ func (x QueryRange) String() string {
}
func (QueryRange) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{0}
return fileDescriptor_413a91106d7bcce8, []int{0}
}
type Event struct {
@ -64,6 +64,7 @@ type Event struct {
Note string `protobuf:"bytes,5,opt,name=Note,proto3" json:"Note,omitempty"`
UserID int64 `protobuf:"varint,6,opt,name=UserID,proto3" json:"UserID,omitempty"`
NotifyTime *duration.Duration `protobuf:"bytes,7,opt,name=NotifyTime,proto3" json:"NotifyTime,omitempty"`
Notified bool `protobuf:"varint,8,opt,name=notified,proto3" json:"notified,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -73,7 +74,7 @@ func (m *Event) Reset() { *m = Event{} }
func (m *Event) String() string { return proto.CompactTextString(m) }
func (*Event) ProtoMessage() {}
func (*Event) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{0}
return fileDescriptor_413a91106d7bcce8, []int{0}
}
func (m *Event) XXX_Unmarshal(b []byte) error {
@ -143,6 +144,13 @@ func (m *Event) GetNotifyTime() *duration.Duration {
return nil
}
func (m *Event) GetNotified() bool {
if m != nil {
return m.Notified
}
return false
}
type CreateReq struct {
Title string `protobuf:"bytes,2,opt,name=Title,proto3" json:"Title,omitempty"`
Date *timestamp.Timestamp `protobuf:"bytes,3,opt,name=Date,proto3" json:"Date,omitempty"`
@ -159,7 +167,7 @@ func (m *CreateReq) Reset() { *m = CreateReq{} }
func (m *CreateReq) String() string { return proto.CompactTextString(m) }
func (*CreateReq) ProtoMessage() {}
func (*CreateReq) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{1}
return fileDescriptor_413a91106d7bcce8, []int{1}
}
func (m *CreateReq) XXX_Unmarshal(b []byte) error {
@ -233,7 +241,7 @@ func (m *CreateRsp) Reset() { *m = CreateRsp{} }
func (m *CreateRsp) String() string { return proto.CompactTextString(m) }
func (*CreateRsp) ProtoMessage() {}
func (*CreateRsp) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{2}
return fileDescriptor_413a91106d7bcce8, []int{2}
}
func (m *CreateRsp) XXX_Unmarshal(b []byte) error {
@ -273,7 +281,7 @@ func (m *UpdateReq) Reset() { *m = UpdateReq{} }
func (m *UpdateReq) String() string { return proto.CompactTextString(m) }
func (*UpdateReq) ProtoMessage() {}
func (*UpdateReq) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{3}
return fileDescriptor_413a91106d7bcce8, []int{3}
}
func (m *UpdateReq) XXX_Unmarshal(b []byte) error {
@ -319,7 +327,7 @@ func (m *DeleteReq) Reset() { *m = DeleteReq{} }
func (m *DeleteReq) String() string { return proto.CompactTextString(m) }
func (*DeleteReq) ProtoMessage() {}
func (*DeleteReq) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{4}
return fileDescriptor_413a91106d7bcce8, []int{4}
}
func (m *DeleteReq) XXX_Unmarshal(b []byte) error {
@ -358,7 +366,7 @@ func (m *ListResp) Reset() { *m = ListResp{} }
func (m *ListResp) String() string { return proto.CompactTextString(m) }
func (*ListResp) ProtoMessage() {}
func (*ListResp) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{5}
return fileDescriptor_413a91106d7bcce8, []int{5}
}
func (m *ListResp) XXX_Unmarshal(b []byte) error {
@ -397,7 +405,7 @@ func (m *GetByIDReq) Reset() { *m = GetByIDReq{} }
func (m *GetByIDReq) String() string { return proto.CompactTextString(m) }
func (*GetByIDReq) ProtoMessage() {}
func (*GetByIDReq) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{6}
return fileDescriptor_413a91106d7bcce8, []int{6}
}
func (m *GetByIDReq) XXX_Unmarshal(b []byte) error {
@ -436,7 +444,7 @@ func (m *GetByIDResp) Reset() { *m = GetByIDResp{} }
func (m *GetByIDResp) String() string { return proto.CompactTextString(m) }
func (*GetByIDResp) ProtoMessage() {}
func (*GetByIDResp) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{7}
return fileDescriptor_413a91106d7bcce8, []int{7}
}
func (m *GetByIDResp) XXX_Unmarshal(b []byte) error {
@ -466,7 +474,7 @@ func (m *GetByIDResp) GetEvents() []*Event {
type GetByDateReq struct {
Date *timestamp.Timestamp `protobuf:"bytes,1,opt,name=Date,proto3" json:"Date,omitempty"`
Range QueryRange `protobuf:"varint,2,opt,name=Range,proto3,enum=grpcserver.QueryRange" json:"Range,omitempty"`
Range QueryRange `protobuf:"varint,2,opt,name=Range,proto3,enum=public.QueryRange" json:"Range,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -476,7 +484,7 @@ func (m *GetByDateReq) Reset() { *m = GetByDateReq{} }
func (m *GetByDateReq) String() string { return proto.CompactTextString(m) }
func (*GetByDateReq) ProtoMessage() {}
func (*GetByDateReq) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{8}
return fileDescriptor_413a91106d7bcce8, []int{8}
}
func (m *GetByDateReq) XXX_Unmarshal(b []byte) error {
@ -522,7 +530,7 @@ func (m *GetByDateResp) Reset() { *m = GetByDateResp{} }
func (m *GetByDateResp) String() string { return proto.CompactTextString(m) }
func (*GetByDateResp) ProtoMessage() {}
func (*GetByDateResp) Descriptor() ([]byte, []int) {
return fileDescriptor_afa6debe97205904, []int{9}
return fileDescriptor_413a91106d7bcce8, []int{9}
}
func (m *GetByDateResp) XXX_Unmarshal(b []byte) error {
@ -551,61 +559,62 @@ func (m *GetByDateResp) GetEvents() []*Event {
}
func init() {
proto.RegisterEnum("grpcserver.QueryRange", QueryRange_name, QueryRange_value)
proto.RegisterType((*Event)(nil), "grpcserver.Event")
proto.RegisterType((*CreateReq)(nil), "grpcserver.CreateReq")
proto.RegisterType((*CreateRsp)(nil), "grpcserver.CreateRsp")
proto.RegisterType((*UpdateReq)(nil), "grpcserver.UpdateReq")
proto.RegisterType((*DeleteReq)(nil), "grpcserver.DeleteReq")
proto.RegisterType((*ListResp)(nil), "grpcserver.ListResp")
proto.RegisterType((*GetByIDReq)(nil), "grpcserver.GetByIDReq")
proto.RegisterType((*GetByIDResp)(nil), "grpcserver.GetByIDResp")
proto.RegisterType((*GetByDateReq)(nil), "grpcserver.GetByDateReq")
proto.RegisterType((*GetByDateResp)(nil), "grpcserver.GetByDateResp")
proto.RegisterEnum("public.QueryRange", QueryRange_name, QueryRange_value)
proto.RegisterType((*Event)(nil), "public.Event")
proto.RegisterType((*CreateReq)(nil), "public.CreateReq")
proto.RegisterType((*CreateRsp)(nil), "public.CreateRsp")
proto.RegisterType((*UpdateReq)(nil), "public.UpdateReq")
proto.RegisterType((*DeleteReq)(nil), "public.DeleteReq")
proto.RegisterType((*ListResp)(nil), "public.ListResp")
proto.RegisterType((*GetByIDReq)(nil), "public.GetByIDReq")
proto.RegisterType((*GetByIDResp)(nil), "public.GetByIDResp")
proto.RegisterType((*GetByDateReq)(nil), "public.GetByDateReq")
proto.RegisterType((*GetByDateResp)(nil), "public.GetByDateResp")
}
func init() { proto.RegisterFile("grpcserver.proto", fileDescriptor_afa6debe97205904) }
func init() { proto.RegisterFile("public.proto", fileDescriptor_413a91106d7bcce8) }
var fileDescriptor_afa6debe97205904 = []byte{
// 608 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x95, 0x4f, 0x6f, 0xd3, 0x4c,
0x10, 0xc6, 0x5f, 0x27, 0x8e, 0x53, 0x4f, 0xf3, 0x96, 0x74, 0x14, 0x52, 0xd7, 0xad, 0x4a, 0xb4,
0x17, 0x42, 0x84, 0x6c, 0x29, 0x15, 0x12, 0xf4, 0x46, 0x71, 0x04, 0x81, 0x36, 0x14, 0x2b, 0x15,
0x82, 0x13, 0x6e, 0xbb, 0x8d, 0x2c, 0x25, 0xb6, 0xb1, 0x37, 0x95, 0xa2, 0x2a, 0x17, 0xbe, 0x02,
0x1f, 0x8d, 0xaf, 0xc0, 0x15, 0xee, 0xdc, 0x90, 0xd7, 0x6b, 0xc7, 0xf9, 0x27, 0xe8, 0x99, 0x5b,
0x76, 0x9f, 0x99, 0xdf, 0xcc, 0xec, 0x3c, 0x8a, 0xa1, 0x3a, 0x08, 0x83, 0xcb, 0x88, 0x86, 0x37,
0x34, 0x34, 0x82, 0xd0, 0x67, 0x3e, 0xc2, 0xec, 0x46, 0x7f, 0x30, 0xf0, 0xfd, 0xc1, 0x90, 0x9a,
0x5c, 0xb9, 0x18, 0x5f, 0x9b, 0xcc, 0x1d, 0xd1, 0x88, 0x39, 0xa3, 0x20, 0x09, 0xd6, 0x0f, 0x16,
0x03, 0xae, 0xc6, 0xa1, 0xc3, 0x5c, 0xdf, 0x13, 0xfa, 0xde, 0xa2, 0x4e, 0x47, 0x01, 0x9b, 0x08,
0x71, 0x5f, 0x88, 0x4e, 0xe0, 0x9a, 0x8e, 0xe7, 0xf9, 0x8c, 0x67, 0x46, 0x89, 0x4a, 0x7e, 0x49,
0x50, 0xea, 0xdc, 0x50, 0x8f, 0xe1, 0x16, 0x14, 0xba, 0x96, 0x26, 0x35, 0xa4, 0x66, 0xd1, 0x2e,
0x74, 0x2d, 0xac, 0x41, 0xa9, 0xef, 0xb2, 0x21, 0xd5, 0x0a, 0x0d, 0xa9, 0xa9, 0xda, 0xc9, 0x01,
0x0d, 0x90, 0x2d, 0x87, 0x51, 0xad, 0xd8, 0x90, 0x9a, 0x9b, 0x6d, 0xdd, 0x48, 0xe0, 0x46, 0x5a,
0xd9, 0xe8, 0xa7, 0xad, 0xdb, 0x3c, 0x0e, 0x0f, 0xa1, 0x7c, 0xe2, 0x30, 0xea, 0x5d, 0x4e, 0x34,
0x99, 0xa7, 0xec, 0x2e, 0xa5, 0x58, 0x62, 0x18, 0x3b, 0x8d, 0x44, 0x04, 0xb9, 0xe7, 0x33, 0xaa,
0x95, 0x78, 0x65, 0xfe, 0x1b, 0xeb, 0xa0, 0x9c, 0x47, 0x34, 0xec, 0x5a, 0x9a, 0xc2, 0x5b, 0x14,
0x27, 0x7c, 0x06, 0xd0, 0xf3, 0x99, 0x7b, 0x3d, 0x89, 0x2b, 0x6b, 0xe5, 0x3f, 0xd5, 0xc8, 0x05,
0x93, 0x1f, 0x12, 0xa8, 0x2f, 0x42, 0xea, 0x30, 0x6a, 0xd3, 0xcf, 0xff, 0xc0, 0xbc, 0x7b, 0xd9,
0xb8, 0x51, 0xb0, 0xb8, 0x6e, 0x62, 0x81, 0x7a, 0x1e, 0x5c, 0x89, 0xb7, 0x58, 0xf4, 0xc2, 0x43,
0x61, 0x12, 0xfe, 0x36, 0x9b, 0xed, 0x6d, 0x23, 0xe7, 0x67, 0x2e, 0xd8, 0x89, 0x1e, 0x97, 0xb0,
0xe8, 0x90, 0xae, 0xa4, 0x90, 0x27, 0xb0, 0x71, 0xe2, 0x46, 0xcc, 0xa6, 0x51, 0x80, 0x8f, 0x40,
0xe1, 0x19, 0x91, 0x26, 0x35, 0x8a, 0xab, 0x91, 0x22, 0x80, 0xec, 0x03, 0xbc, 0xa4, 0xec, 0x78,
0xd2, 0xb5, 0x56, 0x41, 0x9f, 0xc2, 0x66, 0xa6, 0xde, 0x8d, 0x3b, 0x84, 0x0a, 0xcf, 0xb4, 0xc4,
0xd0, 0xe9, 0xaa, 0xa5, 0xbf, 0x5c, 0xf5, 0x63, 0x28, 0xd9, 0x8e, 0x37, 0x48, 0x0c, 0xb3, 0xd5,
0xae, 0xe7, 0x2b, 0xbd, 0x1b, 0xd3, 0x70, 0xc2, 0x55, 0x3b, 0x09, 0x22, 0x47, 0xf0, 0x7f, 0xae,
0xda, 0x9d, 0x3a, 0x6d, 0xb5, 0x00, 0x66, 0x40, 0x2c, 0x43, 0xd1, 0x7a, 0xfe, 0xa1, 0xfa, 0x1f,
0x6e, 0x80, 0xfc, 0xbe, 0xd3, 0x79, 0x53, 0x95, 0x50, 0x85, 0xd2, 0xe9, 0xdb, 0x5e, 0xff, 0x55,
0xb5, 0xd0, 0xfe, 0x59, 0x04, 0x39, 0x06, 0xe1, 0x6b, 0x50, 0x92, 0x6d, 0xe3, 0xfd, 0x3c, 0x39,
0x33, 0xbc, 0xbe, 0xea, 0x3a, 0x0a, 0x08, 0x7e, 0xf9, 0xf6, 0xfd, 0x6b, 0xa1, 0x42, 0xca, 0x26,
0xe5, 0xd5, 0x8f, 0xa4, 0x16, 0x9e, 0x81, 0x92, 0x98, 0x63, 0x9e, 0x95, 0x19, 0x46, 0xaf, 0x2f,
0xbd, 0x56, 0x27, 0xfe, 0x0b, 0x22, 0x3b, 0x1c, 0xb6, 0xad, 0x57, 0x04, 0xcc, 0xbc, 0xed, 0x5a,
0xd3, 0x98, 0x78, 0x0a, 0x4a, 0x62, 0x94, 0x79, 0x62, 0x66, 0x9e, 0xb5, 0xc4, 0x1a, 0x27, 0x6e,
0xb5, 0xe6, 0x88, 0xd8, 0x01, 0x39, 0xb6, 0x16, 0xae, 0xc9, 0xd2, 0x6b, 0xf9, 0x22, 0xa9, 0x09,
0xc9, 0x3d, 0xce, 0x52, 0x31, 0x1d, 0x15, 0xcf, 0xa0, 0x2c, 0xcc, 0x84, 0x73, 0xeb, 0x9c, 0xf9,
0x4f, 0xdf, 0x59, 0x79, 0x1f, 0x05, 0x69, 0x63, 0x38, 0xdf, 0xd8, 0x27, 0x50, 0xb3, 0xb5, 0xa3,
0xb6, 0x94, 0x2b, 0xbc, 0xa7, 0xef, 0xae, 0x51, 0xa2, 0x80, 0x1c, 0x70, 0xae, 0x46, 0xea, 0x19,
0x97, 0xdb, 0x60, 0x6a, 0xde, 0xc6, 0x21, 0xd3, 0xe3, 0xca, 0xc7, 0xdc, 0xb7, 0xe4, 0x42, 0xe1,
0x83, 0x1f, 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xc6, 0xe4, 0xd7, 0xf2, 0x72, 0x06, 0x00, 0x00,
var fileDescriptor_413a91106d7bcce8 = []byte{
// 619 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x95, 0xcd, 0x6e, 0xd3, 0x40,
0x10, 0xc7, 0xb1, 0xe3, 0x38, 0xf6, 0x34, 0x2d, 0xe9, 0x50, 0x8a, 0x71, 0xab, 0x12, 0x2d, 0x42,
0x8a, 0x72, 0xb0, 0x45, 0x8a, 0x90, 0xe0, 0x44, 0x8b, 0x23, 0x6a, 0x51, 0x8a, 0xb0, 0x5a, 0xf1,
0x71, 0x73, 0xdb, 0x6d, 0xb0, 0x94, 0xda, 0x8b, 0xbd, 0x41, 0x8a, 0xaa, 0x5e, 0x78, 0x05, 0xc4,
0x4b, 0x71, 0xe5, 0x15, 0xb8, 0xf2, 0x0e, 0xc8, 0xeb, 0xb5, 0xd3, 0x26, 0xa9, 0xc8, 0x99, 0x5b,
0xe6, 0xeb, 0x37, 0x33, 0x3b, 0x7f, 0xc5, 0xd0, 0x64, 0xa3, 0xe3, 0x61, 0x74, 0xe2, 0xb0, 0x34,
0xe1, 0x09, 0xea, 0x85, 0x65, 0x3f, 0x18, 0x24, 0xc9, 0x60, 0x48, 0x5d, 0xe1, 0x3d, 0x1e, 0x9d,
0xb9, 0x3c, 0x3a, 0xa7, 0x19, 0x0f, 0xcf, 0x59, 0x91, 0x68, 0x6f, 0x4d, 0x27, 0x9c, 0x8e, 0xd2,
0x90, 0x47, 0x49, 0x2c, 0xe3, 0x1b, 0xd3, 0x71, 0x7a, 0xce, 0xf8, 0x58, 0x06, 0x37, 0x65, 0x30,
0x64, 0x91, 0x1b, 0xc6, 0x71, 0xc2, 0x45, 0x65, 0x56, 0x44, 0xc9, 0x0f, 0x15, 0xea, 0xfd, 0xaf,
0x34, 0xe6, 0xb8, 0x02, 0xaa, 0xef, 0x59, 0x4a, 0x5b, 0xe9, 0xd4, 0x02, 0xd5, 0xf7, 0x70, 0x0d,
0xea, 0x87, 0x11, 0x1f, 0x52, 0x4b, 0x6d, 0x2b, 0x1d, 0x33, 0x28, 0x0c, 0x74, 0x40, 0xf3, 0x42,
0x4e, 0xad, 0x5a, 0x5b, 0xe9, 0x2c, 0xf5, 0x6c, 0xa7, 0x80, 0x3b, 0x65, 0x67, 0xe7, 0xb0, 0x1c,
0x3d, 0x10, 0x79, 0xb8, 0x0d, 0x8d, 0xfd, 0x90, 0xd3, 0xf8, 0x64, 0x6c, 0x69, 0xa2, 0xe4, 0xfe,
0x4c, 0x89, 0x27, 0x97, 0x09, 0xca, 0x4c, 0x44, 0xd0, 0x0e, 0x12, 0x4e, 0xad, 0xba, 0xe8, 0x2c,
0x7e, 0xe3, 0x3a, 0xe8, 0x47, 0x19, 0x4d, 0x7d, 0xcf, 0xd2, 0xc5, 0x88, 0xd2, 0xc2, 0x67, 0x00,
0x07, 0x09, 0x8f, 0xce, 0xc6, 0x79, 0x67, 0xab, 0xf1, 0xaf, 0x1e, 0x57, 0x92, 0xd1, 0x06, 0x23,
0xce, 0xad, 0x88, 0x9e, 0x5a, 0x46, 0x5b, 0xe9, 0x18, 0x41, 0x65, 0x93, 0x3f, 0x0a, 0x98, 0x2f,
0x53, 0x1a, 0x72, 0x1a, 0xd0, 0x2f, 0xff, 0xff, 0x5b, 0x90, 0x8d, 0x6a, 0xdd, 0x8c, 0x4d, 0x4b,
0x81, 0xbc, 0x00, 0xf3, 0x88, 0x9d, 0xca, 0xb7, 0x98, 0xd6, 0xc9, 0x43, 0x29, 0x20, 0xf1, 0x36,
0x4b, 0xbd, 0x65, 0x47, 0x6a, 0x5c, 0x38, 0x83, 0x22, 0x96, 0xe3, 0x3d, 0x3a, 0xa4, 0x73, 0x09,
0xe4, 0x31, 0x18, 0xfb, 0x51, 0xc6, 0x03, 0x9a, 0x31, 0x7c, 0x04, 0xba, 0xa8, 0xc8, 0x2c, 0xa5,
0x5d, 0x9b, 0xc5, 0xc9, 0x20, 0xd9, 0x04, 0x78, 0x45, 0xf9, 0xee, 0xd8, 0xf7, 0xe6, 0x01, 0x9f,
0xc0, 0x52, 0x15, 0x5d, 0x9c, 0xf9, 0x19, 0x9a, 0xa2, 0xca, 0x93, 0x8b, 0x96, 0xe7, 0x55, 0x16,
0x3c, 0x6f, 0x07, 0xea, 0x41, 0x18, 0x0f, 0x0a, 0x91, 0xac, 0xf4, 0xb0, 0xec, 0xf2, 0x6e, 0x44,
0xd3, 0xb1, 0x88, 0x04, 0x45, 0x02, 0x79, 0x0a, 0xcb, 0x57, 0x3a, 0x2d, 0x3c, 0x61, 0xb7, 0x0b,
0x30, 0x81, 0x61, 0x03, 0x6a, 0xde, 0xce, 0xc7, 0xd6, 0x2d, 0x34, 0x40, 0x7b, 0xdf, 0xef, 0xbf,
0x6e, 0x29, 0x68, 0x42, 0xfd, 0xcd, 0xdb, 0x83, 0xc3, 0xbd, 0x96, 0xda, 0xfb, 0x59, 0x03, 0x6d,
0x90, 0xb2, 0x13, 0xdc, 0x05, 0xbd, 0xb8, 0x2c, 0xae, 0x96, 0xd4, 0x4a, 0xd8, 0xf6, 0xb4, 0x2b,
0x63, 0x04, 0xbf, 0xfd, 0xfa, 0xfd, 0x5d, 0x6d, 0x92, 0x86, 0x4b, 0x45, 0xd7, 0xe7, 0x4a, 0x17,
0xf7, 0x41, 0x2f, 0x04, 0x30, 0x61, 0x54, 0x82, 0xb0, 0xd7, 0x67, 0x5e, 0xa6, 0x9f, 0xff, 0xfd,
0x90, 0x7b, 0x02, 0xb4, 0x6a, 0x37, 0x25, 0xc8, 0xbd, 0xf0, 0xbd, 0xcb, 0x9c, 0xe6, 0x83, 0x5e,
0x88, 0x61, 0x42, 0xab, 0xc4, 0x71, 0x23, 0x6d, 0x4d, 0xd0, 0x56, 0xba, 0xd7, 0x68, 0xb8, 0x03,
0x5a, 0x2e, 0x1d, 0xbc, 0xa1, 0xca, 0x6e, 0x95, 0x0d, 0x4a, 0x81, 0x91, 0xdb, 0x82, 0x63, 0x62,
0xb9, 0x1e, 0xee, 0x41, 0x43, 0x8a, 0x05, 0xab, 0x93, 0x4d, 0xb4, 0x65, 0xdf, 0x99, 0xf1, 0x65,
0xac, 0x1c, 0x06, 0xaf, 0x0f, 0xf3, 0x01, 0xcc, 0xea, 0xac, 0xb8, 0x76, 0xad, 0x4e, 0x6a, 0xca,
0xbe, 0x3b, 0xc7, 0x9b, 0x31, 0xb2, 0x25, 0x78, 0x16, 0x59, 0xaf, 0x78, 0xe2, 0xc4, 0x97, 0xee,
0x45, 0x9e, 0x72, 0xb9, 0x6b, 0x7c, 0x92, 0xdf, 0x8a, 0x63, 0x5d, 0x2c, 0xb8, 0xfd, 0x37, 0x00,
0x00, 0xff, 0xff, 0xef, 0xf0, 0x80, 0xc3, 0x4a, 0x06, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -638,7 +647,7 @@ func NewGrpcClient(cc *grpc.ClientConn) GrpcClient {
func (c *grpcClient) Create(ctx context.Context, in *CreateReq, opts ...grpc.CallOption) (*CreateRsp, error) {
out := new(CreateRsp)
err := c.cc.Invoke(ctx, "/grpcserver.grpc/Create", in, out, opts...)
err := c.cc.Invoke(ctx, "/public.grpc/Create", in, out, opts...)
if err != nil {
return nil, err
}
@ -647,7 +656,7 @@ func (c *grpcClient) Create(ctx context.Context, in *CreateReq, opts ...grpc.Cal
func (c *grpcClient) Update(ctx context.Context, in *UpdateReq, opts ...grpc.CallOption) (*empty.Empty, error) {
out := new(empty.Empty)
err := c.cc.Invoke(ctx, "/grpcserver.grpc/Update", in, out, opts...)
err := c.cc.Invoke(ctx, "/public.grpc/Update", in, out, opts...)
if err != nil {
return nil, err
}
@ -656,7 +665,7 @@ func (c *grpcClient) Update(ctx context.Context, in *UpdateReq, opts ...grpc.Cal
func (c *grpcClient) Delete(ctx context.Context, in *DeleteReq, opts ...grpc.CallOption) (*empty.Empty, error) {
out := new(empty.Empty)
err := c.cc.Invoke(ctx, "/grpcserver.grpc/Delete", in, out, opts...)
err := c.cc.Invoke(ctx, "/public.grpc/Delete", in, out, opts...)
if err != nil {
return nil, err
}
@ -665,7 +674,7 @@ func (c *grpcClient) Delete(ctx context.Context, in *DeleteReq, opts ...grpc.Cal
func (c *grpcClient) List(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*ListResp, error) {
out := new(ListResp)
err := c.cc.Invoke(ctx, "/grpcserver.grpc/List", in, out, opts...)
err := c.cc.Invoke(ctx, "/public.grpc/List", in, out, opts...)
if err != nil {
return nil, err
}
@ -674,7 +683,7 @@ func (c *grpcClient) List(ctx context.Context, in *empty.Empty, opts ...grpc.Cal
func (c *grpcClient) GetByID(ctx context.Context, in *GetByIDReq, opts ...grpc.CallOption) (*GetByIDResp, error) {
out := new(GetByIDResp)
err := c.cc.Invoke(ctx, "/grpcserver.grpc/GetByID", in, out, opts...)
err := c.cc.Invoke(ctx, "/public.grpc/GetByID", in, out, opts...)
if err != nil {
return nil, err
}
@ -683,7 +692,7 @@ func (c *grpcClient) GetByID(ctx context.Context, in *GetByIDReq, opts ...grpc.C
func (c *grpcClient) GetByDate(ctx context.Context, in *GetByDateReq, opts ...grpc.CallOption) (*GetByDateResp, error) {
out := new(GetByDateResp)
err := c.cc.Invoke(ctx, "/grpcserver.grpc/GetByDate", in, out, opts...)
err := c.cc.Invoke(ctx, "/public.grpc/GetByDate", in, out, opts...)
if err != nil {
return nil, err
}
@ -737,7 +746,7 @@ func _Grpc_Create_Handler(srv interface{}, ctx context.Context, dec func(interfa
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpcserver.grpc/Create",
FullMethod: "/public.grpc/Create",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).Create(ctx, req.(*CreateReq))
@ -755,7 +764,7 @@ func _Grpc_Update_Handler(srv interface{}, ctx context.Context, dec func(interfa
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpcserver.grpc/Update",
FullMethod: "/public.grpc/Update",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).Update(ctx, req.(*UpdateReq))
@ -773,7 +782,7 @@ func _Grpc_Delete_Handler(srv interface{}, ctx context.Context, dec func(interfa
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpcserver.grpc/Delete",
FullMethod: "/public.grpc/Delete",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).Delete(ctx, req.(*DeleteReq))
@ -791,7 +800,7 @@ func _Grpc_List_Handler(srv interface{}, ctx context.Context, dec func(interface
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpcserver.grpc/List",
FullMethod: "/public.grpc/List",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).List(ctx, req.(*empty.Empty))
@ -809,7 +818,7 @@ func _Grpc_GetByID_Handler(srv interface{}, ctx context.Context, dec func(interf
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpcserver.grpc/GetByID",
FullMethod: "/public.grpc/GetByID",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).GetByID(ctx, req.(*GetByIDReq))
@ -827,7 +836,7 @@ func _Grpc_GetByDate_Handler(srv interface{}, ctx context.Context, dec func(inte
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/grpcserver.grpc/GetByDate",
FullMethod: "/public.grpc/GetByDate",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GrpcServer).GetByDate(ctx, req.(*GetByDateReq))
@ -836,7 +845,7 @@ func _Grpc_GetByDate_Handler(srv interface{}, ctx context.Context, dec func(inte
}
var _Grpc_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpcserver.grpc",
ServiceName: "public.grpc",
HandlerType: (*GrpcServer)(nil),
Methods: []grpc.MethodDesc{
{
@ -865,5 +874,5 @@ var _Grpc_serviceDesc = grpc.ServiceDesc{
},
},
Streams: []grpc.StreamDesc{},
Metadata: "grpcserver.proto",
Metadata: "public.proto",
}

View File

@ -0,0 +1,85 @@
package rest
import (
"fmt"
"github.com/golang/protobuf/ptypes"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
)
func createReq2Event(e public.CreateReq) (res event.Event, err error) {
res = event.Event{
Title: e.Title,
Note: e.Note,
UserID: e.UserID,
}
res.Date, err = ptypes.Timestamp(e.Date)
if err != nil {
return event.Event{}, fmt.Errorf("can;t convert types: %w", err)
}
res.Latency, err = ptypes.Duration(e.Latency)
if err != nil {
return event.Event{}, fmt.Errorf("can;t convert types: %w", err)
}
res.NotifyTime, err = ptypes.Duration(e.NotifyTime)
if err != nil {
return event.Event{}, fmt.Errorf("can;t convert types: %w", err)
}
return res, nil
}
func pubEvent2Event(e public.Event) (res event.Event, err error) {
res = event.Event{
Title: e.Title,
Note: e.Note,
UserID: e.UserID,
}
res.Date, err = ptypes.Timestamp(e.Date)
if err != nil {
return event.Event{}, fmt.Errorf("can;t convert types: %w", err)
}
res.Latency, err = ptypes.Duration(e.Latency)
if err != nil {
return event.Event{}, fmt.Errorf("can;t convert types: %w", err)
}
res.NotifyTime, err = ptypes.Duration(e.NotifyTime)
if err != nil {
return event.Event{}, fmt.Errorf("can;t convert types: %w", err)
}
return res, nil
}
func event2pubEvent(e event.Event) (res public.Event, err error) {
res = public.Event{
Title: e.Title,
Latency: ptypes.DurationProto(e.Latency),
Note: e.Note,
UserID: e.UserID,
NotifyTime: ptypes.DurationProto(e.NotifyTime),
}
res.Date, err = ptypes.TimestampProto(e.Date)
if err != nil {
return public.Event{}, fmt.Errorf("can;t convert types: %w", err)
}
return res, nil
}
func events2pubEvents(e map[event.ID]event.Event) (res []public.Event, err error) {
for id, ev := range e {
r := public.Event{
ID: int64(id),
Title: ev.Title,
Latency: ptypes.DurationProto(ev.Latency),
Note: ev.Note,
UserID: ev.UserID,
NotifyTime: ptypes.DurationProto(ev.NotifyTime),
}
r.Date, err = ptypes.TimestampProto(ev.Date)
if err != nil {
return []public.Event{}, fmt.Errorf("can;t convert types: %w", err)
}
res = append(res, r)
}
return res, nil
}

View File

@ -0,0 +1,170 @@
package rest
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"time"
"github.com/gorilla/mux"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/calendar"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/logger"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
)
func FromRESTCreate(calendar *calendar.App) http.HandlerFunc {
return func(r http.ResponseWriter, req *http.Request) {
bodyReq := public.CreateReq{}
bodyIn, err := ioutil.ReadAll(req.Body)
defer req.Body.Close()
if err != nil {
err503("can't body of the HTTP API request", err, calendar.Logger, r)
}
err = json.Unmarshal(bodyIn, &bodyReq)
if err != nil {
err503("can't unmarshal data from HTTP API request", err, calendar.Logger, r)
}
evt, err := createReq2Event(bodyReq)
if err != nil {
err503("can't convert types", err, calendar.Logger, r)
}
id, err := calendar.Storage.Create(evt)
if err != nil {
err503("can't create event through HTTP API", err, calendar.Logger, r)
}
bodyOut, err := json.Marshal(&public.CreateRsp{ID: int64(id)})
if err != nil {
err503("can't marshal request", err, calendar.Logger, r)
}
r.WriteHeader(201)
_, err = r.Write(bodyOut)
if err != nil {
calendar.Logger.Errorf("can't send response")
}
}
}
func FromRESTUpdate(calendar *calendar.App) http.HandlerFunc {
return func(r http.ResponseWriter, req *http.Request) {
paramID, err := strconv.Atoi(mux.Vars(req)["ID"])
if err != nil {
err503("can't get request parameter", err, calendar.Logger, r)
}
bodyReq := public.UpdateReq{}
bodyIn, err := ioutil.ReadAll(req.Body)
defer req.Body.Close()
if err != nil {
err503("can't body of the HTTP API request", err, calendar.Logger, r)
}
err = json.Unmarshal(bodyIn, &bodyReq)
if err != nil {
err503("can't unmarshal data from HTTP API request", err, calendar.Logger, r)
}
evt, err := pubEvent2Event(*bodyReq.Event)
if err != nil {
err503("can't convert types", err, calendar.Logger, r)
}
err = calendar.Storage.Update(event.ID(paramID), evt)
if err != nil {
err503("can't update event through HTTP API", err, calendar.Logger, r)
}
r.WriteHeader(200)
}
}
func FromRESTDelete(calendar *calendar.App) http.HandlerFunc {
return func(r http.ResponseWriter, req *http.Request) {
paramID, err := strconv.Atoi(mux.Vars(req)["ID"])
if err != nil {
err503("can't get request parameter", err, calendar.Logger, r)
}
err = calendar.Storage.Delete(event.ID(paramID))
if err != nil {
err503("can't create event from HTTP API", err, calendar.Logger, r)
}
r.WriteHeader(200)
}
}
func FromRESTList(calendar *calendar.App) http.HandlerFunc {
return func(r http.ResponseWriter, req *http.Request) {
evs, err := calendar.Storage.List()
if err != nil {
err503("can't list events through HTTP API", err, calendar.Logger, r)
}
events, err := events2pubEvents(evs)
if err != nil {
err503("can't convert types", err, calendar.Logger, r)
}
bodyOut, err := json.Marshal(&events)
if err != nil {
err503("can't marshal request", err, calendar.Logger, r)
}
r.WriteHeader(200)
_, err = r.Write(bodyOut)
if err != nil {
calendar.Logger.Errorf("can't send response")
}
}
}
func FromRESTGetByID(calendar *calendar.App) http.HandlerFunc {
return func(r http.ResponseWriter, req *http.Request) {
paramID, err := strconv.Atoi(mux.Vars(req)["ID"])
if err != nil {
err503("can't get request parameter", err, calendar.Logger, r)
}
ev, ok := calendar.Storage.GetByID(event.ID(paramID))
if !ok {
err503("event not found", fmt.Errorf("no one"), calendar.Logger, r)
}
evnt, err := event2pubEvent(ev)
if err != nil {
err503("can't convert types", err, calendar.Logger, r)
}
bodyOut, err := json.Marshal(&evnt)
if err != nil {
err503("can't marshal request", err, calendar.Logger, r)
}
_, err = r.Write(bodyOut)
if err != nil {
calendar.Logger.Errorf("can't send response")
}
}
}
func FromRESTGetByDate(calendar *calendar.App) http.HandlerFunc {
return func(r http.ResponseWriter, req *http.Request) {
paramRange := mux.Vars(req)["Range"]
d, err := strconv.Atoi(mux.Vars(req)["Date"])
paramDate := time.Unix(int64(d), 0)
if err != nil {
err503("can't parse date from request parameter", err, calendar.Logger, r)
}
evs, err := calendar.Storage.GetByDate(paramDate, paramRange)
if err != nil {
err503("can't list events through HTTP API", err, calendar.Logger, r)
}
events, err := events2pubEvents(evs)
if err != nil {
err503("can't convert types", err, calendar.Logger, r)
}
bodyOut, err := json.Marshal(&events)
if err != nil {
err503("can't marshal request", err, calendar.Logger, r)
}
r.WriteHeader(200)
_, err = r.Write(bodyOut)
if err != nil {
calendar.Logger.Errorf("can't send response")
}
}
}
func err503(s string, err error, l logger.Interface, r http.ResponseWriter) {
l.Errorf("%s: %w", s, err.Error())
r.WriteHeader(503)
}

View File

@ -0,0 +1,81 @@
package config
import (
"fmt"
"io/ioutil"
"os"
"reflect"
"strconv"
"strings"
"github.com/BurntSushi/toml"
)
func New(configFile string, str interface{}) error {
if configFile == "" {
err := ApplyEnvVars(str, "APP")
if err != nil {
return fmt.Errorf("can't apply envvars to config :%w", err)
}
return nil
}
f, err := os.Open(configFile)
if err != nil {
return fmt.Errorf("can't open config file: %w", err)
}
defer f.Close()
s, err := ioutil.ReadAll(f)
if err != nil {
return fmt.Errorf("can't read content of the config file : %w", err)
}
_, err = toml.Decode(string(s), str)
if err != nil {
return fmt.Errorf("can't parce config file : %w", err)
}
return nil
}
// Пришлось немного модифицировать пакет github.com/mxschmitt/golang-env-struct. В исходной реализации используется поиск по тегам, а у моих структур тегов быть не должно. Пришлось переделать функцию, чтобы она искала по именам полей.
// Тест дополнен проверкой заполнения структуры из переменных окружения.
func ApplyEnvVars(c interface{}, prefix string) error {
return applyEnvVar(reflect.ValueOf(c), reflect.TypeOf(c), -1, prefix)
}
func applyEnvVar(v reflect.Value, t reflect.Type, counter int, prefix string) error {
if v.Kind() != reflect.Ptr {
return fmt.Errorf("not a pointer value")
}
f := reflect.StructField{}
if counter != -1 {
f = t.Field(counter)
}
v = reflect.Indirect(v)
fName := strings.ToUpper(f.Name)
env := os.Getenv(prefix + fName)
if env != "" {
switch v.Kind() {
case reflect.Int:
envI, err := strconv.Atoi(env)
if err != nil {
return fmt.Errorf("could not parse to int: %w", err)
}
v.SetInt(int64(envI))
case reflect.String:
v.SetString(env)
case reflect.Bool:
envB, err := strconv.ParseBool(env)
if err != nil {
return fmt.Errorf("could not parse bool: %w", err)
}
v.SetBool(envB)
}
}
if v.Kind() == reflect.Struct {
for i := 0; i < v.NumField(); i++ {
if err := applyEnvVar(v.Field(i).Addr(), v.Type(), i, prefix+fName+"_"); err != nil {
return fmt.Errorf("could not apply env var: %w", err)
}
}
}
return nil
}

View File

@ -0,0 +1,90 @@
package config
import (
"github.com/stretchr/testify/require"
"io/ioutil"
oslog "log"
"os"
"testing"
)
var _ = func() bool {
testing.Init()
return true
}()
func TestNewConfig(t *testing.T) {
badfile, err := ioutil.TempFile("", "conf.")
if err != nil {
oslog.Fatal(err)
}
defer os.Remove(badfile.Name())
badfile.WriteString(`aefSD
sadfg
RFABND FYGUMG
V`)
badfile.Sync()
goodfile, err := ioutil.TempFile("", "conf.")
if err != nil {
oslog.Fatal(err)
}
defer os.Remove(goodfile.Name())
goodfile.WriteString(`[storage]
inMemory = true
SQLHost = "localhost"`)
goodfile.Sync()
t.Run("No such file", func(t *testing.T) {
var c Calendar
e := New("adfergdth", &c)
require.Equal(t, Calendar{}, c)
require.Error(t, e)
})
t.Run("Bad file", func(t *testing.T) {
var c Calendar
e := New(badfile.Name(), &c)
require.Equal(t, Calendar{}, c)
require.Error(t, e)
})
t.Run("TOML reading", func(t *testing.T) {
var c Calendar
e := New(goodfile.Name(), &c)
require.Equal(t, true, c.Storage.InMemory)
require.Equal(t, "localhost", c.Storage.SQLHost)
require.NoError(t, e)
})
t.Run("ENV reading", func(t *testing.T) {
for k, v := range map[string]string{"APP_STRUCT1_VAR1": "val1", "APP_STRUCT1_VAR2": "val2", "APP_STRUCT2_VAR1": "val3", "APP_STRUCT2_VAR2": "val4", "APP_STRUCT3_VAR1": "val5", "APP_STRUCT3_VAR2": "val6"} {
require.NoError(t, os.Setenv(k, v))
}
var str struct {
Struct1 struct {
Var1 string
Var2 string
}
Struct2 struct {
Var1 string
Var2 string
}
Struct3 struct {
Var1 string
Var2 string
}
}
err := New("", &str)
require.NoError(t, err)
require.Equal(t, "val1", str.Struct1.Var1)
require.Equal(t, "val2", str.Struct1.Var2)
require.Equal(t, "val3", str.Struct2.Var1)
require.Equal(t, "val4", str.Struct2.Var2)
require.Equal(t, "val5", str.Struct3.Var1)
require.Equal(t, "val6", str.Struct3.Var2)
})
}

View File

@ -0,0 +1,50 @@
package config
type Calendar struct {
GRPC Server
HTTP Server
API Server
Logger Logger
Storage Storage
}
type Scheduler struct {
Rabbitmq Rabbit
Storage Storage
Logger Logger
}
type Sender struct {
Rabbitmq Rabbit
Logger Logger
}
type Server struct {
Address string
Port string
}
type Rabbit struct {
Login string
Pass string
Address string
Port string
Exchange string
Queue string
Key string
}
type Logger struct {
File string
Level string
MuteStdout bool
}
type Storage struct {
InMemory bool
SQLHost string
SQLPort string
SQLDbase string
SQLUser string
SQLPass string
}

View File

@ -7,7 +7,6 @@ import (
"strings"
amitralog "github.com/amitrai48/logger"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/config"
)
type Interface interface {
@ -22,27 +21,34 @@ type Logger struct {
Logger amitralog.Logger
}
type Config struct {
File string
Level string
MuteStdout bool
}
var validLevel = map[string]bool{"debug": true, "info": true, "warn": true, "error": true, "fatal": true}
func New(conf config.Config) (Interface, error) {
if conf.Logger.File == "" || !validLevel[strings.ToLower(conf.Logger.Level)] {
func New(conf Config) (Interface, error) {
if conf.File == "" || !validLevel[strings.ToLower(conf.Level)] {
return nil, errors.New("invalid logger config")
}
c := amitralog.Configuration{
EnableConsole: !conf.Logger.MuteStdout,
EnableConsole: !conf.MuteStdout,
ConsoleLevel: amitralog.Fatal,
ConsoleJSONFormat: false,
EnableFile: true,
FileLevel: strings.ToLower(conf.Logger.Level),
FileLevel: strings.ToLower(conf.Level),
FileJSONFormat: true,
FileLocation: conf.Logger.File,
FileLocation: conf.File,
}
if err := amitralog.NewLogger(c, amitralog.InstanceZapLogger); err != nil {
log.Fatalf("Could not instantiate log %s", err.Error())
}
l := amitralog.WithFields(amitralog.Fields{"hw": "12"})
l := amitralog.WithFields(amitralog.Fields{"hw": "15"})
l.Infof("logger start successful")
return l, nil
}

View File

@ -2,7 +2,6 @@ package logger
import (
"github.com/stretchr/testify/require"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/config"
"io/ioutil"
oslog "log"
"os"
@ -17,11 +16,7 @@ func TestLoggerLogic(t *testing.T) {
}
defer os.Remove(tmpfile.Name())
conf := config.Config{Logger: struct {
File string
Level string
MuteStdout bool
}{File: tmpfile.Name(), Level: "warn", MuteStdout: false}}
conf := Config{File: tmpfile.Name(), Level: "warn", MuteStdout: false}
log, err := New(conf)
if err != nil {
oslog.Fatal(err)
@ -42,21 +37,13 @@ func TestLoggerLogic(t *testing.T) {
func TestLoggerNegative(t *testing.T) {
t.Run("Bad file name", func(t *testing.T) {
conf := config.Config{Logger: struct {
File string
Level string
MuteStdout bool
}{File: "", Level: "debug", MuteStdout: true}}
conf := Config{File: "", Level: "debug", MuteStdout: true}
_, err := New(conf)
require.Error(t, err, "invalid logger config")
})
t.Run("Bad level", func(t *testing.T) {
conf := config.Config{Logger: struct {
File string
Level string
MuteStdout bool
}{File: "asdafad", Level: "wegretryjt", MuteStdout: true}}
conf := Config{File: "asdafad", Level: "wegretryjt", MuteStdout: true}
_, err := New(conf)
require.Error(t, err, "invalid logger config")
})

View File

@ -0,0 +1,56 @@
package rabbit
import (
"context"
"fmt"
"log"
"github.com/streadway/amqp"
)
type RMQConnection interface {
Channel() (*amqp.Channel, error)
}
type Message struct {
Ctx context.Context
Data []byte
}
func (r *Rabbit) Consume(ctx context.Context, queue string) (<-chan Message, error) {
messages := make(chan Message)
ch, err := r.Connection.Channel()
if err != nil {
return nil, fmt.Errorf("can't get channel from AMQP connection: %w", err)
}
deliveries, err := ch.Consume(queue, "", false, false, false, false, nil)
if err != nil {
return nil, fmt.Errorf("start consuming: %w", err)
}
go func() {
for {
select {
case <-ctx.Done():
return
case del := <-deliveries:
if err := del.Ack(false); err != nil {
log.Println(err)
}
msg := Message{
Ctx: context.TODO(),
Data: del.Body,
}
select {
case <-ctx.Done():
return
case messages <- msg:
}
}
}
}()
return messages, nil
}

View File

@ -0,0 +1,34 @@
package rabbit
import (
"fmt"
"log"
"github.com/streadway/amqp"
)
func (r *Rabbit) Publish(b []byte) error {
ch, err := r.Connection.Channel()
defer func() {
if err := ch.Close(); err != nil {
log.Println("can't close AMQP connection")
}
}()
if err != nil {
return fmt.Errorf("can't get channel from AMQP connection: %w", err)
}
err = ch.Publish(
r.Exchange, // exchange
r.Key, // routing key
true, // mandatory
false, // immediate
amqp.Publishing{
DeliveryMode: amqp.Persistent,
ContentType: "application/json; charset=utf-8",
Body: b,
})
if err != nil {
return fmt.Errorf("can't publish message into RabbitMQ: %w", err)
}
return nil
}

View File

@ -0,0 +1,86 @@
package rabbit
import (
"fmt"
"github.com/streadway/amqp"
)
type Rabbit struct {
Connection *amqp.Connection
Channel *amqp.Channel
Exchange string
Key string
Queue string
}
type Config struct {
Login string
Pass string
Address string
Port string
Exchange string
Queue string
Key string
}
func New(conf Config) (*Rabbit, error) {
conn, err := amqp.Dial("amqp://" + conf.Login + ":" + conf.Pass + "@" + conf.Address + ":" + conf.Port + "/")
if err != nil {
return nil, fmt.Errorf("can't dial RabbitMQ over AMQP: %w", err)
}
ch, err := conn.Channel()
if err != nil {
return nil, fmt.Errorf("can't get channel from AMQP connection: %w", err)
}
err = ch.ExchangeDeclare(
conf.Exchange, // name
"direct", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
if err != nil {
return nil, fmt.Errorf("can't (re)declare exchange in RabbitMQ: %w", err)
}
q, err := ch.QueueDeclare(
conf.Queue,
false,
false,
false,
false,
nil,
)
if err != nil {
return nil, fmt.Errorf("can't (re)create queue in RabbitMQ: %w", err)
}
err = ch.QueueBind(q.Name, conf.Key, conf.Exchange, false, nil)
if err != nil {
return nil, fmt.Errorf("can't bind Queue on Exchange in RabbitMQ: %w", err)
}
return &Rabbit{Connection: conn, Channel: ch, Exchange:conf.Exchange, Key: conf.Key, Queue: conf.Queue}, nil
}
func Attach(conf Config) (*Rabbit, error) {
conn, err := amqp.Dial("amqp://" + conf.Login + ":" + conf.Pass + "@" + conf.Address + ":" + conf.Port + "/")
if err != nil {
return nil, fmt.Errorf("can't dial RabbitMQ over AMQP: %w", err)
}
ch, err := conn.Channel()
if err != nil {
return nil, fmt.Errorf("can't get channel from AMQP connection: %w", err)
}
return &Rabbit{Connection: conn, Channel: ch, Exchange: conf.Exchange, Queue: conf.Queue, Key: conf.Key}, nil
}
func (r *Rabbit) Close() error {
if err := r.Channel.Close(); err != nil {
return fmt.Errorf("can't close connection channel: %w", err)
}
if err := r.Connection.Close(); err != nil {
return fmt.Errorf("can't close connection: %w", err)
}
return nil
}

View File

@ -13,4 +13,5 @@ type Event struct {
Note string
UserID int64
NotifyTime time.Duration
Notified bool
}

View File

@ -4,7 +4,7 @@ import (
"sync"
"time"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/event"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
)
type Storage struct {
@ -65,6 +65,40 @@ func (s *Storage) GetByDate(startDate time.Time, rng string) (map[event.ID]event
return res, nil
}
func (s *Storage) GetNotifications() (map[event.ID]event.Event, error) {
s.Mu.Lock()
defer s.Mu.Unlock()
res := make(map[event.ID]event.Event)
for k, v := range s.Events {
if time.Until(v.Date) <= v.NotifyTime && !v.Notified {
res[k] = v
}
}
return res, nil
}
func (s *Storage) SetNotified(id event.ID) error {
s.Mu.Lock()
tmp := s.Events[id]
tmp.Notified = true
s.Events[id] = tmp
s.Mu.Unlock()
return nil
}
func (s *Storage) PurgeOldEvents(days int64) (res int64, err error) {
s.Mu.Lock()
defer s.Mu.Unlock()
seconds := time.Duration(days * 24 * 60 * 60)
for k, v := range s.Events {
if v.Date.Before(time.Now().Add(-seconds)) {
delete(s.Events, k)
res++
}
}
return res, err
}
func getEndDate(startDate time.Time, rng string) time.Time {
switch rng {
case "DAY":

View File

@ -2,7 +2,7 @@ package memory
import (
"github.com/stretchr/testify/require"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/event"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
"testing"
"time"
)

View File

@ -0,0 +1,238 @@
package sql
import (
"database/sql"
"fmt"
"log"
"time"
// Postgresql driver.
_ "github.com/lib/pq"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
)
const dateTimeLayout = "2006-01-02T15:04:00Z"
type Config struct {
User string
Pass string
Host string
Port string
Dbase string
}
type Storage struct {
db *sql.DB
}
func New(config Config) *Storage {
db, err := sql.Open("postgres", "user="+config.User+" password="+config.Pass+" host="+config.Host+" port="+config.Port+" dbname="+config.Dbase+" sslmode=disable")
if err != nil {
log.Fatalf("can't connect to db: %s", err.Error())
}
return &Storage{db: db}
}
func (s *Storage) Close() error {
return s.db.Close()
}
func (s *Storage) Create(ev event.Event) (event.ID, error) {
lastInsertID := -1
if err := s.db.QueryRow(
`INSERT INTO events
(title, date, latency, note, userID, notifyTime,notified) VALUES
($1, $2, $3, $4, $5, $6, $7) RETURNING id`,
ev.Title,
ev.Date.Format(dateTimeLayout),
ev.Latency,
ev.Note,
ev.UserID,
ev.NotifyTime,
false,
).Scan(&lastInsertID); err != nil {
return -1, fmt.Errorf("can't create event in SQL DB: %w", err)
}
return event.ID(lastInsertID), nil
}
func (s *Storage) Update(id event.ID, event event.Event) error {
_, err := s.db.Exec(
`UPDATE events set
title=$1, date=$2, latency=$3, note=$4, userID=$5, notifyTime=$6
where id=$7`,
event.Title,
event.Date.Format(dateTimeLayout),
event.Latency,
event.Note,
event.UserID,
event.NotifyTime,
id,
)
if err != nil {
return fmt.Errorf("can't update event in SQL DB: %w", err)
}
return nil
}
func (s *Storage) Delete(id event.ID) error {
_, err := s.db.Exec(
`DELETE from events where id=$1`,
id,
)
if err != nil {
return fmt.Errorf("can't delete event from SQL DB: %w", err)
}
return nil
}
func (s *Storage) List() (map[event.ID]event.Event, error) {
res := make(map[event.ID]event.Event)
results, err := s.db.Query(`SELECT id,title,date,latency,note,userID,notifyTime,notified from events ORDER BY id`)
if err != nil {
return nil, fmt.Errorf("can't get list of events from SQL DB: %w", err)
}
defer results.Close()
for results.Next() {
var id event.ID
var evt event.Event
var dateRaw string
err = results.Scan(&id, &evt.Title, &dateRaw, &evt.Latency, &evt.Note, &evt.UserID, &evt.NotifyTime, &evt.Notified)
if err != nil {
return nil, fmt.Errorf("can't parce list of events getted from SQL DB: %w", err)
}
evt.Date, err = time.Parse(dateTimeLayout, dateRaw)
if err != nil {
return nil, fmt.Errorf("can't parce Date getted from SQL DB: %w", err)
}
res[id] = evt
}
if results.Err() != nil {
return nil, fmt.Errorf("something happens while we try to parce lines getted from SQL DB: %w", results.Err())
}
return res, nil
}
func (s *Storage) GetByID(id event.ID) (event.Event, bool) {
var res event.Event
var dateRaw string
err := s.db.QueryRow(
`SELECT title,date,latency,note,userID,notifyTime,notified from events where id=$1`, id).Scan(&res.Title, &dateRaw, &res.Latency, &res.Note, &res.UserID, &res.NotifyTime, &res.Notified)
if err != nil {
return res, false
}
dateParced, err := time.Parse(dateTimeLayout, dateRaw)
if err != nil {
return res, false
}
res.Date = dateParced
return res, true
}
func (s *Storage) GetByDate(startDate time.Time, rng string) (map[event.ID]event.Event, error) {
res := make(map[event.ID]event.Event)
endDate := getEndDate(startDate, rng)
results, err := s.db.Query(
`SELECT id,title,date,latency,note,userID,notifyTime,notified
from events
where (date>=$1 AND date<=$2)
ORDER BY id`,
startDate.Format(dateTimeLayout),
endDate.Format(dateTimeLayout))
if err != nil {
return nil, fmt.Errorf("can't get list of events from SQL DB: %w", err)
}
defer results.Close()
for results.Next() {
var id event.ID
var evt event.Event
var dateRaw string
err = results.Scan(&id, &evt.Title, &dateRaw, &evt.Latency, &evt.Note, &evt.UserID, &evt.NotifyTime, &evt.Notified)
if err != nil {
return nil, fmt.Errorf("can't parce list of events getted from SQL DB: %w", err)
}
evt.Date, err = time.Parse(dateTimeLayout, dateRaw)
if err != nil {
return nil, fmt.Errorf("can't parce Date getted from SQL DB: %w", err)
}
res[id] = evt
}
if results.Err() != nil {
return nil, fmt.Errorf("something happens while we try to parce lines getted from SQL DB: %w", results.Err())
}
return res, nil
}
func (s *Storage) GetNotifications() (map[event.ID]event.Event, error) {
res := make(map[event.ID]event.Event)
results, err := s.db.Query(
`SELECT id,title,date,latency,note,userID,notifyTime
from events
where (NOW()>date) AND (NOT notified)
ORDER BY id`)
if err != nil {
return nil, fmt.Errorf("can't get list of events from SQL DB: %w", err)
}
defer results.Close()
for results.Next() {
var id event.ID
var evt event.Event
var dateRaw string
err = results.Scan(&id, &evt.Title, &dateRaw, &evt.Latency, &evt.Note, &evt.UserID, &evt.NotifyTime)
if err != nil {
return nil, fmt.Errorf("can't parce list of events getted from SQL DB: %w", err)
}
evt.Date, err = time.Parse(dateTimeLayout, dateRaw)
if err != nil {
return nil, fmt.Errorf("can't parce Date getted from SQL DB: %w", err)
}
res[id] = evt
}
if results.Err() != nil {
return nil, fmt.Errorf("something happens while we try to parce lines getted from SQL DB: %w", results.Err())
}
return res, nil
}
func (s *Storage) SetNotified(id event.ID) error {
_, err := s.db.Exec(
`UPDATE events set
notified=true
where id=$1`,
id,
)
if err != nil {
return fmt.Errorf("can't set event as notified in SQL DB: %w", err)
}
return nil
}
func (s *Storage) PurgeOldEvents(days int64) (int64, error) {
r, err := s.db.Exec(
`DELETE from events where date<$1`,
time.Now().Add(-time.Duration(days*24*60*60)),
)
if err != nil {
return 0, fmt.Errorf("can't delete old events from SQL DB: %w", err)
}
l, err := r.RowsAffected()
if err != nil {
return 0, fmt.Errorf("can't run RowAffected on SQL DB query: %w", err)
}
return l, nil
}
func getEndDate(startDate time.Time, rng string) time.Time {
switch rng {
case "DAY":
return startDate.AddDate(0, 0, 1)
case "WEEK":
return startDate.AddDate(0, 0, 7)
case "MONTH":
return startDate.AddDate(0, 1, 0)
default:
return startDate
}
}

View File

@ -3,9 +3,9 @@ package store
import (
"time"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/event"
memorystorage "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/memory"
sqlstorage "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/internal/storage/sql"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/event"
memorystorage "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/memory"
sqlstorage "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/storage/sql"
)
type Config struct {
@ -24,6 +24,9 @@ type StorageInterface interface {
List() (map[event.ID]event.Event, error)
GetByID(event.ID) (event.Event, bool)
GetByDate(time.Time, string) (map[event.ID]event.Event, error)
GetNotifications() (map[event.ID]event.Event, error)
SetNotified(event.ID) error
PurgeOldEvents(int64) (int64, error)
}
func NewStore(conf Config) StorageInterface {

View File

@ -0,0 +1,6 @@
FROM golang:1.14
RUN mkdir -p /app
WORKDIR /app
COPY . .
RUN go get -d /app/test/.
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test ./test/...

View File

@ -0,0 +1,97 @@
package client
import (
"context"
"github.com/golang/protobuf/ptypes/empty"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"time"
)
type GRPCAPI struct {
Name string
Ctx context.Context
Host string
Port string
}
func (h GRPCAPI) GetName() string {
return h.Name
}
func (h GRPCAPI) Create(req *public.CreateReq) (*public.CreateRsp, error) {
ctx, cliGRPC, err := getCli(h)
if err != nil {
return nil, err
}
resp, err := cliGRPC.Create(ctx, req)
if err != nil {
return nil, err
}
return resp, nil
}
func (h GRPCAPI) Update(req *public.UpdateReq) error {
ctx, cliGRPC, err := getCli(h)
if err != nil {
return err
}
_, err = cliGRPC.Update(ctx, req)
if err != nil {
return err
}
return nil
}
func (h GRPCAPI) Delete(req *public.DeleteReq) error {
ctx, cliGRPC, err := getCli(h)
if err != nil {
return err
}
_, err = cliGRPC.Delete(ctx, req)
if err != nil {
return err
}
return nil
}
func (h GRPCAPI) GetByID(req *public.GetByIDReq) (*public.GetByIDResp, error) {
ctx, cliGRPC, err := getCli(h)
if err != nil {
return nil, err
}
resp, err := cliGRPC.GetByID(ctx, req)
if err != nil {
return nil, err
}
return resp, nil
}
func (h GRPCAPI) List() (*public.ListResp, error) {
ctx, cliGRPC, err := getCli(h)
if err != nil {
return nil, err
}
resp, err := cliGRPC.List(ctx, &empty.Empty{})
if err != nil {
return nil, err
}
return resp, nil
}
func (h GRPCAPI) GetByDate(req *public.GetByDateReq) (*public.GetByDateResp, error) {
ctx, cliGRPC, err := getCli(h)
if err != nil {
return nil, err
}
resp, err := cliGRPC.GetByDate(ctx, req)
if err != nil {
return nil, err
}
return resp, nil
}
func getCli(h GRPCAPI) (context.Context, public.GrpcClient, error) {
ctx, _ := context.WithTimeout(h.Ctx, 15*time.Second)
cliGRPC, err := public.NewClient(ctx, h.Host, h.Port)
return ctx, cliGRPC, err
}

View File

@ -0,0 +1,151 @@
package client
import (
"bytes"
"encoding/json"
"fmt"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"io/ioutil"
"net"
"net/http"
"strconv"
"time"
)
type HTTPAPI struct {
Name string
BaseURL string
}
func (h HTTPAPI) GetName() string {
return h.Name
}
func (h HTTPAPI) Create(req *public.CreateReq) (*public.CreateRsp, error) {
jreq, err := json.Marshal(req)
if err != nil {
return &public.CreateRsp{}, err
}
res, body, err := apiCall("POST", h.BaseURL+"/events", jreq)
if err != nil {
return &public.CreateRsp{}, err
}
if res.StatusCode != 201 {
return &public.CreateRsp{}, fmt.Errorf("unexpected status code %d", res.StatusCode)
}
var createRsp public.CreateRsp
err = json.Unmarshal(body, &createRsp)
if err != nil {
return &public.CreateRsp{}, err
}
return &createRsp, nil
}
func (h HTTPAPI) Update(req *public.UpdateReq) error {
jreq, err := json.Marshal(req)
if err != nil {
return err
}
res, _, err := apiCall("PUT", h.BaseURL+"/events/"+strconv.Itoa(int(req.ID)), jreq)
if err != nil {
return err
}
if res.StatusCode != 200 {
return fmt.Errorf("unexpected status code %d", res.StatusCode)
}
return nil
}
func (h HTTPAPI) Delete(req *public.DeleteReq) error {
jreq, err := json.Marshal(req)
if err != nil {
return err
}
res, _, err := apiCall("DELETE", h.BaseURL+"/events/"+strconv.Itoa(int(req.ID)), jreq)
if err != nil {
return err
}
if res.StatusCode != 200 {
return fmt.Errorf("unexpected status code %d", res.StatusCode)
}
return nil
}
func (h HTTPAPI) GetByID(req *public.GetByIDReq) (*public.GetByIDResp, error) {
jreq, err := json.Marshal(req)
if err != nil {
return nil, err
}
res, body, err := apiCall("GET", h.BaseURL+"/events/"+strconv.Itoa(int(req.ID)), jreq)
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("unexpected status code %d", res.StatusCode)
}
var ev public.Event
err = json.Unmarshal(body, &ev)
if err != nil {
return nil, err
}
return &public.GetByIDResp{Events: []*public.Event{&ev}}, nil
}
func (h HTTPAPI) List() (*public.ListResp, error) {
res, body, err := apiCall("GET", h.BaseURL+"/events", nil)
if err != nil {
return &public.ListResp{}, err
}
if res.StatusCode != 200 {
return &public.ListResp{}, fmt.Errorf("unexpected status code %d", res.StatusCode)
}
var listResp public.ListResp
err = json.Unmarshal(body, &listResp.Events)
if err != nil {
return &public.ListResp{}, err
}
return &listResp, nil
}
func (h HTTPAPI) GetByDate(req *public.GetByDateReq) (*public.GetByDateResp, error) {
jreq, err := json.Marshal(req)
if err != nil {
return &public.GetByDateResp{}, err
}
res, body, err := apiCall("GET", h.BaseURL+"/events/"+req.Range.String()+"/"+strconv.Itoa(int(req.Date.Seconds)), jreq)
if err != nil {
return &public.GetByDateResp{}, err
}
if res.StatusCode != 200 {
return &public.GetByDateResp{}, fmt.Errorf("unexpected status code %d", res.StatusCode)
}
var getByDateResp public.GetByDateResp
err = json.Unmarshal(body, &getByDateResp.Events)
if err != nil {
return &public.GetByDateResp{}, err
}
return &getByDateResp, nil
}
func apiCall(method string, url string, payload []byte) (*http.Response, []byte, error) {
client := &http.Client{Transport: &http.Transport{DialContext: (&net.Dialer{Timeout: 15 * time.Second}).DialContext}}
req, err := http.NewRequest(method, url, bytes.NewBuffer(payload))
if err != nil {
return nil, nil, err
}
req.Header.Add("Content-Type", "application/json")
req.Close = true
res, err := client.Do(req)
if err != nil {
return nil, nil, err
}
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, nil, err
}
if err = res.Body.Close(); err != nil {
return nil, nil, err
}
return res, body, nil
}

View File

@ -0,0 +1,13 @@
package client
import "github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
type Interface interface {
GetName() string
Create(req *public.CreateReq) (*public.CreateRsp, error)
Update(req *public.UpdateReq) error
Delete(req *public.DeleteReq) error
GetByID(req *public.GetByIDReq) (*public.GetByIDResp, error)
List() (*public.ListResp, error)
GetByDate(req *public.GetByDateReq) (*public.GetByDateResp, error)
}

View File

@ -0,0 +1,9 @@
package main
import (
"log"
)
func main() {
log.Fatalln("Nothing to do")
}

View File

@ -0,0 +1,187 @@
package main
import (
"context"
"log"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/test/client"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/test/misc"
)
func TestPublicAPIEndpoints(t *testing.T) {
cli := []client.Interface{
client.GRPCAPI{Ctx: context.Background(), Host: "localhost", Port: "50051", Name: "GRPC API"},
client.HTTPAPI{BaseURL: "http://localhost:50052", Name: "HTTP REST API"},
}
wg := sync.WaitGroup{}
wg.Add(len(cli) * 6)
for _, c := range cli {
t.Run("test "+c.GetName()+" for Create, GetById and Delete", func(t *testing.T) {
var ids []int64
defer func() {
wg.Done()
clean(c, &ids)
}()
resp1, err := c.Create(&misc.TestEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
ids = append(ids, resp1.ID)
resp2, err := c.GetByID(&public.GetByIDReq{ID: resp1.ID})
require.NoError(t, err)
require.Equal(t, 1, len(resp2.Events))
require.Equal(t, misc.TestEvent01.Title, resp2.Events[0].Title)
require.Equal(t, misc.TestEvent01.UserID, resp2.Events[0].UserID)
require.Equal(t, misc.TestEvent01.Note, resp2.Events[0].Note)
})
t.Run("test "+c.GetName()+" for Create, Update, GetById and Delete", func(t *testing.T) {
var ids []int64
defer func() {
wg.Done()
clean(c, &ids)
}()
resp1, err := c.Create(&misc.TestEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
ids = append(ids, resp1.ID)
err = c.Update(&public.UpdateReq{ID: resp1.ID, Event: &public.Event{ID: resp1.ID, Title: misc.TestEvent02.Title, Date: misc.TestEvent02.Date, Latency: misc.TestEvent02.Latency, Note: misc.TestEvent02.Note, UserID: misc.TestEvent02.UserID, NotifyTime: misc.TestEvent02.NotifyTime}})
require.NoError(t, err)
resp2, err := c.GetByID(&public.GetByIDReq{ID: resp1.ID})
require.NoError(t, err)
require.Equal(t, 1, len(resp2.Events))
require.Equal(t, misc.TestEvent02.Title, resp2.Events[0].Title)
require.Equal(t, misc.TestEvent02.UserID, resp2.Events[0].UserID)
require.Equal(t, misc.TestEvent02.Note, resp2.Events[0].Note)
})
t.Run("test "+c.GetName()+" for Create, Delete and GetById", func(t *testing.T) {
defer wg.Done()
resp1, err := c.Create(&misc.TestEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
err = c.Delete(&public.DeleteReq{ID: resp1.ID})
require.NoError(t, err)
resp2, err := c.GetByID(&public.GetByIDReq{ID: resp1.ID})
require.Error(t, err)
require.Nil(t, resp2)
})
t.Run("test "+c.GetName()+" for Create, List and Delete", func(t *testing.T) {
var ids []int64
defer func() {
wg.Done()
clean(c, &ids)
}()
resp1, err := c.Create(&misc.TestEvent01)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
ids = append(ids, resp1.ID)
resp2, err := c.Create(&misc.TestEvent02)
require.NoError(t, err)
require.Greater(t, resp2.ID, int64(0))
ids = append(ids, resp2.ID)
resp3, err := c.List()
require.NoError(t, err)
require.GreaterOrEqual(t, len(resp3.Events), 2)
var e1, e2 bool
for _, v := range resp3.Events {
if v.ID == resp1.ID {
e1 = true
}
if v.ID == resp2.ID {
e2 = true
}
}
require.True(t, e1)
require.True(t, e2)
err = c.Delete(&public.DeleteReq{ID: resp1.ID})
require.NoError(t, err)
err = c.Delete(&public.DeleteReq{ID: resp2.ID})
require.NoError(t, err)
})
t.Run("test "+c.GetName()+" for Create, GetByDate and Delete", func(t *testing.T) {
var ids []int64
defer func() {
wg.Done()
clean(c, &ids)
}()
startDate, err := time.Parse("2006-01-02T15:04:00", "3100-01-01T12:00:00")
require.NoError(t, err)
for i := time.Hour; i < (60 * 24 * time.Hour); i = i + 48*time.Hour {
resp1, err := c.Create(&public.CreateReq{
Title: "Test event 02",
Date: misc.Time2pbtimestamp(startDate.Add(i)),
Latency: misc.Dur2pbduration(2 * 24 * time.Hour),
Note: "Note of test event 02",
NotifyTime: misc.Dur2pbduration(5 * time.Minute),
UserID: 2222,
})
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
ids = append(ids, resp1.ID)
}
resp2, err := c.GetByDate(&public.GetByDateReq{Date: misc.Time2pbtimestamp(startDate), Range: public.QueryRange_DAY})
require.NoError(t, err)
require.Equal(t, 1, len(resp2.Events))
resp3, err := c.GetByDate(&public.GetByDateReq{Date: misc.Time2pbtimestamp(startDate), Range: public.QueryRange_WEEK})
require.NoError(t, err)
require.Equal(t, 4, len(resp3.Events))
resp4, err := c.GetByDate(&public.GetByDateReq{Date: misc.Time2pbtimestamp(startDate), Range: public.QueryRange_MONTH})
require.NoError(t, err)
require.Equal(t, 16, len(resp4.Events))
})
t.Run("test "+c.GetName()+" for send notification", func(t *testing.T) {
var ids []int64
defer func() {
wg.Done()
clean(c, &ids)
}()
resp1, err := c.Create(&misc.TestEvent03)
require.NoError(t, err)
require.Greater(t, resp1.ID, int64(0))
ids = append(ids, resp1.ID)
time.Sleep(5 * time.Second)
resp2, err := c.GetByID(&public.GetByIDReq{ID: resp1.ID})
require.NoError(t, err)
require.Equal(t, 1, len(resp2.Events))
require.True(t, resp2.Events[0].Notified)
})
}
}
func clean(c client.Interface, ids *[]int64) {
for _, m := range *ids {
err := c.Delete(&public.DeleteReq{ID: m})
if err != nil {
log.Println("error when try to clean DB: ", err.Error())
}
}
}

View File

@ -0,0 +1,49 @@
package misc
import (
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/duration"
"github.com/golang/protobuf/ptypes/timestamp"
"github.com/tiburon-777/HW_OTUS/hw12_13_14_15_calendar/pkg/api/public"
"log"
"time"
)
var TestEvent01 = public.CreateReq{
Title: "Test event 01",
Date: Time2pbtimestamp(time.Now().Add(30 * time.Second)),
Latency: Dur2pbduration(24 * time.Hour),
Note: "Note of test event 01",
NotifyTime: Dur2pbduration(5 * time.Minute),
UserID: 1111,
}
var TestEvent02 = public.CreateReq{
Title: "Test event 02",
Date: Time2pbtimestamp(time.Now().Add(60 * time.Second)),
Latency: Dur2pbduration(2 * 24 * time.Hour),
Note: "Note of test event 02",
NotifyTime: Dur2pbduration(5 * time.Minute),
UserID: 2222,
}
var TestEvent03 = public.CreateReq{
Title: "Test event 03",
Date: Time2pbtimestamp(time.Now().Add(-5 * time.Minute)),
Latency: Dur2pbduration(24 * time.Hour),
Note: "Note of test event 03",
NotifyTime: Dur2pbduration(1 * time.Second),
UserID: 1111,
}
func Time2pbtimestamp(t time.Time) *timestamp.Timestamp {
r, err := ptypes.TimestampProto(t)
if err != nil {
log.Fatalf("cant convert Time to Timestamp: %s", err.Error())
}
return r
}
func Dur2pbduration(t time.Duration) *duration.Duration {
return ptypes.DurationProto(t)
}