diff --git a/Taskfile.yml b/Taskfile.yml index 01f1fe1..c22a1b2 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -18,7 +18,7 @@ vars: PROTOC_GEN_GO: '{{.BIN_DIR}}/protoc-gen-go' PROTOC_GEN_GO_GRPC: '{{.BIN_DIR}}/protoc-gen-go-grpc' - MODULES: inventory payment shared + MODULES: inventory payment order shared tasks: install-formatters: @@ -138,7 +138,6 @@ tasks: proto:install-plugins: desc: "Устанавливает protoc плагины в каталог bin" - dir: shared/proto cmds: - | [ -f {{.PROTOC_GEN_GO}} ] || { @@ -153,7 +152,7 @@ tasks: proto:update-deps: deps: [ install-buf ] desc: Обновляет зависимости protobuf из удаленных репозиториев (googleapis и т.д.) - dir: shared/proto + dir: shared cmds: - | echo "🔄 Обновляем зависимости buf..." @@ -162,7 +161,7 @@ tasks: proto:lint: deps: [ proto:install-plugins, proto:update-deps ] desc: Проверка .proto-файлов на соответствие стилю - dir: shared/proto + dir: shared cmds: - | echo "🔍 Проверяем .proto файлы..." @@ -171,8 +170,17 @@ tasks: proto:gen: deps: [ install-buf, proto:install-plugins, proto:update-deps, proto:lint ] desc: Генерация Go-кода из .proto - dir: shared/proto + dir: shared cmds: - | echo "🏗️ Генерируем Go код из .proto..." {{.BUF}} generate + + ogen:gen: + desc: "Генерация shared/api" + dir: shared/api + cmds: + - | + echo "🏗️ Генерируем Go код из openapi..." + go generate ./... + diff --git a/go.work.sum b/go.work.sum index f881c84..8843367 100644 --- a/go.work.sum +++ b/go.work.sum @@ -118,6 +118,7 @@ cloud.google.com/go/webrisk v1.11.2/go.mod h1:yH44GeXz5iz4HFsIlGeoVvnjwnmfbni7Lw cloud.google.com/go/websecurityscanner v1.7.7/go.mod h1:ng/PzARaus3Bj4Os4LpUnyYHsbtJky1HbBDmz148v1o= cloud.google.com/go/workflows v1.14.3/go.mod h1:CC9+YdVI2Kvp0L58WajHpEfKJxhrtRh3uQ0SYWcmAk4= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= @@ -126,14 +127,23 @@ github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJP github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= google.golang.org/genproto/googleapis/api v0.0.0-20260120174246-409b4a993575/go.mod h1:dd646eSK+Dk9kxVBl1nChEOhJPtMXriCcVb4x3o6J+E= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/inventory/cmd/main.go b/inventory/cmd/main.go index dccded2..7a44657 100644 --- a/inventory/cmd/main.go +++ b/inventory/cmd/main.go @@ -68,10 +68,6 @@ func (s *inventoryService) ListParts(ctx context.Context, req *inventoryv1.ListP } } - if len(filteredParts) == 0 { - return nil, status.Errorf(codes.NotFound, "parts not found") - } - return &inventoryv1.ListPartsResponse{ Parts: filteredParts, }, nil @@ -84,12 +80,6 @@ func main() { return } - defer func() { - if cerr := lis.Close(); cerr != nil { - log.Printf("failed to close listener: %v\n", cerr) - } - }() - grpcServer := grpc.NewServer() reflection.Register(grpcServer) @@ -169,7 +159,7 @@ func createParts(count int) []*inventoryv1.Part { Uuid: gofakeit.UUID(), Name: gofakeit.Name(), Description: gofakeit.Sentence(10), - Price: int64(gofakeit.IntRange(1, 100000)), + PriceMinor: int64(gofakeit.IntRange(1, 100000)), StockQuantity: int64(gofakeit.IntRange(1, 100)), Category: randomCategory(), Dimensions: fakeDimensions(), diff --git a/inventory/go.mod b/inventory/go.mod index c80abd6..09995c4 100644 --- a/inventory/go.mod +++ b/inventory/go.mod @@ -12,8 +12,8 @@ require ( ) require ( - golang.org/x/net v0.47.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect ) diff --git a/inventory/go.sum b/inventory/go.sum index 8a01d65..2f79f3b 100644 --- a/inventory/go.sum +++ b/inventory/go.sum @@ -22,12 +22,12 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6 go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= diff --git a/order/cmd/main.go b/order/cmd/main.go new file mode 100644 index 0000000..fc0ff79 --- /dev/null +++ b/order/cmd/main.go @@ -0,0 +1,360 @@ +package main + +import ( + "context" + "errors" + "fmt" + "log" + "net" + "net/http" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + "github.com/google/uuid" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + orderv1 "github.com/qyrlabs/test-backend/shared/pkg/openapi/order/v1" + inventoryv1 "github.com/qyrlabs/test-backend/shared/pkg/proto/inventory/v1" + paymentv1 "github.com/qyrlabs/test-backend/shared/pkg/proto/payment/v1" +) + +const ( + httpPort = "8080" + inventoryServiceAddress = "localhost:50061" + paymentServiceAddress = "localhost:50062" + + // Timeouts for HTTP-Server + requestTimeout = 10 * time.Second + readHeaderTimeout = 5 * time.Second + shutdownTimeout = 10 * time.Second +) + +// Repo + +type OrderStorage struct { + mutex sync.RWMutex + orders map[string]*orderv1.Order +} + +func NewOrderStorage() *OrderStorage { + return &OrderStorage{ + orders: make(map[string]*orderv1.Order), + } +} + +func (s *OrderStorage) GetOrder(uuid string) *orderv1.Order { + s.mutex.RLock() + defer s.mutex.RUnlock() + + return s.orders[uuid] +} + +func (s *OrderStorage) UpdateOrder(order *orderv1.Order) { + s.mutex.Lock() + defer s.mutex.Unlock() + + order_uuid := order.GetOrderUUID().String() + s.orders[order_uuid] = order + log.Println(order) +} + +// Handler + +type OrderHandler struct { + storage *OrderStorage + inventoryClient inventoryv1.InventoryServiceClient + paymentClient paymentv1.PaymentServiceClient +} + +func NewOrderHandler(storage *OrderStorage, inventoryClient inventoryv1.InventoryServiceClient, paymentClient paymentv1.PaymentServiceClient) *OrderHandler { + return &OrderHandler{ + storage: storage, + inventoryClient: inventoryClient, + paymentClient: paymentClient, + } +} + +// CancelOrder implements cancelOrder operation. +// +// Cancels an existing order. +// +// POST /api/v1/orders/{order_uuid}/cancel +func (h *OrderHandler) CancelOrder(ctx context.Context, params orderv1.CancelOrderParams) (orderv1.CancelOrderRes, error) { + order := h.storage.GetOrder(params.OrderUUID.String()) + + if order == nil { + return &orderv1.NotFoundError{ + Code: http.StatusNotFound, + Message: "order not found", + }, nil + } + + if order.GetStatus() == orderv1.OrderStatusSTATUSCANCELLED { + return &orderv1.ConflictError{ + Code: http.StatusConflict, + Message: "order already cancelled", + }, nil + } + + if order.GetStatus() == orderv1.OrderStatusSTATUSPAID { + return &orderv1.ConflictError{ + Code: http.StatusConflict, + Message: "order already paid and cannot be cancelled", + }, nil + } + + order.SetStatus(orderv1.OrderStatusSTATUSCANCELLED) + + h.storage.UpdateOrder(order) + + return order, nil +} + +// CreateOrder implements createOrder operation. +// +// Creates a new order. +// +// POST /api/v1/orders +func (h *OrderHandler) CreateOrder(ctx context.Context, req *orderv1.OrderCreateRequest) (orderv1.CreateOrderRes, error) { + partUuids := make([]string, 0, len(req.GetPartUuids())) + for _, uuid := range req.GetPartUuids() { + partUuids = append(partUuids, uuid.String()) + } + + filteredParts, err := h.inventoryClient.ListParts(ctx, &inventoryv1.ListPartsRequest{ + Filter: &inventoryv1.PartsFilter{ + Uuids: partUuids, + }, + }) + if err != nil { + return &orderv1.BadGatewayError{ + Code: http.StatusBadGateway, + Message: fmt.Sprintf("failed to get filtered parts: %v", err), + }, nil + } + + if len(filteredParts.GetParts()) != len(partUuids) { + return &orderv1.ValidationError{ + Code: http.StatusUnprocessableEntity, + Message: "missing specified part uuids", + }, nil + } + + var totalPrice int64 = 0 + for _, part := range filteredParts.GetParts() { + totalPrice += part.GetPriceMinor() + } + + order := &orderv1.Order{ + OrderUUID: uuid.New(), + UserUUID: uuid.UUID(req.GetUserUUID()), + PartUuids: req.GetPartUuids(), + TotalPriceMinor: totalPrice, + Status: orderv1.OrderStatusSTATUSPENDINGPAYMENT, + } + + h.storage.UpdateOrder(order) + + return &orderv1.OrderCreateResponse{ + OrderUUID: orderv1.OrderUUID(order.GetOrderUUID()), + TotalPriceMinor: orderv1.TotalPriceMinor(order.GetTotalPriceMinor()), + }, nil +} + +// GetOrderByUuid implements getOrderByUuid operation. +// +// Retrieves order details by UUID. +// +// GET /api/v1/orders/{order_uuid} +func (h *OrderHandler) GetOrderByUuid(ctx context.Context, params orderv1.GetOrderByUuidParams) (orderv1.GetOrderByUuidRes, error) { + order := h.storage.GetOrder(params.OrderUUID.String()) + + if order == nil { + return &orderv1.NotFoundError{ + Code: http.StatusNotFound, + Message: "order not found", + }, nil + } + + return order, nil +} + +// PayOrder implements payOrder operation. +// +// Processes payment for an existing order. +// +// POST /api/v1/orders/{order_uuid}/pay +func (h *OrderHandler) PayOrder(ctx context.Context, req *orderv1.OrderPayRequest, params orderv1.PayOrderParams) (orderv1.PayOrderRes, error) { + order := h.storage.GetOrder(params.OrderUUID.String()) + + if order == nil { + return &orderv1.NotFoundError{ + Code: http.StatusNotFound, + Message: "order not found", + }, nil + } + + if order.GetStatus() == orderv1.OrderStatusSTATUSPAID { + return &orderv1.ConflictError{ + Code: http.StatusConflict, + Message: "order already paid", + }, nil + } + + if order.GetStatus() == orderv1.OrderStatusSTATUSCANCELLED { + return &orderv1.ConflictError{ + Code: http.StatusConflict, + Message: "order cancelled", + }, nil + } + + paymentMethod, ok := paymentv1.PaymentMethod_value[string(req.GetPaymentMethod())] + if !ok { + return &orderv1.ValidationError{ + Code: http.StatusUnprocessableEntity, + Message: "invalid payment method", + }, nil + } + + payOrderResponse, err := h.paymentClient.PayOrder(ctx, &paymentv1.PayOrderRequest{ + OrderUuid: order.GetOrderUUID().String(), + UserUuid: order.GetUserUUID().String(), + PaymentMethod: paymentv1.PaymentMethod(paymentMethod), + }) + if err != nil { + return &orderv1.BadGatewayError{ + Code: http.StatusBadGateway, + Message: fmt.Sprintf("failed to pay order: %v", err), + }, nil + } + + transactionUuid := uuid.MustParse(payOrderResponse.GetTransactionUuid()) + + order.SetStatus(orderv1.OrderStatusSTATUSPAID) + order.SetTransactionUUID(orderv1.NewOptUUID(transactionUuid)) + + h.storage.UpdateOrder(order) + + return &orderv1.OrderPayResponse{ + TransactionUUID: transactionUuid, + }, nil +} + +// NewError creates *GenericErrorStatusCode from error returned by handler. +// +// Used for common default response. +func (h *OrderHandler) NewError(ctx context.Context, err error) *orderv1.GenericErrorStatusCode { + return &orderv1.GenericErrorStatusCode{ + StatusCode: http.StatusInternalServerError, + Response: orderv1.GenericError{ + Code: orderv1.NewOptInt(http.StatusInternalServerError), + Message: orderv1.NewOptString(err.Error()), + }, + } +} + +func initApplication() (*grpc.ClientConn, *grpc.ClientConn, *orderv1.Server, error) { + inventoryConn, err := grpc.NewClient( + inventoryServiceAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + return nil, nil, nil, fmt.Errorf("failed to create inventory service grpc connection: %w", err) + } + + paymentConn, err := grpc.NewClient( + paymentServiceAddress, + grpc.WithTransportCredentials(insecure.NewCredentials()), + ) + if err != nil { + // Cleanup: закрываем уже открытое inventoryServiceConn соединение при ошибке + if cerr := inventoryConn.Close(); cerr != nil { + log.Printf("failed to close inventory service grpc connection: %v", cerr) + } + return nil, nil, nil, fmt.Errorf("failed to create payment service grpc connection: %w", err) + } + + inventoryClient := inventoryv1.NewInventoryServiceClient(inventoryConn) + paymentClient := paymentv1.NewPaymentServiceClient(paymentConn) + + storage := NewOrderStorage() + orderHandler := NewOrderHandler(storage, inventoryClient, paymentClient) + + orderServer, err := orderv1.NewServer(orderHandler) + if err != nil { + // Cleanup: закрываем уже открытое inventoryServiceConn соединение при ошибке + if cerr := inventoryConn.Close(); cerr != nil { + log.Printf("failed to close inventory service grpc connection: %v", cerr) + } + if cerr := paymentConn.Close(); cerr != nil { + log.Printf("failed to close payment service grpc connection: %v", cerr) + } + return nil, nil, nil, fmt.Errorf("failed to create order server: %w", err) + } + + return inventoryConn, paymentConn, orderServer, nil +} + +func main() { + inventoryConn, paymentConn, orderServer, err := initApplication() + if err != nil { + log.Fatalf("failed to init application: %v", err) + } + + defer func() { + if cerr := inventoryConn.Close(); cerr != nil { + log.Printf("failed to close inventory service grpc connection: %v", cerr) + } + }() + + defer func() { + if cerr := paymentConn.Close(); cerr != nil { + log.Printf("failed to close payment service grpc connection: %v", cerr) + } + }() + + router := chi.NewRouter() + + router.Use(middleware.Logger) + router.Use(middleware.Recoverer) + router.Use(middleware.Timeout(requestTimeout)) + + router.Mount("/", orderServer) + + server := &http.Server{ + Addr: net.JoinHostPort("localhost", httpPort), + Handler: router, + ReadHeaderTimeout: readHeaderTimeout, + } + + go func() { + log.Printf("http server listening on %s\n", server.Addr) + err := server.ListenAndServe() + if err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Printf("failed to start http server: %v", err) + } + }() + + // Graceful Shutdown + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + + log.Println("Shutting down http server...") + + ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout) + defer cancel() + + err = server.Shutdown(ctx) + if err != nil { + log.Printf("failed to shutdown http server: %v", err) + } + + log.Println("http server stopped") +} diff --git a/order/go.mod b/order/go.mod index 3d8ec22..3c901f7 100644 --- a/order/go.mod +++ b/order/go.mod @@ -1,3 +1,42 @@ module github.com/qyrlabs/test-backend/order go 1.25.6 + +replace github.com/qyrlabs/test-backend/shared => ../shared + +require ( + github.com/go-chi/chi/v5 v5.2.4 + github.com/google/uuid v1.6.0 + github.com/qyrlabs/test-backend/shared v0.0.0-00010101000000-000000000000 + google.golang.org/grpc v1.78.0 +) + +require ( + github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-faster/errors v0.7.1 // indirect + github.com/go-faster/jx v1.2.0 // indirect + github.com/go-faster/yaml v0.4.6 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/ogen-go/ogen v1.18.0 // indirect + github.com/segmentio/asm v1.2.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/order/go.sum b/order/go.sum new file mode 100644 index 0000000..d82f413 --- /dev/null +++ b/order/go.sum @@ -0,0 +1,91 @@ +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/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= +github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= +github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= +github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= +github.com/go-faster/jx v1.2.0 h1:T2YHJPrFaYu21fJtUxC9GzmluKu8rVIFDwwGBKTDseI= +github.com/go-faster/jx v1.2.0/go.mod h1:UWLOVDmMG597a5tBFPLIWJdUxz5/2emOpfsj9Neg0PE= +github.com/go-faster/yaml v0.4.6 h1:lOK/EhI04gCpPgPhgt0bChS6bvw7G3WwI8xxVe0sw9I= +github.com/go-faster/yaml v0.4.6/go.mod h1:390dRIvV4zbnO7qC9FGo6YYutc+wyyUSHBgbXL52eXk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/ogen-go/ogen v1.18.0 h1:6RQ7lFBjOeNaUWu4getfqIh4GJbEY4hqKuzDtec/g60= +github.com/ogen-go/ogen v1.18.0/go.mod h1:dHFr2Wf6cA7tSxMI+zPC21UR5hAlDw8ZYUkK3PziURY= +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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY= +golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/payment/cmd/main.go b/payment/cmd/main.go index fa37243..264d7ba 100644 --- a/payment/cmd/main.go +++ b/payment/cmd/main.go @@ -50,12 +50,6 @@ func main() { return } - defer func() { - if cerr := lis.Close(); cerr != nil { - log.Printf("failed to close listener: %v\n", cerr) - } - }() - grpcServer := grpc.NewServer() reflection.Register(grpcServer) diff --git a/payment/go.mod b/payment/go.mod index 8253de3..3ebcc4d 100644 --- a/payment/go.mod +++ b/payment/go.mod @@ -11,9 +11,9 @@ require ( ) require ( - golang.org/x/net v0.47.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/payment/go.sum b/payment/go.sum index ae87d8a..b362ca9 100644 --- a/payment/go.sum +++ b/payment/go.sum @@ -20,12 +20,12 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6 go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= diff --git a/shared/api/order/v1/components/enums/order_status.yaml b/shared/api/order/v1/components/enums/order_status.yaml new file mode 100644 index 0000000..24e34e4 --- /dev/null +++ b/shared/api/order/v1/components/enums/order_status.yaml @@ -0,0 +1,7 @@ +type: string +description: Статус заказа +enum: + - STATUS_PENDING_PAYMENT + - STATUS_PAID + - STATUS_CANCELLED +example: STATUS_PAID diff --git a/shared/api/order/v1/components/enums/payment_method.yaml b/shared/api/order/v1/components/enums/payment_method.yaml new file mode 100644 index 0000000..737ab42 --- /dev/null +++ b/shared/api/order/v1/components/enums/payment_method.yaml @@ -0,0 +1,9 @@ + type: string + description: Способ оплаты + enum: + - PAYMENT_METHOD_UNSPECIFIED + - PAYMENT_METHOD_CARD + - PAYMENT_METHOD_SBP + - PAYMENT_METHOD_CREDIT_CARD + - PAYMENT_METHOD_INVESTOR_MONEY + example: PAYMENT_METHOD_CREDIT_CARD \ No newline at end of file diff --git a/shared/api/order/v1/components/errors/bad_gateway_error.yaml b/shared/api/order/v1/components/errors/bad_gateway_error.yaml new file mode 100644 index 0000000..84bb45d --- /dev/null +++ b/shared/api/order/v1/components/errors/bad_gateway_error.yaml @@ -0,0 +1,13 @@ +type: object +required: + - code + - message +properties: + code: + type: integer + description: HTTP-код ошибки + example: 502 + message: + type: string + description: Описание ошибки + example: "Bad Gateway: Upstream service error" diff --git a/shared/api/order/v1/components/errors/conflict_error.yaml b/shared/api/order/v1/components/errors/conflict_error.yaml new file mode 100644 index 0000000..beb0a4c --- /dev/null +++ b/shared/api/order/v1/components/errors/conflict_error.yaml @@ -0,0 +1,13 @@ +type: object +required: + - code + - message +properties: + code: + type: integer + description: HTTP-код ошибки + example: 409 + message: + type: string + description: Описание ошибки + example: "Conflict: Resource conflict detected" diff --git a/shared/api/order/v1/components/errors/generic_error.yaml b/shared/api/order/v1/components/errors/generic_error.yaml new file mode 100644 index 0000000..0829744 --- /dev/null +++ b/shared/api/order/v1/components/errors/generic_error.yaml @@ -0,0 +1,8 @@ +type: object +properties: + code: + type: integer + description: HTTP-код ошибки + message: + type: string + description: Описание ошибки diff --git a/shared/api/order/v1/components/errors/not_found_error.yaml b/shared/api/order/v1/components/errors/not_found_error.yaml new file mode 100644 index 0000000..aee26a9 --- /dev/null +++ b/shared/api/order/v1/components/errors/not_found_error.yaml @@ -0,0 +1,13 @@ +type: object +required: + - code + - message +properties: + code: + type: integer + description: HTTP-код ошибки + example: 404 + message: + type: string + description: Описание ошибки + example: "Order with UUID 'cae5e039-0224-4f36-86c2-224385d6f9e6' not found" diff --git a/shared/api/order/v1/components/errors/validation_error.yaml b/shared/api/order/v1/components/errors/validation_error.yaml new file mode 100644 index 0000000..e91ff57 --- /dev/null +++ b/shared/api/order/v1/components/errors/validation_error.yaml @@ -0,0 +1,13 @@ +type: object +required: + - code + - message +properties: + code: + type: integer + description: HTTP-код ошибки + example: 422 + message: + type: string + description: Описание ошибки + example: "Input validation failed" diff --git a/shared/api/order/v1/components/order.yaml b/shared/api/order/v1/components/order.yaml new file mode 100644 index 0000000..8caaffd --- /dev/null +++ b/shared/api/order/v1/components/order.yaml @@ -0,0 +1,51 @@ +type: object + +required: + - order_uuid + - user_uuid + - part_uuids + - total_price_minor + - status + +properties: + + order_uuid: + type: string + format: uuid + description: UUID заказа + example: cae5e039-0224-4f36-86c2-224385d6f9e6 + + user_uuid: + type: string + format: uuid + description: UUID пользователя + example: cae5e039-0224-4f36-86c2-224385d6f9e6 + + part_uuids: + type: array + description: Список UUID деталей + minItems: 1 + items: + type: string + format: uuid + example: cae5e039-0224-4f36-86c2-224385d6f9e6 + + total_price_minor: + type: integer + format: int64 + description: Сумма заказа в копейках + example: 12350 + + transaction_uuid: + type: string + format: uuid + description: UUID транзакции + example: cae5e039-0224-4f36-86c2-224385d6f9e6 + + payment_method: + allOf: + - $ref: ./enums/payment_method.yaml + + status: + allOf: + - $ref: ./enums/order_status.yaml \ No newline at end of file diff --git a/shared/api/order/v1/components/requests/order_create_request.yaml b/shared/api/order/v1/components/requests/order_create_request.yaml new file mode 100644 index 0000000..b52cf13 --- /dev/null +++ b/shared/api/order/v1/components/requests/order_create_request.yaml @@ -0,0 +1,11 @@ +type: object +required: + - user_uuid + - part_uuids +properties: + user_uuid: + allOf: + - $ref: '../order.yaml#/properties/user_uuid' + part_uuids: + allOf: + - $ref: '../order.yaml#/properties/part_uuids' diff --git a/shared/api/order/v1/components/requests/order_pay_request.yaml b/shared/api/order/v1/components/requests/order_pay_request.yaml new file mode 100644 index 0000000..ad12694 --- /dev/null +++ b/shared/api/order/v1/components/requests/order_pay_request.yaml @@ -0,0 +1,6 @@ +type: object +required: + - payment_method +properties: + payment_method: + $ref: '../enums/payment_method.yaml' diff --git a/shared/api/order/v1/components/responses/order_create_response.yaml b/shared/api/order/v1/components/responses/order_create_response.yaml new file mode 100644 index 0000000..3c1348c --- /dev/null +++ b/shared/api/order/v1/components/responses/order_create_response.yaml @@ -0,0 +1,11 @@ +type: object +required: + - order_uuid + - total_price_minor +properties: + order_uuid: + allOf: + - $ref: '../order.yaml#/properties/order_uuid' + total_price_minor: + allOf: + - $ref: '../order.yaml#/properties/total_price_minor' diff --git a/shared/api/order/v1/components/responses/order_pay_response.yaml b/shared/api/order/v1/components/responses/order_pay_response.yaml new file mode 100644 index 0000000..2025f30 --- /dev/null +++ b/shared/api/order/v1/components/responses/order_pay_response.yaml @@ -0,0 +1,8 @@ +type: object +required: + - transaction_uuid +properties: + transaction_uuid: + type: string + format: uuid + description: UUID транзакции diff --git a/shared/api/order/v1/components/responses/order_response.yaml b/shared/api/order/v1/components/responses/order_response.yaml new file mode 100644 index 0000000..51020e9 --- /dev/null +++ b/shared/api/order/v1/components/responses/order_response.yaml @@ -0,0 +1 @@ +$ref: '../order.yaml' diff --git a/shared/api/order/v1/generate.go b/shared/api/order/v1/generate.go new file mode 100644 index 0000000..8789431 --- /dev/null +++ b/shared/api/order/v1/generate.go @@ -0,0 +1,3 @@ +package weather + +//go:generate go tool ogen --config ../../../ogen-config.yaml --target ../../../pkg/openapi/order/v1 --package orderv1 --clean order.v1.openapi.yaml diff --git a/shared/api/order/v1/order.v1.openapi.yaml b/shared/api/order/v1/order.v1.openapi.yaml new file mode 100644 index 0000000..e8bd3d2 --- /dev/null +++ b/shared/api/order/v1/order.v1.openapi.yaml @@ -0,0 +1,28 @@ +openapi: 3.0.3 +info: + title: OrderService API + version: 1.0.0 + description: | + RESTful API for order management in a microservices architecture. + + This service handles: + - Order creation + - Order retrieval + - Order payment processing + - Order cancellation + + ## Error Handling + The API uses standard HTTP status codes and returns structured error responses. +tags: + - name: Orders + description: Order management operations. + +paths: + /api/v1/orders: + $ref: ./paths/orders.yaml + /api/v1/orders/{order_uuid}: + $ref: ./paths/orders_uuid.yaml + /api/v1/orders/{order_uuid}/pay: + $ref: ./paths/orders_uuid_pay.yaml + /api/v1/orders/{order_uuid}/cancel: + $ref: ./paths/orders_uuid_cancel.yaml diff --git a/shared/api/order/v1/params/order_uuid.yaml b/shared/api/order/v1/params/order_uuid.yaml new file mode 100644 index 0000000..9b1449e --- /dev/null +++ b/shared/api/order/v1/params/order_uuid.yaml @@ -0,0 +1,8 @@ +name: order_uuid +in: path +required: true +description: Уникальный идентификатор заказа +schema: + type: string + format: uuid + example: cae5e039-0224-4f36-86c2-224385d6f9e6 diff --git a/shared/api/order/v1/paths/orders.yaml b/shared/api/order/v1/paths/orders.yaml new file mode 100644 index 0000000..1e1af2b --- /dev/null +++ b/shared/api/order/v1/paths/orders.yaml @@ -0,0 +1,37 @@ +post: + summary: Create a new order + description: Creates a new order + operationId: createOrder + tags: + - Orders + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/requests/order_create_request.yaml' + responses: + '201': + description: Order created successfully + content: + application/json: + schema: + $ref: '../components/responses/order_create_response.yaml' + '422': + description: Validation error + content: + application/json: + schema: + $ref: '../components/errors/validation_error.yaml' + '502': + description: Bad gateway + content: + application/json: + schema: + $ref: '../components/errors/bad_gateway_error.yaml' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '../components/errors/generic_error.yaml' diff --git a/shared/api/order/v1/paths/orders_uuid.yaml b/shared/api/order/v1/paths/orders_uuid.yaml new file mode 100644 index 0000000..be0dee7 --- /dev/null +++ b/shared/api/order/v1/paths/orders_uuid.yaml @@ -0,0 +1,39 @@ +get: + summary: Get order by UUID + description: Retrieves order details by UUID + operationId: getOrderByUuid + tags: + - Orders + parameters: + - $ref: '../params/order_uuid.yaml' + responses: + '200': + description: Order retrieved successfully + content: + application/json: + schema: + $ref: '../components/responses/order_response.yaml' + '404': + description: Order not found + content: + application/json: + schema: + $ref: '../components/errors/not_found_error.yaml' + '422': + description: Validation error + content: + application/json: + schema: + $ref: '../components/errors/validation_error.yaml' + '502': + description: Bad gateway + content: + application/json: + schema: + $ref: '../components/errors/bad_gateway_error.yaml' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '../components/errors/generic_error.yaml' \ No newline at end of file diff --git a/shared/api/order/v1/paths/orders_uuid_cancel.yaml b/shared/api/order/v1/paths/orders_uuid_cancel.yaml new file mode 100644 index 0000000..bf7c513 --- /dev/null +++ b/shared/api/order/v1/paths/orders_uuid_cancel.yaml @@ -0,0 +1,33 @@ +post: + summary: Cancel an order + description: Cancels an existing order + operationId: cancelOrder + tags: + - Orders + parameters: + - $ref: '../params/order_uuid.yaml' + responses: + '200': + description: Order cancelled successfully + content: + application/json: + schema: + $ref: '../components/responses/order_response.yaml' + '404': + description: Order not found + content: + application/json: + schema: + $ref: '../components/errors/not_found_error.yaml' + '409': + description: Order cannot be cancelled + content: + application/json: + schema: + $ref: '../components/errors/conflict_error.yaml' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '../components/errors/generic_error.yaml' \ No newline at end of file diff --git a/shared/api/order/v1/paths/orders_uuid_pay.yaml b/shared/api/order/v1/paths/orders_uuid_pay.yaml new file mode 100644 index 0000000..84cbd08 --- /dev/null +++ b/shared/api/order/v1/paths/orders_uuid_pay.yaml @@ -0,0 +1,51 @@ +post: + summary: Pay for an order + description: Processes payment for an existing order + operationId: payOrder + tags: + - Orders + parameters: + - $ref: '../params/order_uuid.yaml' + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/requests/order_pay_request.yaml' + responses: + '200': + description: Payment processed successfully + content: + application/json: + schema: + $ref: '../components/responses/order_pay_response.yaml' + '404': + description: Order not found + content: + application/json: + schema: + $ref: '../components/errors/not_found_error.yaml' + '409': + description: Order already paid or cancelled + content: + application/json: + schema: + $ref: '../components/errors/conflict_error.yaml' + '422': + description: Validation error + content: + application/json: + schema: + $ref: '../components/errors/validation_error.yaml' + '502': + description: Payment gateway error + content: + application/json: + schema: + $ref: '../components/errors/bad_gateway_error.yaml' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '../components/errors/generic_error.yaml' \ No newline at end of file diff --git a/shared/proto/buf.gen.yaml b/shared/buf.gen.yaml similarity index 66% rename from shared/proto/buf.gen.yaml rename to shared/buf.gen.yaml index c92b75b..d6c426a 100644 --- a/shared/proto/buf.gen.yaml +++ b/shared/buf.gen.yaml @@ -3,11 +3,11 @@ version: v2 clean: true plugins: - - local: ../../bin/protoc-gen-go - out: ../pkg/proto + - local: ../bin/protoc-gen-go + out: ./pkg/proto opt: - paths=source_relative - - local: ../../bin/protoc-gen-go-grpc - out: ../pkg/proto + - local: ../bin/protoc-gen-go-grpc + out: ./pkg/proto opt: - paths=source_relative \ No newline at end of file diff --git a/shared/proto/buf.lock b/shared/buf.lock similarity index 100% rename from shared/proto/buf.lock rename to shared/buf.lock diff --git a/shared/proto/buf.yaml b/shared/buf.yaml similarity index 91% rename from shared/proto/buf.yaml rename to shared/buf.yaml index 551fdf7..653a942 100644 --- a/shared/proto/buf.yaml +++ b/shared/buf.yaml @@ -1,5 +1,7 @@ # For details on buf.yaml configuration, visit https://buf.build/docs/configuration/v2/buf-yaml version: v2 +modules: + - path: proto lint: use: - STANDARD diff --git a/shared/go.mod b/shared/go.mod index a048230..69871a3 100644 --- a/shared/go.mod +++ b/shared/go.mod @@ -3,13 +3,40 @@ module github.com/qyrlabs/test-backend/shared go 1.25.6 require ( + github.com/go-faster/errors v0.7.1 + github.com/go-faster/jx v1.2.0 + github.com/google/uuid v1.6.0 + github.com/ogen-go/ogen v1.18.0 + go.opentelemetry.io/otel v1.38.0 + go.opentelemetry.io/otel/metric v1.38.0 + go.opentelemetry.io/otel/trace v1.38.0 google.golang.org/grpc v1.78.0 google.golang.org/protobuf v1.36.11 ) require ( - golang.org/x/net v0.47.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-faster/yaml v0.4.6 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/segmentio/asm v1.2.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 // indirect + golang.org/x/mod v0.30.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/tools v0.39.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) + +tool github.com/ogen-go/ogen/cmd/ogen diff --git a/shared/go.sum b/shared/go.sum index ae87d8a..404b4a7 100644 --- a/shared/go.sum +++ b/shared/go.sum @@ -1,3 +1,18 @@ +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/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= +github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= +github.com/go-faster/jx v1.2.0 h1:T2YHJPrFaYu21fJtUxC9GzmluKu8rVIFDwwGBKTDseI= +github.com/go-faster/jx v1.2.0/go.mod h1:UWLOVDmMG597a5tBFPLIWJdUxz5/2emOpfsj9Neg0PE= +github.com/go-faster/yaml v0.4.6 h1:lOK/EhI04gCpPgPhgt0bChS6bvw7G3WwI8xxVe0sw9I= +github.com/go-faster/yaml v0.4.6/go.mod h1:390dRIvV4zbnO7qC9FGo6YYutc+wyyUSHBgbXL52eXk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -8,6 +23,26 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/ogen-go/ogen v1.18.0 h1:6RQ7lFBjOeNaUWu4getfqIh4GJbEY4hqKuzDtec/g60= +github.com/ogen-go/ogen v1.18.0/go.mod h1:dHFr2Wf6cA7tSxMI+zPC21UR5hAlDw8ZYUkK3PziURY= +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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= @@ -20,12 +55,27 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6 go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY= +golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/genproto/googleapis/rpc v0.0.0-20260120174246-409b4a993575 h1:vzOYHDZEHIsPYYnaSYo60AqHkJronSu0rzTz/s4quL0= @@ -34,3 +84,10 @@ google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/shared/ogen-config.yaml b/shared/ogen-config.yaml new file mode 100644 index 0000000..7d9a3ec --- /dev/null +++ b/shared/ogen-config.yaml @@ -0,0 +1,5 @@ +# sets parser options. +parser: + # enables remote references resolving. + allow_remote: true + \ No newline at end of file diff --git a/shared/pkg/openapi/order/v1/oas_cfg_gen.go b/shared/pkg/openapi/order/v1/oas_cfg_gen.go new file mode 100644 index 0000000..363b665 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_cfg_gen.go @@ -0,0 +1,291 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "net/http" + + ht "github.com/ogen-go/ogen/http" + "github.com/ogen-go/ogen/middleware" + "github.com/ogen-go/ogen/ogenerrors" + "github.com/ogen-go/ogen/otelogen" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/trace" +) + +var ( + // Allocate option closure once. + clientSpanKind = trace.WithSpanKind(trace.SpanKindClient) + // Allocate option closure once. + serverSpanKind = trace.WithSpanKind(trace.SpanKindServer) +) + +type ( + optionFunc[C any] func(*C) + otelOptionFunc func(*otelConfig) +) + +type otelConfig struct { + TracerProvider trace.TracerProvider + Tracer trace.Tracer + MeterProvider metric.MeterProvider + Meter metric.Meter + Attributes []attribute.KeyValue +} + +func (cfg *otelConfig) initOTEL() { + if cfg.TracerProvider == nil { + cfg.TracerProvider = otel.GetTracerProvider() + } + if cfg.MeterProvider == nil { + cfg.MeterProvider = otel.GetMeterProvider() + } + cfg.Tracer = cfg.TracerProvider.Tracer(otelogen.Name, + trace.WithInstrumentationVersion(otelogen.SemVersion()), + ) + cfg.Meter = cfg.MeterProvider.Meter(otelogen.Name, + metric.WithInstrumentationVersion(otelogen.SemVersion()), + ) +} + +// ErrorHandler is error handler. +type ErrorHandler = ogenerrors.ErrorHandler + +type serverConfig struct { + otelConfig + NotFound http.HandlerFunc + MethodNotAllowed func(w http.ResponseWriter, r *http.Request, allowed string) + ErrorHandler ErrorHandler + Prefix string + Middleware Middleware + MaxMultipartMemory int64 +} + +// ServerOption is server config option. +type ServerOption interface { + applyServer(*serverConfig) +} + +var _ ServerOption = (optionFunc[serverConfig])(nil) + +func (o optionFunc[C]) applyServer(c *C) { + o(c) +} + +var _ ServerOption = (otelOptionFunc)(nil) + +func (o otelOptionFunc) applyServer(c *serverConfig) { + o(&c.otelConfig) +} + +func newServerConfig(opts ...ServerOption) serverConfig { + cfg := serverConfig{ + NotFound: http.NotFound, + MethodNotAllowed: func(w http.ResponseWriter, r *http.Request, allowed string) { + status := http.StatusMethodNotAllowed + if r.Method == "OPTIONS" { + w.Header().Set("Access-Control-Allow-Methods", allowed) + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + status = http.StatusNoContent + } else { + w.Header().Set("Allow", allowed) + } + w.WriteHeader(status) + }, + ErrorHandler: ogenerrors.DefaultErrorHandler, + Middleware: nil, + MaxMultipartMemory: 32 << 20, // 32 MB + } + for _, opt := range opts { + opt.applyServer(&cfg) + } + cfg.initOTEL() + return cfg +} + +type baseServer struct { + cfg serverConfig + requests metric.Int64Counter + errors metric.Int64Counter + duration metric.Float64Histogram +} + +func (s baseServer) notFound(w http.ResponseWriter, r *http.Request) { + s.cfg.NotFound(w, r) +} + +func (s baseServer) notAllowed(w http.ResponseWriter, r *http.Request, allowed string) { + s.cfg.MethodNotAllowed(w, r, allowed) +} + +func (cfg serverConfig) baseServer() (s baseServer, err error) { + s = baseServer{cfg: cfg} + if s.requests, err = otelogen.ServerRequestCountCounter(s.cfg.Meter); err != nil { + return s, err + } + if s.errors, err = otelogen.ServerErrorsCountCounter(s.cfg.Meter); err != nil { + return s, err + } + if s.duration, err = otelogen.ServerDurationHistogram(s.cfg.Meter); err != nil { + return s, err + } + return s, nil +} + +type clientConfig struct { + otelConfig + Client ht.Client +} + +// ClientOption is client config option. +type ClientOption interface { + applyClient(*clientConfig) +} + +var _ ClientOption = (optionFunc[clientConfig])(nil) + +func (o optionFunc[C]) applyClient(c *C) { + o(c) +} + +var _ ClientOption = (otelOptionFunc)(nil) + +func (o otelOptionFunc) applyClient(c *clientConfig) { + o(&c.otelConfig) +} + +func newClientConfig(opts ...ClientOption) clientConfig { + cfg := clientConfig{ + Client: http.DefaultClient, + } + for _, opt := range opts { + opt.applyClient(&cfg) + } + cfg.initOTEL() + return cfg +} + +type baseClient struct { + cfg clientConfig + requests metric.Int64Counter + errors metric.Int64Counter + duration metric.Float64Histogram +} + +func (cfg clientConfig) baseClient() (c baseClient, err error) { + c = baseClient{cfg: cfg} + if c.requests, err = otelogen.ClientRequestCountCounter(c.cfg.Meter); err != nil { + return c, err + } + if c.errors, err = otelogen.ClientErrorsCountCounter(c.cfg.Meter); err != nil { + return c, err + } + if c.duration, err = otelogen.ClientDurationHistogram(c.cfg.Meter); err != nil { + return c, err + } + return c, nil +} + +// Option is config option. +type Option interface { + ServerOption + ClientOption +} + +// WithTracerProvider specifies a tracer provider to use for creating a tracer. +// +// If none is specified, the global provider is used. +func WithTracerProvider(provider trace.TracerProvider) Option { + return otelOptionFunc(func(cfg *otelConfig) { + if provider != nil { + cfg.TracerProvider = provider + } + }) +} + +// WithMeterProvider specifies a meter provider to use for creating a meter. +// +// If none is specified, the otel.GetMeterProvider() is used. +func WithMeterProvider(provider metric.MeterProvider) Option { + return otelOptionFunc(func(cfg *otelConfig) { + if provider != nil { + cfg.MeterProvider = provider + } + }) +} + +// WithAttributes specifies default otel attributes. +func WithAttributes(attributes ...attribute.KeyValue) Option { + return otelOptionFunc(func(cfg *otelConfig) { + cfg.Attributes = attributes + }) +} + +// WithClient specifies http client to use. +func WithClient(client ht.Client) ClientOption { + return optionFunc[clientConfig](func(cfg *clientConfig) { + if client != nil { + cfg.Client = client + } + }) +} + +// WithNotFound specifies Not Found handler to use. +func WithNotFound(notFound http.HandlerFunc) ServerOption { + return optionFunc[serverConfig](func(cfg *serverConfig) { + if notFound != nil { + cfg.NotFound = notFound + } + }) +} + +// WithMethodNotAllowed specifies Method Not Allowed handler to use. +func WithMethodNotAllowed(methodNotAllowed func(w http.ResponseWriter, r *http.Request, allowed string)) ServerOption { + return optionFunc[serverConfig](func(cfg *serverConfig) { + if methodNotAllowed != nil { + cfg.MethodNotAllowed = methodNotAllowed + } + }) +} + +// WithErrorHandler specifies error handler to use. +func WithErrorHandler(h ErrorHandler) ServerOption { + return optionFunc[serverConfig](func(cfg *serverConfig) { + if h != nil { + cfg.ErrorHandler = h + } + }) +} + +// WithPathPrefix specifies server path prefix. +func WithPathPrefix(prefix string) ServerOption { + return optionFunc[serverConfig](func(cfg *serverConfig) { + cfg.Prefix = prefix + }) +} + +// WithMiddleware specifies middlewares to use. +func WithMiddleware(m ...Middleware) ServerOption { + return optionFunc[serverConfig](func(cfg *serverConfig) { + switch len(m) { + case 0: + cfg.Middleware = nil + case 1: + cfg.Middleware = m[0] + default: + cfg.Middleware = middleware.ChainMiddlewares(m...) + } + }) +} + +// WithMaxMultipartMemory specifies limit of memory for storing file parts. +// File parts which can't be stored in memory will be stored on disk in temporary files. +func WithMaxMultipartMemory(max int64) ServerOption { + return optionFunc[serverConfig](func(cfg *serverConfig) { + if max > 0 { + cfg.MaxMultipartMemory = max + } + }) +} diff --git a/shared/pkg/openapi/order/v1/oas_client_gen.go b/shared/pkg/openapi/order/v1/oas_client_gen.go new file mode 100644 index 0000000..60b51ac --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_client_gen.go @@ -0,0 +1,455 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "context" + "net/url" + "strings" + "time" + + "github.com/go-faster/errors" + "github.com/ogen-go/ogen/conv" + ht "github.com/ogen-go/ogen/http" + "github.com/ogen-go/ogen/otelogen" + "github.com/ogen-go/ogen/uri" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + "go.opentelemetry.io/otel/trace" +) + +func trimTrailingSlashes(u *url.URL) { + u.Path = strings.TrimRight(u.Path, "/") + u.RawPath = strings.TrimRight(u.RawPath, "/") +} + +// Invoker invokes operations described by OpenAPI v3 specification. +type Invoker interface { + // CancelOrder invokes cancelOrder operation. + // + // Cancels an existing order. + // + // POST /api/v1/orders/{order_uuid}/cancel + CancelOrder(ctx context.Context, params CancelOrderParams) (CancelOrderRes, error) + // CreateOrder invokes createOrder operation. + // + // Creates a new order. + // + // POST /api/v1/orders + CreateOrder(ctx context.Context, request *OrderCreateRequest) (CreateOrderRes, error) + // GetOrderByUuid invokes getOrderByUuid operation. + // + // Retrieves order details by UUID. + // + // GET /api/v1/orders/{order_uuid} + GetOrderByUuid(ctx context.Context, params GetOrderByUuidParams) (GetOrderByUuidRes, error) + // PayOrder invokes payOrder operation. + // + // Processes payment for an existing order. + // + // POST /api/v1/orders/{order_uuid}/pay + PayOrder(ctx context.Context, request *OrderPayRequest, params PayOrderParams) (PayOrderRes, error) +} + +// Client implements OAS client. +type Client struct { + serverURL *url.URL + baseClient +} +type errorHandler interface { + NewError(ctx context.Context, err error) *GenericErrorStatusCode +} + +var _ Handler = struct { + errorHandler + *Client +}{} + +// NewClient initializes new Client defined by OAS. +func NewClient(serverURL string, opts ...ClientOption) (*Client, error) { + u, err := url.Parse(serverURL) + if err != nil { + return nil, err + } + trimTrailingSlashes(u) + + c, err := newClientConfig(opts...).baseClient() + if err != nil { + return nil, err + } + return &Client{ + serverURL: u, + baseClient: c, + }, nil +} + +type serverURLKey struct{} + +// WithServerURL sets context key to override server URL. +func WithServerURL(ctx context.Context, u *url.URL) context.Context { + return context.WithValue(ctx, serverURLKey{}, u) +} + +func (c *Client) requestURL(ctx context.Context) *url.URL { + u, ok := ctx.Value(serverURLKey{}).(*url.URL) + if !ok { + return c.serverURL + } + return u +} + +// CancelOrder invokes cancelOrder operation. +// +// Cancels an existing order. +// +// POST /api/v1/orders/{order_uuid}/cancel +func (c *Client) CancelOrder(ctx context.Context, params CancelOrderParams) (CancelOrderRes, error) { + res, err := c.sendCancelOrder(ctx, params) + return res, err +} + +func (c *Client) sendCancelOrder(ctx context.Context, params CancelOrderParams) (res CancelOrderRes, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("cancelOrder"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.URLTemplateKey.String("/api/v1/orders/{order_uuid}/cancel"), + } + otelAttrs = append(otelAttrs, c.cfg.Attributes...) + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, CancelOrderOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [3]string + pathParts[0] = "/api/v1/orders/" + { + // Encode "order_uuid" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "order_uuid", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.UUIDToString(params.OrderUUID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + encoded, err := e.Result() + if err != nil { + return res, errors.Wrap(err, "encode path") + } + pathParts[1] = encoded + } + pathParts[2] = "/cancel" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "POST", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodeCancelOrderResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + +// CreateOrder invokes createOrder operation. +// +// Creates a new order. +// +// POST /api/v1/orders +func (c *Client) CreateOrder(ctx context.Context, request *OrderCreateRequest) (CreateOrderRes, error) { + res, err := c.sendCreateOrder(ctx, request) + return res, err +} + +func (c *Client) sendCreateOrder(ctx context.Context, request *OrderCreateRequest) (res CreateOrderRes, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("createOrder"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.URLTemplateKey.String("/api/v1/orders"), + } + otelAttrs = append(otelAttrs, c.cfg.Attributes...) + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, CreateOrderOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [1]string + pathParts[0] = "/api/v1/orders" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "POST", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + if err := encodeCreateOrderRequest(request, r); err != nil { + return res, errors.Wrap(err, "encode request") + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodeCreateOrderResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + +// GetOrderByUuid invokes getOrderByUuid operation. +// +// Retrieves order details by UUID. +// +// GET /api/v1/orders/{order_uuid} +func (c *Client) GetOrderByUuid(ctx context.Context, params GetOrderByUuidParams) (GetOrderByUuidRes, error) { + res, err := c.sendGetOrderByUuid(ctx, params) + return res, err +} + +func (c *Client) sendGetOrderByUuid(ctx context.Context, params GetOrderByUuidParams) (res GetOrderByUuidRes, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("getOrderByUuid"), + semconv.HTTPRequestMethodKey.String("GET"), + semconv.URLTemplateKey.String("/api/v1/orders/{order_uuid}"), + } + otelAttrs = append(otelAttrs, c.cfg.Attributes...) + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, GetOrderByUuidOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [2]string + pathParts[0] = "/api/v1/orders/" + { + // Encode "order_uuid" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "order_uuid", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.UUIDToString(params.OrderUUID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + encoded, err := e.Result() + if err != nil { + return res, errors.Wrap(err, "encode path") + } + pathParts[1] = encoded + } + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "GET", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodeGetOrderByUuidResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + +// PayOrder invokes payOrder operation. +// +// Processes payment for an existing order. +// +// POST /api/v1/orders/{order_uuid}/pay +func (c *Client) PayOrder(ctx context.Context, request *OrderPayRequest, params PayOrderParams) (PayOrderRes, error) { + res, err := c.sendPayOrder(ctx, request, params) + return res, err +} + +func (c *Client) sendPayOrder(ctx context.Context, request *OrderPayRequest, params PayOrderParams) (res PayOrderRes, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("payOrder"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.URLTemplateKey.String("/api/v1/orders/{order_uuid}/pay"), + } + otelAttrs = append(otelAttrs, c.cfg.Attributes...) + + // Run stopwatch. + startTime := time.Now() + defer func() { + // Use floating point division here for higher precision (instead of Millisecond method). + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), metric.WithAttributes(otelAttrs...)) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, PayOrderOperation, + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, metric.WithAttributes(otelAttrs...)) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + var pathParts [3]string + pathParts[0] = "/api/v1/orders/" + { + // Encode "order_uuid" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "order_uuid", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.UUIDToString(params.OrderUUID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + encoded, err := e.Result() + if err != nil { + return res, errors.Wrap(err, "encode path") + } + pathParts[1] = encoded + } + pathParts[2] = "/pay" + uri.AddPathParts(u, pathParts[:]...) + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "POST", u) + if err != nil { + return res, errors.Wrap(err, "create request") + } + if err := encodePayOrderRequest(request, r); err != nil { + return res, errors.Wrap(err, "encode request") + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodePayOrderResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} diff --git a/shared/pkg/openapi/order/v1/oas_handlers_gen.go b/shared/pkg/openapi/order/v1/oas_handlers_gen.go new file mode 100644 index 0000000..366b87d --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_handlers_gen.go @@ -0,0 +1,657 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "context" + "net/http" + "time" + + "github.com/go-faster/errors" + ht "github.com/ogen-go/ogen/http" + "github.com/ogen-go/ogen/middleware" + "github.com/ogen-go/ogen/ogenerrors" + "github.com/ogen-go/ogen/otelogen" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + "go.opentelemetry.io/otel/trace" +) + +type codeRecorder struct { + http.ResponseWriter + status int +} + +func (c *codeRecorder) WriteHeader(status int) { + c.status = status + c.ResponseWriter.WriteHeader(status) +} + +func (c *codeRecorder) Unwrap() http.ResponseWriter { + return c.ResponseWriter +} + +// handleCancelOrderRequest handles cancelOrder operation. +// +// Cancels an existing order. +// +// POST /api/v1/orders/{order_uuid}/cancel +func (s *Server) handleCancelOrderRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("cancelOrder"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/api/v1/orders/{order_uuid}/cancel"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), CancelOrderOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code < 100 || code >= 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: CancelOrderOperation, + ID: "cancelOrder", + } + ) + params, err := decodeCancelOrderParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var rawBody []byte + + var response CancelOrderRes + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: CancelOrderOperation, + OperationSummary: "Cancel an order", + OperationID: "cancelOrder", + Body: nil, + RawBody: rawBody, + Params: middleware.Parameters{ + { + Name: "order_uuid", + In: "path", + }: params.OrderUUID, + }, + Raw: r, + } + + type ( + Request = struct{} + Params = CancelOrderParams + Response = CancelOrderRes + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackCancelOrderParams, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + response, err = s.h.CancelOrder(ctx, params) + return response, err + }, + ) + } else { + response, err = s.h.CancelOrder(ctx, params) + } + if err != nil { + if errRes, ok := errors.Into[*GenericErrorStatusCode](err); ok { + if err := encodeErrorResponse(errRes, w, span); err != nil { + defer recordError("Internal", err) + } + return + } + if errors.Is(err, ht.ErrNotImplemented) { + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { + defer recordError("Internal", err) + } + return + } + + if err := encodeCancelOrderResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + +// handleCreateOrderRequest handles createOrder operation. +// +// Creates a new order. +// +// POST /api/v1/orders +func (s *Server) handleCreateOrderRequest(args [0]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("createOrder"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/api/v1/orders"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), CreateOrderOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code < 100 || code >= 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: CreateOrderOperation, + ID: "createOrder", + } + ) + + var rawBody []byte + request, rawBody, close, err := s.decodeCreateOrderRequest(r) + if err != nil { + err = &ogenerrors.DecodeRequestError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeRequest", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + defer func() { + if err := close(); err != nil { + recordError("CloseRequest", err) + } + }() + + var response CreateOrderRes + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: CreateOrderOperation, + OperationSummary: "Create a new order", + OperationID: "createOrder", + Body: request, + RawBody: rawBody, + Params: middleware.Parameters{}, + Raw: r, + } + + type ( + Request = *OrderCreateRequest + Params = struct{} + Response = CreateOrderRes + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + nil, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + response, err = s.h.CreateOrder(ctx, request) + return response, err + }, + ) + } else { + response, err = s.h.CreateOrder(ctx, request) + } + if err != nil { + if errRes, ok := errors.Into[*GenericErrorStatusCode](err); ok { + if err := encodeErrorResponse(errRes, w, span); err != nil { + defer recordError("Internal", err) + } + return + } + if errors.Is(err, ht.ErrNotImplemented) { + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { + defer recordError("Internal", err) + } + return + } + + if err := encodeCreateOrderResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + +// handleGetOrderByUuidRequest handles getOrderByUuid operation. +// +// Retrieves order details by UUID. +// +// GET /api/v1/orders/{order_uuid} +func (s *Server) handleGetOrderByUuidRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("getOrderByUuid"), + semconv.HTTPRequestMethodKey.String("GET"), + semconv.HTTPRouteKey.String("/api/v1/orders/{order_uuid}"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), GetOrderByUuidOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code < 100 || code >= 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: GetOrderByUuidOperation, + ID: "getOrderByUuid", + } + ) + params, err := decodeGetOrderByUuidParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var rawBody []byte + + var response GetOrderByUuidRes + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: GetOrderByUuidOperation, + OperationSummary: "Get order by UUID", + OperationID: "getOrderByUuid", + Body: nil, + RawBody: rawBody, + Params: middleware.Parameters{ + { + Name: "order_uuid", + In: "path", + }: params.OrderUUID, + }, + Raw: r, + } + + type ( + Request = struct{} + Params = GetOrderByUuidParams + Response = GetOrderByUuidRes + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackGetOrderByUuidParams, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + response, err = s.h.GetOrderByUuid(ctx, params) + return response, err + }, + ) + } else { + response, err = s.h.GetOrderByUuid(ctx, params) + } + if err != nil { + if errRes, ok := errors.Into[*GenericErrorStatusCode](err); ok { + if err := encodeErrorResponse(errRes, w, span); err != nil { + defer recordError("Internal", err) + } + return + } + if errors.Is(err, ht.ErrNotImplemented) { + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { + defer recordError("Internal", err) + } + return + } + + if err := encodeGetOrderByUuidResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} + +// handlePayOrderRequest handles payOrder operation. +// +// Processes payment for an existing order. +// +// POST /api/v1/orders/{order_uuid}/pay +func (s *Server) handlePayOrderRequest(args [1]string, argsEscaped bool, w http.ResponseWriter, r *http.Request) { + statusWriter := &codeRecorder{ResponseWriter: w} + w = statusWriter + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("payOrder"), + semconv.HTTPRequestMethodKey.String("POST"), + semconv.HTTPRouteKey.String("/api/v1/orders/{order_uuid}/pay"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), PayOrderOperation, + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Add Labeler to context. + labeler := &Labeler{attrs: otelAttrs} + ctx = contextWithLabeler(ctx, labeler) + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + code := statusWriter.status + if code != 0 { + codeAttr := semconv.HTTPResponseStatusCode(code) + attrs = append(attrs, codeAttr) + span.SetAttributes(codeAttr) + } + attrOpt := metric.WithAttributes(attrs...) + + // Increment request counter. + s.requests.Add(ctx, 1, attrOpt) + + // Use floating point division here for higher precision (instead of Millisecond method). + s.duration.Record(ctx, float64(elapsedDuration)/float64(time.Millisecond), attrOpt) + }() + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + + // https://opentelemetry.io/docs/specs/semconv/http/http-spans/#status + // Span Status MUST be left unset if HTTP status code was in the 1xx, 2xx or 3xx ranges, + // unless there was another error (e.g., network error receiving the response body; or 3xx codes with + // max redirects exceeded), in which case status MUST be set to Error. + code := statusWriter.status + if code < 100 || code >= 500 { + span.SetStatus(codes.Error, stage) + } + + attrSet := labeler.AttributeSet() + attrs := attrSet.ToSlice() + if code != 0 { + attrs = append(attrs, semconv.HTTPResponseStatusCode(code)) + } + + s.errors.Add(ctx, 1, metric.WithAttributes(attrs...)) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: PayOrderOperation, + ID: "payOrder", + } + ) + params, err := decodePayOrderParams(args, argsEscaped, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var rawBody []byte + request, rawBody, close, err := s.decodePayOrderRequest(r) + if err != nil { + err = &ogenerrors.DecodeRequestError{ + OperationContext: opErrContext, + Err: err, + } + defer recordError("DecodeRequest", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + defer func() { + if err := close(); err != nil { + recordError("CloseRequest", err) + } + }() + + var response PayOrderRes + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: PayOrderOperation, + OperationSummary: "Pay for an order", + OperationID: "payOrder", + Body: request, + RawBody: rawBody, + Params: middleware.Parameters{ + { + Name: "order_uuid", + In: "path", + }: params.OrderUUID, + }, + Raw: r, + } + + type ( + Request = *OrderPayRequest + Params = PayOrderParams + Response = PayOrderRes + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackPayOrderParams, + func(ctx context.Context, request Request, params Params) (response Response, err error) { + response, err = s.h.PayOrder(ctx, request, params) + return response, err + }, + ) + } else { + response, err = s.h.PayOrder(ctx, request, params) + } + if err != nil { + if errRes, ok := errors.Into[*GenericErrorStatusCode](err); ok { + if err := encodeErrorResponse(errRes, w, span); err != nil { + defer recordError("Internal", err) + } + return + } + if errors.Is(err, ht.ErrNotImplemented) { + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + if err := encodeErrorResponse(s.h.NewError(ctx, err), w, span); err != nil { + defer recordError("Internal", err) + } + return + } + + if err := encodePayOrderResponse(response, w, span); err != nil { + defer recordError("EncodeResponse", err) + if !errors.Is(err, ht.ErrInternalServerErrorResponse) { + s.cfg.ErrorHandler(ctx, w, r, err) + } + return + } +} diff --git a/shared/pkg/openapi/order/v1/oas_interfaces_gen.go b/shared/pkg/openapi/order/v1/oas_interfaces_gen.go new file mode 100644 index 0000000..5c384a0 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_interfaces_gen.go @@ -0,0 +1,18 @@ +// Code generated by ogen, DO NOT EDIT. +package orderv1 + +type CancelOrderRes interface { + cancelOrderRes() +} + +type CreateOrderRes interface { + createOrderRes() +} + +type GetOrderByUuidRes interface { + getOrderByUuidRes() +} + +type PayOrderRes interface { + payOrderRes() +} diff --git a/shared/pkg/openapi/order/v1/oas_json_gen.go b/shared/pkg/openapi/order/v1/oas_json_gen.go new file mode 100644 index 0000000..838758f --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_json_gen.go @@ -0,0 +1,1560 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "math/bits" + "strconv" + + "github.com/go-faster/errors" + "github.com/go-faster/jx" + "github.com/google/uuid" + "github.com/ogen-go/ogen/json" + "github.com/ogen-go/ogen/validate" +) + +// Encode implements json.Marshaler. +func (s *BadGatewayError) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *BadGatewayError) encodeFields(e *jx.Encoder) { + { + e.FieldStart("code") + e.Int(s.Code) + } + { + e.FieldStart("message") + e.Str(s.Message) + } +} + +var jsonFieldsNameOfBadGatewayError = [2]string{ + 0: "code", + 1: "message", +} + +// Decode decodes BadGatewayError from json. +func (s *BadGatewayError) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode BadGatewayError to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "code": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := d.Int() + s.Code = int(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"code\"") + } + case "message": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + v, err := d.Str() + s.Message = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"message\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode BadGatewayError") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfBadGatewayError) { + name = jsonFieldsNameOfBadGatewayError[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *BadGatewayError) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *BadGatewayError) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *ConflictError) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *ConflictError) encodeFields(e *jx.Encoder) { + { + e.FieldStart("code") + e.Int(s.Code) + } + { + e.FieldStart("message") + e.Str(s.Message) + } +} + +var jsonFieldsNameOfConflictError = [2]string{ + 0: "code", + 1: "message", +} + +// Decode decodes ConflictError from json. +func (s *ConflictError) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode ConflictError to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "code": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := d.Int() + s.Code = int(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"code\"") + } + case "message": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + v, err := d.Str() + s.Message = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"message\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode ConflictError") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfConflictError) { + name = jsonFieldsNameOfConflictError[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *ConflictError) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *ConflictError) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *GenericError) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *GenericError) encodeFields(e *jx.Encoder) { + { + if s.Code.Set { + e.FieldStart("code") + s.Code.Encode(e) + } + } + { + if s.Message.Set { + e.FieldStart("message") + s.Message.Encode(e) + } + } +} + +var jsonFieldsNameOfGenericError = [2]string{ + 0: "code", + 1: "message", +} + +// Decode decodes GenericError from json. +func (s *GenericError) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode GenericError to nil") + } + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "code": + if err := func() error { + s.Code.Reset() + if err := s.Code.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"code\"") + } + case "message": + if err := func() error { + s.Message.Reset() + if err := s.Message.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"message\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode GenericError") + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *GenericError) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *GenericError) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *NotFoundError) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *NotFoundError) encodeFields(e *jx.Encoder) { + { + e.FieldStart("code") + e.Int(s.Code) + } + { + e.FieldStart("message") + e.Str(s.Message) + } +} + +var jsonFieldsNameOfNotFoundError = [2]string{ + 0: "code", + 1: "message", +} + +// Decode decodes NotFoundError from json. +func (s *NotFoundError) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode NotFoundError to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "code": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := d.Int() + s.Code = int(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"code\"") + } + case "message": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + v, err := d.Str() + s.Message = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"message\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode NotFoundError") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfNotFoundError) { + name = jsonFieldsNameOfNotFoundError[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *NotFoundError) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *NotFoundError) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes int as json. +func (o OptInt) Encode(e *jx.Encoder) { + if !o.Set { + return + } + e.Int(int(o.Value)) +} + +// Decode decodes int from json. +func (o *OptInt) Decode(d *jx.Decoder) error { + if o == nil { + return errors.New("invalid: unable to decode OptInt to nil") + } + o.Set = true + v, err := d.Int() + if err != nil { + return err + } + o.Value = int(v) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OptInt) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OptInt) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes PaymentMethod as json. +func (o OptPaymentMethod) Encode(e *jx.Encoder) { + if !o.Set { + return + } + e.Str(string(o.Value)) +} + +// Decode decodes PaymentMethod from json. +func (o *OptPaymentMethod) Decode(d *jx.Decoder) error { + if o == nil { + return errors.New("invalid: unable to decode OptPaymentMethod to nil") + } + o.Set = true + if err := o.Value.Decode(d); err != nil { + return err + } + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OptPaymentMethod) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OptPaymentMethod) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes string as json. +func (o OptString) Encode(e *jx.Encoder) { + if !o.Set { + return + } + e.Str(string(o.Value)) +} + +// Decode decodes string from json. +func (o *OptString) Decode(d *jx.Decoder) error { + if o == nil { + return errors.New("invalid: unable to decode OptString to nil") + } + o.Set = true + v, err := d.Str() + if err != nil { + return err + } + o.Value = string(v) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OptString) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OptString) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes uuid.UUID as json. +func (o OptUUID) Encode(e *jx.Encoder) { + if !o.Set { + return + } + json.EncodeUUID(e, o.Value) +} + +// Decode decodes uuid.UUID from json. +func (o *OptUUID) Decode(d *jx.Decoder) error { + if o == nil { + return errors.New("invalid: unable to decode OptUUID to nil") + } + o.Set = true + v, err := json.DecodeUUID(d) + if err != nil { + return err + } + o.Value = v + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OptUUID) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OptUUID) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *Order) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *Order) encodeFields(e *jx.Encoder) { + { + e.FieldStart("order_uuid") + json.EncodeUUID(e, s.OrderUUID) + } + { + e.FieldStart("user_uuid") + json.EncodeUUID(e, s.UserUUID) + } + { + e.FieldStart("part_uuids") + e.ArrStart() + for _, elem := range s.PartUuids { + json.EncodeUUID(e, elem) + } + e.ArrEnd() + } + { + e.FieldStart("total_price_minor") + e.Int64(s.TotalPriceMinor) + } + { + if s.TransactionUUID.Set { + e.FieldStart("transaction_uuid") + s.TransactionUUID.Encode(e) + } + } + { + if s.PaymentMethod.Set { + e.FieldStart("payment_method") + s.PaymentMethod.Encode(e) + } + } + { + e.FieldStart("status") + s.Status.Encode(e) + } +} + +var jsonFieldsNameOfOrder = [7]string{ + 0: "order_uuid", + 1: "user_uuid", + 2: "part_uuids", + 3: "total_price_minor", + 4: "transaction_uuid", + 5: "payment_method", + 6: "status", +} + +// Decode decodes Order from json. +func (s *Order) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode Order to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "order_uuid": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := json.DecodeUUID(d) + s.OrderUUID = v + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"order_uuid\"") + } + case "user_uuid": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + v, err := json.DecodeUUID(d) + s.UserUUID = v + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"user_uuid\"") + } + case "part_uuids": + requiredBitSet[0] |= 1 << 2 + if err := func() error { + s.PartUuids = make([]uuid.UUID, 0) + if err := d.Arr(func(d *jx.Decoder) error { + var elem uuid.UUID + v, err := json.DecodeUUID(d) + elem = v + if err != nil { + return err + } + s.PartUuids = append(s.PartUuids, elem) + return nil + }); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"part_uuids\"") + } + case "total_price_minor": + requiredBitSet[0] |= 1 << 3 + if err := func() error { + v, err := d.Int64() + s.TotalPriceMinor = int64(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"total_price_minor\"") + } + case "transaction_uuid": + if err := func() error { + s.TransactionUUID.Reset() + if err := s.TransactionUUID.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"transaction_uuid\"") + } + case "payment_method": + if err := func() error { + s.PaymentMethod.Reset() + if err := s.PaymentMethod.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"payment_method\"") + } + case "status": + requiredBitSet[0] |= 1 << 6 + if err := func() error { + if err := s.Status.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"status\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode Order") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b01001111, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfOrder) { + name = jsonFieldsNameOfOrder[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *Order) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *Order) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *OrderCreateRequest) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *OrderCreateRequest) encodeFields(e *jx.Encoder) { + { + e.FieldStart("user_uuid") + s.UserUUID.Encode(e) + } + { + e.FieldStart("part_uuids") + s.PartUuids.Encode(e) + } +} + +var jsonFieldsNameOfOrderCreateRequest = [2]string{ + 0: "user_uuid", + 1: "part_uuids", +} + +// Decode decodes OrderCreateRequest from json. +func (s *OrderCreateRequest) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode OrderCreateRequest to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "user_uuid": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + if err := s.UserUUID.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"user_uuid\"") + } + case "part_uuids": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + if err := s.PartUuids.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"part_uuids\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode OrderCreateRequest") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfOrderCreateRequest) { + name = jsonFieldsNameOfOrderCreateRequest[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *OrderCreateRequest) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OrderCreateRequest) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *OrderCreateResponse) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *OrderCreateResponse) encodeFields(e *jx.Encoder) { + { + e.FieldStart("order_uuid") + s.OrderUUID.Encode(e) + } + { + e.FieldStart("total_price_minor") + s.TotalPriceMinor.Encode(e) + } +} + +var jsonFieldsNameOfOrderCreateResponse = [2]string{ + 0: "order_uuid", + 1: "total_price_minor", +} + +// Decode decodes OrderCreateResponse from json. +func (s *OrderCreateResponse) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode OrderCreateResponse to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "order_uuid": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + if err := s.OrderUUID.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"order_uuid\"") + } + case "total_price_minor": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + if err := s.TotalPriceMinor.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"total_price_minor\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode OrderCreateResponse") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfOrderCreateResponse) { + name = jsonFieldsNameOfOrderCreateResponse[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *OrderCreateResponse) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OrderCreateResponse) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *OrderPayRequest) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *OrderPayRequest) encodeFields(e *jx.Encoder) { + { + e.FieldStart("payment_method") + s.PaymentMethod.Encode(e) + } +} + +var jsonFieldsNameOfOrderPayRequest = [1]string{ + 0: "payment_method", +} + +// Decode decodes OrderPayRequest from json. +func (s *OrderPayRequest) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode OrderPayRequest to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "payment_method": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + if err := s.PaymentMethod.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"payment_method\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode OrderPayRequest") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000001, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfOrderPayRequest) { + name = jsonFieldsNameOfOrderPayRequest[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *OrderPayRequest) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OrderPayRequest) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *OrderPayResponse) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *OrderPayResponse) encodeFields(e *jx.Encoder) { + { + e.FieldStart("transaction_uuid") + json.EncodeUUID(e, s.TransactionUUID) + } +} + +var jsonFieldsNameOfOrderPayResponse = [1]string{ + 0: "transaction_uuid", +} + +// Decode decodes OrderPayResponse from json. +func (s *OrderPayResponse) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode OrderPayResponse to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "transaction_uuid": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := json.DecodeUUID(d) + s.TransactionUUID = v + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"transaction_uuid\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode OrderPayResponse") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000001, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfOrderPayResponse) { + name = jsonFieldsNameOfOrderPayResponse[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *OrderPayResponse) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OrderPayResponse) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes OrderStatus as json. +func (s OrderStatus) Encode(e *jx.Encoder) { + e.Str(string(s)) +} + +// Decode decodes OrderStatus from json. +func (s *OrderStatus) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode OrderStatus to nil") + } + v, err := d.StrBytes() + if err != nil { + return err + } + // Try to use constant string. + switch OrderStatus(v) { + case OrderStatusSTATUSPENDINGPAYMENT: + *s = OrderStatusSTATUSPENDINGPAYMENT + case OrderStatusSTATUSPAID: + *s = OrderStatusSTATUSPAID + case OrderStatusSTATUSCANCELLED: + *s = OrderStatusSTATUSCANCELLED + default: + *s = OrderStatus(v) + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OrderStatus) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OrderStatus) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes OrderUUID as json. +func (s OrderUUID) Encode(e *jx.Encoder) { + unwrapped := uuid.UUID(s) + + json.EncodeUUID(e, unwrapped) +} + +// Decode decodes OrderUUID from json. +func (s *OrderUUID) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode OrderUUID to nil") + } + var unwrapped uuid.UUID + if err := func() error { + v, err := json.DecodeUUID(d) + unwrapped = v + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "alias") + } + *s = OrderUUID(unwrapped) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s OrderUUID) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *OrderUUID) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes PartUuids as json. +func (s PartUuids) Encode(e *jx.Encoder) { + unwrapped := []uuid.UUID(s) + + e.ArrStart() + for _, elem := range unwrapped { + json.EncodeUUID(e, elem) + } + e.ArrEnd() +} + +// Decode decodes PartUuids from json. +func (s *PartUuids) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode PartUuids to nil") + } + var unwrapped []uuid.UUID + if err := func() error { + unwrapped = make([]uuid.UUID, 0) + if err := d.Arr(func(d *jx.Decoder) error { + var elem uuid.UUID + v, err := json.DecodeUUID(d) + elem = v + if err != nil { + return err + } + unwrapped = append(unwrapped, elem) + return nil + }); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "alias") + } + *s = PartUuids(unwrapped) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s PartUuids) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *PartUuids) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes PaymentMethod as json. +func (s PaymentMethod) Encode(e *jx.Encoder) { + e.Str(string(s)) +} + +// Decode decodes PaymentMethod from json. +func (s *PaymentMethod) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode PaymentMethod to nil") + } + v, err := d.StrBytes() + if err != nil { + return err + } + // Try to use constant string. + switch PaymentMethod(v) { + case PaymentMethodPAYMENTMETHODUNSPECIFIED: + *s = PaymentMethodPAYMENTMETHODUNSPECIFIED + case PaymentMethodPAYMENTMETHODCARD: + *s = PaymentMethodPAYMENTMETHODCARD + case PaymentMethodPAYMENTMETHODSBP: + *s = PaymentMethodPAYMENTMETHODSBP + case PaymentMethodPAYMENTMETHODCREDITCARD: + *s = PaymentMethodPAYMENTMETHODCREDITCARD + case PaymentMethodPAYMENTMETHODINVESTORMONEY: + *s = PaymentMethodPAYMENTMETHODINVESTORMONEY + default: + *s = PaymentMethod(v) + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s PaymentMethod) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *PaymentMethod) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes TotalPriceMinor as json. +func (s TotalPriceMinor) Encode(e *jx.Encoder) { + unwrapped := int64(s) + + e.Int64(unwrapped) +} + +// Decode decodes TotalPriceMinor from json. +func (s *TotalPriceMinor) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode TotalPriceMinor to nil") + } + var unwrapped int64 + if err := func() error { + v, err := d.Int64() + unwrapped = int64(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "alias") + } + *s = TotalPriceMinor(unwrapped) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s TotalPriceMinor) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *TotalPriceMinor) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode encodes UserUUID as json. +func (s UserUUID) Encode(e *jx.Encoder) { + unwrapped := uuid.UUID(s) + + json.EncodeUUID(e, unwrapped) +} + +// Decode decodes UserUUID from json. +func (s *UserUUID) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode UserUUID to nil") + } + var unwrapped uuid.UUID + if err := func() error { + v, err := json.DecodeUUID(d) + unwrapped = v + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "alias") + } + *s = UserUUID(unwrapped) + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s UserUUID) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *UserUUID) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + +// Encode implements json.Marshaler. +func (s *ValidationError) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s *ValidationError) encodeFields(e *jx.Encoder) { + { + e.FieldStart("code") + e.Int(s.Code) + } + { + e.FieldStart("message") + e.Str(s.Message) + } +} + +var jsonFieldsNameOfValidationError = [2]string{ + 0: "code", + 1: "message", +} + +// Decode decodes ValidationError from json. +func (s *ValidationError) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode ValidationError to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "code": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := d.Int() + s.Code = int(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"code\"") + } + case "message": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + v, err := d.Str() + s.Message = string(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"message\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode ValidationError") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfValidationError) { + name = jsonFieldsNameOfValidationError[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s *ValidationError) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *ValidationError) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} diff --git a/shared/pkg/openapi/order/v1/oas_labeler_gen.go b/shared/pkg/openapi/order/v1/oas_labeler_gen.go new file mode 100644 index 0000000..2f92fa5 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_labeler_gen.go @@ -0,0 +1,42 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" +) + +// Labeler is used to allow adding custom attributes to the server request metrics. +type Labeler struct { + attrs []attribute.KeyValue +} + +// Add attributes to the Labeler. +func (l *Labeler) Add(attrs ...attribute.KeyValue) { + l.attrs = append(l.attrs, attrs...) +} + +// AttributeSet returns the attributes added to the Labeler as an attribute.Set. +func (l *Labeler) AttributeSet() attribute.Set { + return attribute.NewSet(l.attrs...) +} + +type labelerContextKey struct{} + +// LabelerFromContext retrieves the Labeler from the provided context, if present. +// +// If no Labeler was found in the provided context a new, empty Labeler is returned and the second +// return value is false. In this case it is safe to use the Labeler but any attributes added to +// it will not be used. +func LabelerFromContext(ctx context.Context) (*Labeler, bool) { + if l, ok := ctx.Value(labelerContextKey{}).(*Labeler); ok { + return l, true + } + return &Labeler{}, false +} + +func contextWithLabeler(ctx context.Context, l *Labeler) context.Context { + return context.WithValue(ctx, labelerContextKey{}, l) +} diff --git a/shared/pkg/openapi/order/v1/oas_middleware_gen.go b/shared/pkg/openapi/order/v1/oas_middleware_gen.go new file mode 100644 index 0000000..e16eb46 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_middleware_gen.go @@ -0,0 +1,10 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "github.com/ogen-go/ogen/middleware" +) + +// Middleware is middleware type. +type Middleware = middleware.Middleware diff --git a/shared/pkg/openapi/order/v1/oas_operations_gen.go b/shared/pkg/openapi/order/v1/oas_operations_gen.go new file mode 100644 index 0000000..13af493 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_operations_gen.go @@ -0,0 +1,13 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +// OperationName is the ogen operation name +type OperationName = string + +const ( + CancelOrderOperation OperationName = "CancelOrder" + CreateOrderOperation OperationName = "CreateOrder" + GetOrderByUuidOperation OperationName = "GetOrderByUuid" + PayOrderOperation OperationName = "PayOrder" +) diff --git a/shared/pkg/openapi/order/v1/oas_parameters_gen.go b/shared/pkg/openapi/order/v1/oas_parameters_gen.go new file mode 100644 index 0000000..97fc8ab --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_parameters_gen.go @@ -0,0 +1,214 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "net/http" + "net/url" + + "github.com/go-faster/errors" + "github.com/google/uuid" + "github.com/ogen-go/ogen/conv" + "github.com/ogen-go/ogen/middleware" + "github.com/ogen-go/ogen/ogenerrors" + "github.com/ogen-go/ogen/uri" + "github.com/ogen-go/ogen/validate" +) + +// CancelOrderParams is parameters of cancelOrder operation. +type CancelOrderParams struct { + // Уникальный идентификатор заказа. + OrderUUID uuid.UUID +} + +func unpackCancelOrderParams(packed middleware.Parameters) (params CancelOrderParams) { + { + key := middleware.ParameterKey{ + Name: "order_uuid", + In: "path", + } + params.OrderUUID = packed[key].(uuid.UUID) + } + return params +} + +func decodeCancelOrderParams(args [1]string, argsEscaped bool, r *http.Request) (params CancelOrderParams, _ error) { + // Decode path: order_uuid. + if err := func() error { + param := args[0] + if argsEscaped { + unescaped, err := url.PathUnescape(args[0]) + if err != nil { + return errors.Wrap(err, "unescape path") + } + param = unescaped + } + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "order_uuid", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToUUID(val) + if err != nil { + return err + } + + params.OrderUUID = c + return nil + }(); err != nil { + return err + } + } else { + return validate.ErrFieldRequired + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "order_uuid", + In: "path", + Err: err, + } + } + return params, nil +} + +// GetOrderByUuidParams is parameters of getOrderByUuid operation. +type GetOrderByUuidParams struct { + // Уникальный идентификатор заказа. + OrderUUID uuid.UUID +} + +func unpackGetOrderByUuidParams(packed middleware.Parameters) (params GetOrderByUuidParams) { + { + key := middleware.ParameterKey{ + Name: "order_uuid", + In: "path", + } + params.OrderUUID = packed[key].(uuid.UUID) + } + return params +} + +func decodeGetOrderByUuidParams(args [1]string, argsEscaped bool, r *http.Request) (params GetOrderByUuidParams, _ error) { + // Decode path: order_uuid. + if err := func() error { + param := args[0] + if argsEscaped { + unescaped, err := url.PathUnescape(args[0]) + if err != nil { + return errors.Wrap(err, "unescape path") + } + param = unescaped + } + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "order_uuid", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToUUID(val) + if err != nil { + return err + } + + params.OrderUUID = c + return nil + }(); err != nil { + return err + } + } else { + return validate.ErrFieldRequired + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "order_uuid", + In: "path", + Err: err, + } + } + return params, nil +} + +// PayOrderParams is parameters of payOrder operation. +type PayOrderParams struct { + // Уникальный идентификатор заказа. + OrderUUID uuid.UUID +} + +func unpackPayOrderParams(packed middleware.Parameters) (params PayOrderParams) { + { + key := middleware.ParameterKey{ + Name: "order_uuid", + In: "path", + } + params.OrderUUID = packed[key].(uuid.UUID) + } + return params +} + +func decodePayOrderParams(args [1]string, argsEscaped bool, r *http.Request) (params PayOrderParams, _ error) { + // Decode path: order_uuid. + if err := func() error { + param := args[0] + if argsEscaped { + unescaped, err := url.PathUnescape(args[0]) + if err != nil { + return errors.Wrap(err, "unescape path") + } + param = unescaped + } + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "order_uuid", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToUUID(val) + if err != nil { + return err + } + + params.OrderUUID = c + return nil + }(); err != nil { + return err + } + } else { + return validate.ErrFieldRequired + } + return nil + }(); err != nil { + return params, &ogenerrors.DecodeParamError{ + Name: "order_uuid", + In: "path", + Err: err, + } + } + return params, nil +} diff --git a/shared/pkg/openapi/order/v1/oas_request_decoders_gen.go b/shared/pkg/openapi/order/v1/oas_request_decoders_gen.go new file mode 100644 index 0000000..f82f78e --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_request_decoders_gen.go @@ -0,0 +1,173 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "bytes" + "io" + "mime" + "net/http" + + "github.com/go-faster/errors" + "github.com/go-faster/jx" + "github.com/ogen-go/ogen/ogenerrors" + "github.com/ogen-go/ogen/validate" +) + +func (s *Server) decodeCreateOrderRequest(r *http.Request) ( + req *OrderCreateRequest, + rawBody []byte, + close func() error, + rerr error, +) { + var closers []func() error + close = func() error { + var merr error + // Close in reverse order, to match defer behavior. + for i := len(closers) - 1; i >= 0; i-- { + c := closers[i] + merr = errors.Join(merr, c()) + } + return merr + } + defer func() { + if rerr != nil { + rerr = errors.Join(rerr, close()) + } + }() + ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err != nil { + return req, rawBody, close, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + if r.ContentLength == 0 { + return req, rawBody, close, validate.ErrBodyRequired + } + buf, err := io.ReadAll(r.Body) + defer func() { + _ = r.Body.Close() + }() + if err != nil { + return req, rawBody, close, err + } + + // Reset the body to allow for downstream reading. + r.Body = io.NopCloser(bytes.NewBuffer(buf)) + + if len(buf) == 0 { + return req, rawBody, close, validate.ErrBodyRequired + } + + rawBody = append(rawBody, buf...) + d := jx.DecodeBytes(buf) + + var request OrderCreateRequest + if err := func() error { + if err := request.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return req, rawBody, close, err + } + if err := func() error { + if err := request.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return req, rawBody, close, errors.Wrap(err, "validate") + } + return &request, rawBody, close, nil + default: + return req, rawBody, close, validate.InvalidContentType(ct) + } +} + +func (s *Server) decodePayOrderRequest(r *http.Request) ( + req *OrderPayRequest, + rawBody []byte, + close func() error, + rerr error, +) { + var closers []func() error + close = func() error { + var merr error + // Close in reverse order, to match defer behavior. + for i := len(closers) - 1; i >= 0; i-- { + c := closers[i] + merr = errors.Join(merr, c()) + } + return merr + } + defer func() { + if rerr != nil { + rerr = errors.Join(rerr, close()) + } + }() + ct, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) + if err != nil { + return req, rawBody, close, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + if r.ContentLength == 0 { + return req, rawBody, close, validate.ErrBodyRequired + } + buf, err := io.ReadAll(r.Body) + defer func() { + _ = r.Body.Close() + }() + if err != nil { + return req, rawBody, close, err + } + + // Reset the body to allow for downstream reading. + r.Body = io.NopCloser(bytes.NewBuffer(buf)) + + if len(buf) == 0 { + return req, rawBody, close, validate.ErrBodyRequired + } + + rawBody = append(rawBody, buf...) + d := jx.DecodeBytes(buf) + + var request OrderPayRequest + if err := func() error { + if err := request.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return req, rawBody, close, err + } + if err := func() error { + if err := request.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return req, rawBody, close, errors.Wrap(err, "validate") + } + return &request, rawBody, close, nil + default: + return req, rawBody, close, validate.InvalidContentType(ct) + } +} diff --git a/shared/pkg/openapi/order/v1/oas_request_encoders_gen.go b/shared/pkg/openapi/order/v1/oas_request_encoders_gen.go new file mode 100644 index 0000000..3b29ba2 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_request_encoders_gen.go @@ -0,0 +1,39 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "bytes" + "net/http" + + "github.com/go-faster/jx" + ht "github.com/ogen-go/ogen/http" +) + +func encodeCreateOrderRequest( + req *OrderCreateRequest, + r *http.Request, +) error { + const contentType = "application/json" + e := new(jx.Encoder) + { + req.Encode(e) + } + encoded := e.Bytes() + ht.SetBody(r, bytes.NewReader(encoded), contentType) + return nil +} + +func encodePayOrderRequest( + req *OrderPayRequest, + r *http.Request, +) error { + const contentType = "application/json" + e := new(jx.Encoder) + { + req.Encode(e) + } + encoded := e.Bytes() + ht.SetBody(r, bytes.NewReader(encoded), contentType) + return nil +} diff --git a/shared/pkg/openapi/order/v1/oas_response_decoders_gen.go b/shared/pkg/openapi/order/v1/oas_response_decoders_gen.go new file mode 100644 index 0000000..7b45a5d --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_response_decoders_gen.go @@ -0,0 +1,749 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "io" + "mime" + "net/http" + + "github.com/go-faster/errors" + "github.com/go-faster/jx" + "github.com/ogen-go/ogen/ogenerrors" + "github.com/ogen-go/ogen/validate" +) + +func decodeCancelOrderResponse(resp *http.Response) (res CancelOrderRes, _ error) { + switch resp.StatusCode { + case 200: + // Code 200. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response Order + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 404: + // Code 404. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response NotFoundError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 409: + // Code 409. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response ConflictError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + } + // Convenient error response. + defRes, err := func() (res *GenericErrorStatusCode, err error) { + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response GenericError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &GenericErrorStatusCode{ + StatusCode: resp.StatusCode, + Response: response, + }, nil + default: + return res, validate.InvalidContentType(ct) + } + }() + if err != nil { + return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) + } + return res, errors.Wrap(defRes, "error") +} + +func decodeCreateOrderResponse(resp *http.Response) (res CreateOrderRes, _ error) { + switch resp.StatusCode { + case 201: + // Code 201. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response OrderCreateResponse + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 422: + // Code 422. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response ValidationError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 502: + // Code 502. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response BadGatewayError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + } + // Convenient error response. + defRes, err := func() (res *GenericErrorStatusCode, err error) { + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response GenericError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &GenericErrorStatusCode{ + StatusCode: resp.StatusCode, + Response: response, + }, nil + default: + return res, validate.InvalidContentType(ct) + } + }() + if err != nil { + return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) + } + return res, errors.Wrap(defRes, "error") +} + +func decodeGetOrderByUuidResponse(resp *http.Response) (res GetOrderByUuidRes, _ error) { + switch resp.StatusCode { + case 200: + // Code 200. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response Order + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + // Validate response. + if err := func() error { + if err := response.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "validate") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 404: + // Code 404. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response NotFoundError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 422: + // Code 422. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response ValidationError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 502: + // Code 502. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response BadGatewayError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + } + // Convenient error response. + defRes, err := func() (res *GenericErrorStatusCode, err error) { + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response GenericError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &GenericErrorStatusCode{ + StatusCode: resp.StatusCode, + Response: response, + }, nil + default: + return res, validate.InvalidContentType(ct) + } + }() + if err != nil { + return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) + } + return res, errors.Wrap(defRes, "error") +} + +func decodePayOrderResponse(resp *http.Response) (res PayOrderRes, _ error) { + switch resp.StatusCode { + case 200: + // Code 200. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response OrderPayResponse + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 404: + // Code 404. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response NotFoundError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 409: + // Code 409. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response ConflictError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 422: + // Code 422. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response ValidationError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 502: + // Code 502. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response BadGatewayError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + } + // Convenient error response. + defRes, err := func() (res *GenericErrorStatusCode, err error) { + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + buf, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + d := jx.DecodeBytes(buf) + + var response GenericError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + if err := d.Skip(); err != io.EOF { + return errors.New("unexpected trailing data") + } + return nil + }(); err != nil { + err = &ogenerrors.DecodeBodyError{ + ContentType: ct, + Body: buf, + Err: err, + } + return res, err + } + return &GenericErrorStatusCode{ + StatusCode: resp.StatusCode, + Response: response, + }, nil + default: + return res, validate.InvalidContentType(ct) + } + }() + if err != nil { + return res, errors.Wrapf(err, "default (code %d)", resp.StatusCode) + } + return res, errors.Wrap(defRes, "error") +} diff --git a/shared/pkg/openapi/order/v1/oas_response_encoders_gen.go b/shared/pkg/openapi/order/v1/oas_response_encoders_gen.go new file mode 100644 index 0000000..e3d7bb9 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_response_encoders_gen.go @@ -0,0 +1,263 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "net/http" + + "github.com/go-faster/errors" + "github.com/go-faster/jx" + ht "github.com/ogen-go/ogen/http" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +func encodeCancelOrderResponse(response CancelOrderRes, w http.ResponseWriter, span trace.Span) error { + switch response := response.(type) { + case *Order: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + span.SetStatus(codes.Ok, http.StatusText(200)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *NotFoundError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(404) + span.SetStatus(codes.Error, http.StatusText(404)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *ConflictError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(409) + span.SetStatus(codes.Error, http.StatusText(409)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + default: + return errors.Errorf("unexpected response type: %T", response) + } +} + +func encodeCreateOrderResponse(response CreateOrderRes, w http.ResponseWriter, span trace.Span) error { + switch response := response.(type) { + case *OrderCreateResponse: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(201) + span.SetStatus(codes.Ok, http.StatusText(201)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *ValidationError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(422) + span.SetStatus(codes.Error, http.StatusText(422)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *BadGatewayError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(502) + span.SetStatus(codes.Error, http.StatusText(502)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + default: + return errors.Errorf("unexpected response type: %T", response) + } +} + +func encodeGetOrderByUuidResponse(response GetOrderByUuidRes, w http.ResponseWriter, span trace.Span) error { + switch response := response.(type) { + case *Order: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + span.SetStatus(codes.Ok, http.StatusText(200)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *NotFoundError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(404) + span.SetStatus(codes.Error, http.StatusText(404)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *ValidationError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(422) + span.SetStatus(codes.Error, http.StatusText(422)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *BadGatewayError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(502) + span.SetStatus(codes.Error, http.StatusText(502)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + default: + return errors.Errorf("unexpected response type: %T", response) + } +} + +func encodePayOrderResponse(response PayOrderRes, w http.ResponseWriter, span trace.Span) error { + switch response := response.(type) { + case *OrderPayResponse: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(200) + span.SetStatus(codes.Ok, http.StatusText(200)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *NotFoundError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(404) + span.SetStatus(codes.Error, http.StatusText(404)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *ConflictError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(409) + span.SetStatus(codes.Error, http.StatusText(409)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *ValidationError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(422) + span.SetStatus(codes.Error, http.StatusText(422)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + case *BadGatewayError: + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(502) + span.SetStatus(codes.Error, http.StatusText(502)) + + e := new(jx.Encoder) + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + return nil + + default: + return errors.Errorf("unexpected response type: %T", response) + } +} + +func encodeErrorResponse(response *GenericErrorStatusCode, w http.ResponseWriter, span trace.Span) error { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + code := response.StatusCode + if code == 0 { + // Set default status code. + code = http.StatusOK + } + w.WriteHeader(code) + if st := http.StatusText(code); code >= http.StatusBadRequest { + span.SetStatus(codes.Error, st) + } else { + span.SetStatus(codes.Ok, st) + } + + e := new(jx.Encoder) + response.Response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + + if code >= http.StatusInternalServerError { + return errors.Wrapf(ht.ErrInternalServerErrorResponse, "code: %d, message: %s", code, http.StatusText(code)) + } + return nil + +} diff --git a/shared/pkg/openapi/order/v1/oas_router_gen.go b/shared/pkg/openapi/order/v1/oas_router_gen.go new file mode 100644 index 0000000..8fddd48 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_router_gen.go @@ -0,0 +1,377 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "net/http" + "net/url" + "strings" + + "github.com/ogen-go/ogen/uri" +) + +func (s *Server) cutPrefix(path string) (string, bool) { + prefix := s.cfg.Prefix + if prefix == "" { + return path, true + } + if !strings.HasPrefix(path, prefix) { + // Prefix doesn't match. + return "", false + } + // Cut prefix from the path. + return strings.TrimPrefix(path, prefix), true +} + +// ServeHTTP serves http request as defined by OpenAPI v3 specification, +// calling handler that matches the path or returning not found error. +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + elem := r.URL.Path + elemIsEscaped := false + if rawPath := r.URL.RawPath; rawPath != "" { + if normalized, ok := uri.NormalizeEscapedPath(rawPath); ok { + elem = normalized + elemIsEscaped = strings.ContainsRune(elem, '%') + } + } + + elem, ok := s.cutPrefix(elem) + if !ok || len(elem) == 0 { + s.notFound(w, r) + return + } + args := [1]string{} + + // Static code generated router with unwrapped path search. + switch { + default: + if len(elem) == 0 { + break + } + switch elem[0] { + case '/': // Prefix: "/api/v1/orders" + + if l := len("/api/v1/orders"); len(elem) >= l && elem[0:l] == "/api/v1/orders" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + switch r.Method { + case "POST": + s.handleCreateOrderRequest([0]string{}, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + switch elem[0] { + case '/': // Prefix: "/" + + if l := len("/"); len(elem) >= l && elem[0:l] == "/" { + elem = elem[l:] + } else { + break + } + + // Param: "order_uuid" + // Match until "/" + idx := strings.IndexByte(elem, '/') + if idx < 0 { + idx = len(elem) + } + args[0] = elem[:idx] + elem = elem[idx:] + + if len(elem) == 0 { + switch r.Method { + case "GET": + s.handleGetOrderByUuidRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "GET") + } + + return + } + switch elem[0] { + case '/': // Prefix: "/" + + if l := len("/"); len(elem) >= l && elem[0:l] == "/" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 'c': // Prefix: "cancel" + + if l := len("cancel"); len(elem) >= l && elem[0:l] == "cancel" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handleCancelOrderRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + case 'p': // Prefix: "pay" + + if l := len("pay"); len(elem) >= l && elem[0:l] == "pay" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "POST": + s.handlePayOrderRequest([1]string{ + args[0], + }, elemIsEscaped, w, r) + default: + s.notAllowed(w, r, "POST") + } + + return + } + + } + + } + + } + + } + } + s.notFound(w, r) +} + +// Route is route object. +type Route struct { + name string + summary string + operationID string + operationGroup string + pathPattern string + count int + args [1]string +} + +// Name returns ogen operation name. +// +// It is guaranteed to be unique and not empty. +func (r Route) Name() string { + return r.name +} + +// Summary returns OpenAPI summary. +func (r Route) Summary() string { + return r.summary +} + +// OperationID returns OpenAPI operationId. +func (r Route) OperationID() string { + return r.operationID +} + +// OperationGroup returns the x-ogen-operation-group value. +func (r Route) OperationGroup() string { + return r.operationGroup +} + +// PathPattern returns OpenAPI path. +func (r Route) PathPattern() string { + return r.pathPattern +} + +// Args returns parsed arguments. +func (r Route) Args() []string { + return r.args[:r.count] +} + +// FindRoute finds Route for given method and path. +// +// Note: this method does not unescape path or handle reserved characters in path properly. Use FindPath instead. +func (s *Server) FindRoute(method, path string) (Route, bool) { + return s.FindPath(method, &url.URL{Path: path}) +} + +// FindPath finds Route for given method and URL. +func (s *Server) FindPath(method string, u *url.URL) (r Route, _ bool) { + var ( + elem = u.Path + args = r.args + ) + if rawPath := u.RawPath; rawPath != "" { + if normalized, ok := uri.NormalizeEscapedPath(rawPath); ok { + elem = normalized + } + defer func() { + for i, arg := range r.args[:r.count] { + if unescaped, err := url.PathUnescape(arg); err == nil { + r.args[i] = unescaped + } + } + }() + } + + elem, ok := s.cutPrefix(elem) + if !ok { + return r, false + } + + // Static code generated router with unwrapped path search. + switch { + default: + if len(elem) == 0 { + break + } + switch elem[0] { + case '/': // Prefix: "/api/v1/orders" + + if l := len("/api/v1/orders"); len(elem) >= l && elem[0:l] == "/api/v1/orders" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + switch method { + case "POST": + r.name = CreateOrderOperation + r.summary = "Create a new order" + r.operationID = "createOrder" + r.operationGroup = "" + r.pathPattern = "/api/v1/orders" + r.args = args + r.count = 0 + return r, true + default: + return + } + } + switch elem[0] { + case '/': // Prefix: "/" + + if l := len("/"); len(elem) >= l && elem[0:l] == "/" { + elem = elem[l:] + } else { + break + } + + // Param: "order_uuid" + // Match until "/" + idx := strings.IndexByte(elem, '/') + if idx < 0 { + idx = len(elem) + } + args[0] = elem[:idx] + elem = elem[idx:] + + if len(elem) == 0 { + switch method { + case "GET": + r.name = GetOrderByUuidOperation + r.summary = "Get order by UUID" + r.operationID = "getOrderByUuid" + r.operationGroup = "" + r.pathPattern = "/api/v1/orders/{order_uuid}" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + switch elem[0] { + case '/': // Prefix: "/" + + if l := len("/"); len(elem) >= l && elem[0:l] == "/" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + break + } + switch elem[0] { + case 'c': // Prefix: "cancel" + + if l := len("cancel"); len(elem) >= l && elem[0:l] == "cancel" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = CancelOrderOperation + r.summary = "Cancel an order" + r.operationID = "cancelOrder" + r.operationGroup = "" + r.pathPattern = "/api/v1/orders/{order_uuid}/cancel" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + case 'p': // Prefix: "pay" + + if l := len("pay"); len(elem) >= l && elem[0:l] == "pay" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch method { + case "POST": + r.name = PayOrderOperation + r.summary = "Pay for an order" + r.operationID = "payOrder" + r.operationGroup = "" + r.pathPattern = "/api/v1/orders/{order_uuid}/pay" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + + } + + } + + } + + } + } + return r, false +} diff --git a/shared/pkg/openapi/order/v1/oas_schemas_gen.go b/shared/pkg/openapi/order/v1/oas_schemas_gen.go new file mode 100644 index 0000000..605071e --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_schemas_gen.go @@ -0,0 +1,677 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "fmt" + + "github.com/go-faster/errors" + "github.com/google/uuid" +) + +func (s *GenericErrorStatusCode) Error() string { + return fmt.Sprintf("code %d: %+v", s.StatusCode, s.Response) +} + +// Ref: # +type BadGatewayError struct { + // HTTP-код ошибки. + Code int `json:"code"` + // Описание ошибки. + Message string `json:"message"` +} + +// GetCode returns the value of Code. +func (s *BadGatewayError) GetCode() int { + return s.Code +} + +// GetMessage returns the value of Message. +func (s *BadGatewayError) GetMessage() string { + return s.Message +} + +// SetCode sets the value of Code. +func (s *BadGatewayError) SetCode(val int) { + s.Code = val +} + +// SetMessage sets the value of Message. +func (s *BadGatewayError) SetMessage(val string) { + s.Message = val +} + +func (*BadGatewayError) createOrderRes() {} +func (*BadGatewayError) getOrderByUuidRes() {} +func (*BadGatewayError) payOrderRes() {} + +// Ref: # +type ConflictError struct { + // HTTP-код ошибки. + Code int `json:"code"` + // Описание ошибки. + Message string `json:"message"` +} + +// GetCode returns the value of Code. +func (s *ConflictError) GetCode() int { + return s.Code +} + +// GetMessage returns the value of Message. +func (s *ConflictError) GetMessage() string { + return s.Message +} + +// SetCode sets the value of Code. +func (s *ConflictError) SetCode(val int) { + s.Code = val +} + +// SetMessage sets the value of Message. +func (s *ConflictError) SetMessage(val string) { + s.Message = val +} + +func (*ConflictError) cancelOrderRes() {} +func (*ConflictError) payOrderRes() {} + +// Ref: # +type GenericError struct { + // HTTP-код ошибки. + Code OptInt `json:"code"` + // Описание ошибки. + Message OptString `json:"message"` +} + +// GetCode returns the value of Code. +func (s *GenericError) GetCode() OptInt { + return s.Code +} + +// GetMessage returns the value of Message. +func (s *GenericError) GetMessage() OptString { + return s.Message +} + +// SetCode sets the value of Code. +func (s *GenericError) SetCode(val OptInt) { + s.Code = val +} + +// SetMessage sets the value of Message. +func (s *GenericError) SetMessage(val OptString) { + s.Message = val +} + +// GenericErrorStatusCode wraps GenericError with StatusCode. +type GenericErrorStatusCode struct { + StatusCode int + Response GenericError +} + +// GetStatusCode returns the value of StatusCode. +func (s *GenericErrorStatusCode) GetStatusCode() int { + return s.StatusCode +} + +// GetResponse returns the value of Response. +func (s *GenericErrorStatusCode) GetResponse() GenericError { + return s.Response +} + +// SetStatusCode sets the value of StatusCode. +func (s *GenericErrorStatusCode) SetStatusCode(val int) { + s.StatusCode = val +} + +// SetResponse sets the value of Response. +func (s *GenericErrorStatusCode) SetResponse(val GenericError) { + s.Response = val +} + +// Ref: # +type NotFoundError struct { + // HTTP-код ошибки. + Code int `json:"code"` + // Описание ошибки. + Message string `json:"message"` +} + +// GetCode returns the value of Code. +func (s *NotFoundError) GetCode() int { + return s.Code +} + +// GetMessage returns the value of Message. +func (s *NotFoundError) GetMessage() string { + return s.Message +} + +// SetCode sets the value of Code. +func (s *NotFoundError) SetCode(val int) { + s.Code = val +} + +// SetMessage sets the value of Message. +func (s *NotFoundError) SetMessage(val string) { + s.Message = val +} + +func (*NotFoundError) cancelOrderRes() {} +func (*NotFoundError) getOrderByUuidRes() {} +func (*NotFoundError) payOrderRes() {} + +// NewOptInt returns new OptInt with value set to v. +func NewOptInt(v int) OptInt { + return OptInt{ + Value: v, + Set: true, + } +} + +// OptInt is optional int. +type OptInt struct { + Value int + Set bool +} + +// IsSet returns true if OptInt was set. +func (o OptInt) IsSet() bool { return o.Set } + +// Reset unsets value. +func (o *OptInt) Reset() { + var v int + o.Value = v + o.Set = false +} + +// SetTo sets value to v. +func (o *OptInt) SetTo(v int) { + o.Set = true + o.Value = v +} + +// Get returns value and boolean that denotes whether value was set. +func (o OptInt) Get() (v int, ok bool) { + if !o.Set { + return v, false + } + return o.Value, true +} + +// Or returns value if set, or given parameter if does not. +func (o OptInt) Or(d int) int { + if v, ok := o.Get(); ok { + return v + } + return d +} + +// NewOptPaymentMethod returns new OptPaymentMethod with value set to v. +func NewOptPaymentMethod(v PaymentMethod) OptPaymentMethod { + return OptPaymentMethod{ + Value: v, + Set: true, + } +} + +// OptPaymentMethod is optional PaymentMethod. +type OptPaymentMethod struct { + Value PaymentMethod + Set bool +} + +// IsSet returns true if OptPaymentMethod was set. +func (o OptPaymentMethod) IsSet() bool { return o.Set } + +// Reset unsets value. +func (o *OptPaymentMethod) Reset() { + var v PaymentMethod + o.Value = v + o.Set = false +} + +// SetTo sets value to v. +func (o *OptPaymentMethod) SetTo(v PaymentMethod) { + o.Set = true + o.Value = v +} + +// Get returns value and boolean that denotes whether value was set. +func (o OptPaymentMethod) Get() (v PaymentMethod, ok bool) { + if !o.Set { + return v, false + } + return o.Value, true +} + +// Or returns value if set, or given parameter if does not. +func (o OptPaymentMethod) Or(d PaymentMethod) PaymentMethod { + if v, ok := o.Get(); ok { + return v + } + return d +} + +// NewOptString returns new OptString with value set to v. +func NewOptString(v string) OptString { + return OptString{ + Value: v, + Set: true, + } +} + +// OptString is optional string. +type OptString struct { + Value string + Set bool +} + +// IsSet returns true if OptString was set. +func (o OptString) IsSet() bool { return o.Set } + +// Reset unsets value. +func (o *OptString) Reset() { + var v string + o.Value = v + o.Set = false +} + +// SetTo sets value to v. +func (o *OptString) SetTo(v string) { + o.Set = true + o.Value = v +} + +// Get returns value and boolean that denotes whether value was set. +func (o OptString) Get() (v string, ok bool) { + if !o.Set { + return v, false + } + return o.Value, true +} + +// Or returns value if set, or given parameter if does not. +func (o OptString) Or(d string) string { + if v, ok := o.Get(); ok { + return v + } + return d +} + +// NewOptUUID returns new OptUUID with value set to v. +func NewOptUUID(v uuid.UUID) OptUUID { + return OptUUID{ + Value: v, + Set: true, + } +} + +// OptUUID is optional uuid.UUID. +type OptUUID struct { + Value uuid.UUID + Set bool +} + +// IsSet returns true if OptUUID was set. +func (o OptUUID) IsSet() bool { return o.Set } + +// Reset unsets value. +func (o *OptUUID) Reset() { + var v uuid.UUID + o.Value = v + o.Set = false +} + +// SetTo sets value to v. +func (o *OptUUID) SetTo(v uuid.UUID) { + o.Set = true + o.Value = v +} + +// Get returns value and boolean that denotes whether value was set. +func (o OptUUID) Get() (v uuid.UUID, ok bool) { + if !o.Set { + return v, false + } + return o.Value, true +} + +// Or returns value if set, or given parameter if does not. +func (o OptUUID) Or(d uuid.UUID) uuid.UUID { + if v, ok := o.Get(); ok { + return v + } + return d +} + +// Ref: # +type Order struct { + // UUID заказа. + OrderUUID uuid.UUID `json:"order_uuid"` + // UUID пользователя. + UserUUID uuid.UUID `json:"user_uuid"` + // Список UUID деталей. + PartUuids []uuid.UUID `json:"part_uuids"` + // Сумма заказа в копейках. + TotalPriceMinor int64 `json:"total_price_minor"` + // UUID транзакции. + TransactionUUID OptUUID `json:"transaction_uuid"` + PaymentMethod OptPaymentMethod `json:"payment_method"` + Status OrderStatus `json:"status"` +} + +// GetOrderUUID returns the value of OrderUUID. +func (s *Order) GetOrderUUID() uuid.UUID { + return s.OrderUUID +} + +// GetUserUUID returns the value of UserUUID. +func (s *Order) GetUserUUID() uuid.UUID { + return s.UserUUID +} + +// GetPartUuids returns the value of PartUuids. +func (s *Order) GetPartUuids() []uuid.UUID { + return s.PartUuids +} + +// GetTotalPriceMinor returns the value of TotalPriceMinor. +func (s *Order) GetTotalPriceMinor() int64 { + return s.TotalPriceMinor +} + +// GetTransactionUUID returns the value of TransactionUUID. +func (s *Order) GetTransactionUUID() OptUUID { + return s.TransactionUUID +} + +// GetPaymentMethod returns the value of PaymentMethod. +func (s *Order) GetPaymentMethod() OptPaymentMethod { + return s.PaymentMethod +} + +// GetStatus returns the value of Status. +func (s *Order) GetStatus() OrderStatus { + return s.Status +} + +// SetOrderUUID sets the value of OrderUUID. +func (s *Order) SetOrderUUID(val uuid.UUID) { + s.OrderUUID = val +} + +// SetUserUUID sets the value of UserUUID. +func (s *Order) SetUserUUID(val uuid.UUID) { + s.UserUUID = val +} + +// SetPartUuids sets the value of PartUuids. +func (s *Order) SetPartUuids(val []uuid.UUID) { + s.PartUuids = val +} + +// SetTotalPriceMinor sets the value of TotalPriceMinor. +func (s *Order) SetTotalPriceMinor(val int64) { + s.TotalPriceMinor = val +} + +// SetTransactionUUID sets the value of TransactionUUID. +func (s *Order) SetTransactionUUID(val OptUUID) { + s.TransactionUUID = val +} + +// SetPaymentMethod sets the value of PaymentMethod. +func (s *Order) SetPaymentMethod(val OptPaymentMethod) { + s.PaymentMethod = val +} + +// SetStatus sets the value of Status. +func (s *Order) SetStatus(val OrderStatus) { + s.Status = val +} + +func (*Order) cancelOrderRes() {} +func (*Order) getOrderByUuidRes() {} + +// Ref: # +type OrderCreateRequest struct { + UserUUID UserUUID `json:"user_uuid"` + PartUuids PartUuids `json:"part_uuids"` +} + +// GetUserUUID returns the value of UserUUID. +func (s *OrderCreateRequest) GetUserUUID() UserUUID { + return s.UserUUID +} + +// GetPartUuids returns the value of PartUuids. +func (s *OrderCreateRequest) GetPartUuids() PartUuids { + return s.PartUuids +} + +// SetUserUUID sets the value of UserUUID. +func (s *OrderCreateRequest) SetUserUUID(val UserUUID) { + s.UserUUID = val +} + +// SetPartUuids sets the value of PartUuids. +func (s *OrderCreateRequest) SetPartUuids(val PartUuids) { + s.PartUuids = val +} + +// Ref: # +type OrderCreateResponse struct { + OrderUUID OrderUUID `json:"order_uuid"` + TotalPriceMinor TotalPriceMinor `json:"total_price_minor"` +} + +// GetOrderUUID returns the value of OrderUUID. +func (s *OrderCreateResponse) GetOrderUUID() OrderUUID { + return s.OrderUUID +} + +// GetTotalPriceMinor returns the value of TotalPriceMinor. +func (s *OrderCreateResponse) GetTotalPriceMinor() TotalPriceMinor { + return s.TotalPriceMinor +} + +// SetOrderUUID sets the value of OrderUUID. +func (s *OrderCreateResponse) SetOrderUUID(val OrderUUID) { + s.OrderUUID = val +} + +// SetTotalPriceMinor sets the value of TotalPriceMinor. +func (s *OrderCreateResponse) SetTotalPriceMinor(val TotalPriceMinor) { + s.TotalPriceMinor = val +} + +func (*OrderCreateResponse) createOrderRes() {} + +// Ref: # +type OrderPayRequest struct { + PaymentMethod PaymentMethod `json:"payment_method"` +} + +// GetPaymentMethod returns the value of PaymentMethod. +func (s *OrderPayRequest) GetPaymentMethod() PaymentMethod { + return s.PaymentMethod +} + +// SetPaymentMethod sets the value of PaymentMethod. +func (s *OrderPayRequest) SetPaymentMethod(val PaymentMethod) { + s.PaymentMethod = val +} + +// Ref: # +type OrderPayResponse struct { + // UUID транзакции. + TransactionUUID uuid.UUID `json:"transaction_uuid"` +} + +// GetTransactionUUID returns the value of TransactionUUID. +func (s *OrderPayResponse) GetTransactionUUID() uuid.UUID { + return s.TransactionUUID +} + +// SetTransactionUUID sets the value of TransactionUUID. +func (s *OrderPayResponse) SetTransactionUUID(val uuid.UUID) { + s.TransactionUUID = val +} + +func (*OrderPayResponse) payOrderRes() {} + +// Статус заказа. +// Ref: # +type OrderStatus string + +const ( + OrderStatusSTATUSPENDINGPAYMENT OrderStatus = "STATUS_PENDING_PAYMENT" + OrderStatusSTATUSPAID OrderStatus = "STATUS_PAID" + OrderStatusSTATUSCANCELLED OrderStatus = "STATUS_CANCELLED" +) + +// AllValues returns all OrderStatus values. +func (OrderStatus) AllValues() []OrderStatus { + return []OrderStatus{ + OrderStatusSTATUSPENDINGPAYMENT, + OrderStatusSTATUSPAID, + OrderStatusSTATUSCANCELLED, + } +} + +// MarshalText implements encoding.TextMarshaler. +func (s OrderStatus) MarshalText() ([]byte, error) { + switch s { + case OrderStatusSTATUSPENDINGPAYMENT: + return []byte(s), nil + case OrderStatusSTATUSPAID: + return []byte(s), nil + case OrderStatusSTATUSCANCELLED: + return []byte(s), nil + default: + return nil, errors.Errorf("invalid value: %q", s) + } +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (s *OrderStatus) UnmarshalText(data []byte) error { + switch OrderStatus(data) { + case OrderStatusSTATUSPENDINGPAYMENT: + *s = OrderStatusSTATUSPENDINGPAYMENT + return nil + case OrderStatusSTATUSPAID: + *s = OrderStatusSTATUSPAID + return nil + case OrderStatusSTATUSCANCELLED: + *s = OrderStatusSTATUSCANCELLED + return nil + default: + return errors.Errorf("invalid value: %q", data) + } +} + +type OrderUUID uuid.UUID + +type PartUuids []uuid.UUID + +// Способ оплаты. +// Ref: # +type PaymentMethod string + +const ( + PaymentMethodPAYMENTMETHODUNSPECIFIED PaymentMethod = "PAYMENT_METHOD_UNSPECIFIED" + PaymentMethodPAYMENTMETHODCARD PaymentMethod = "PAYMENT_METHOD_CARD" + PaymentMethodPAYMENTMETHODSBP PaymentMethod = "PAYMENT_METHOD_SBP" + PaymentMethodPAYMENTMETHODCREDITCARD PaymentMethod = "PAYMENT_METHOD_CREDIT_CARD" + PaymentMethodPAYMENTMETHODINVESTORMONEY PaymentMethod = "PAYMENT_METHOD_INVESTOR_MONEY" +) + +// AllValues returns all PaymentMethod values. +func (PaymentMethod) AllValues() []PaymentMethod { + return []PaymentMethod{ + PaymentMethodPAYMENTMETHODUNSPECIFIED, + PaymentMethodPAYMENTMETHODCARD, + PaymentMethodPAYMENTMETHODSBP, + PaymentMethodPAYMENTMETHODCREDITCARD, + PaymentMethodPAYMENTMETHODINVESTORMONEY, + } +} + +// MarshalText implements encoding.TextMarshaler. +func (s PaymentMethod) MarshalText() ([]byte, error) { + switch s { + case PaymentMethodPAYMENTMETHODUNSPECIFIED: + return []byte(s), nil + case PaymentMethodPAYMENTMETHODCARD: + return []byte(s), nil + case PaymentMethodPAYMENTMETHODSBP: + return []byte(s), nil + case PaymentMethodPAYMENTMETHODCREDITCARD: + return []byte(s), nil + case PaymentMethodPAYMENTMETHODINVESTORMONEY: + return []byte(s), nil + default: + return nil, errors.Errorf("invalid value: %q", s) + } +} + +// UnmarshalText implements encoding.TextUnmarshaler. +func (s *PaymentMethod) UnmarshalText(data []byte) error { + switch PaymentMethod(data) { + case PaymentMethodPAYMENTMETHODUNSPECIFIED: + *s = PaymentMethodPAYMENTMETHODUNSPECIFIED + return nil + case PaymentMethodPAYMENTMETHODCARD: + *s = PaymentMethodPAYMENTMETHODCARD + return nil + case PaymentMethodPAYMENTMETHODSBP: + *s = PaymentMethodPAYMENTMETHODSBP + return nil + case PaymentMethodPAYMENTMETHODCREDITCARD: + *s = PaymentMethodPAYMENTMETHODCREDITCARD + return nil + case PaymentMethodPAYMENTMETHODINVESTORMONEY: + *s = PaymentMethodPAYMENTMETHODINVESTORMONEY + return nil + default: + return errors.Errorf("invalid value: %q", data) + } +} + +type TotalPriceMinor int64 + +type UserUUID uuid.UUID + +// Ref: # +type ValidationError struct { + // HTTP-код ошибки. + Code int `json:"code"` + // Описание ошибки. + Message string `json:"message"` +} + +// GetCode returns the value of Code. +func (s *ValidationError) GetCode() int { + return s.Code +} + +// GetMessage returns the value of Message. +func (s *ValidationError) GetMessage() string { + return s.Message +} + +// SetCode sets the value of Code. +func (s *ValidationError) SetCode(val int) { + s.Code = val +} + +// SetMessage sets the value of Message. +func (s *ValidationError) SetMessage(val string) { + s.Message = val +} + +func (*ValidationError) createOrderRes() {} +func (*ValidationError) getOrderByUuidRes() {} +func (*ValidationError) payOrderRes() {} diff --git a/shared/pkg/openapi/order/v1/oas_server_gen.go b/shared/pkg/openapi/order/v1/oas_server_gen.go new file mode 100644 index 0000000..ffc88e9 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_server_gen.go @@ -0,0 +1,58 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "context" +) + +// Handler handles operations described by OpenAPI v3 specification. +type Handler interface { + // CancelOrder implements cancelOrder operation. + // + // Cancels an existing order. + // + // POST /api/v1/orders/{order_uuid}/cancel + CancelOrder(ctx context.Context, params CancelOrderParams) (CancelOrderRes, error) + // CreateOrder implements createOrder operation. + // + // Creates a new order. + // + // POST /api/v1/orders + CreateOrder(ctx context.Context, req *OrderCreateRequest) (CreateOrderRes, error) + // GetOrderByUuid implements getOrderByUuid operation. + // + // Retrieves order details by UUID. + // + // GET /api/v1/orders/{order_uuid} + GetOrderByUuid(ctx context.Context, params GetOrderByUuidParams) (GetOrderByUuidRes, error) + // PayOrder implements payOrder operation. + // + // Processes payment for an existing order. + // + // POST /api/v1/orders/{order_uuid}/pay + PayOrder(ctx context.Context, req *OrderPayRequest, params PayOrderParams) (PayOrderRes, error) + // NewError creates *GenericErrorStatusCode from error returned by handler. + // + // Used for common default response. + NewError(ctx context.Context, err error) *GenericErrorStatusCode +} + +// Server implements http server based on OpenAPI v3 specification and +// calls Handler to handle requests. +type Server struct { + h Handler + baseServer +} + +// NewServer creates new Server. +func NewServer(h Handler, opts ...ServerOption) (*Server, error) { + s, err := newServerConfig(opts...).baseServer() + if err != nil { + return nil, err + } + return &Server{ + h: h, + baseServer: s, + }, nil +} diff --git a/shared/pkg/openapi/order/v1/oas_unimplemented_gen.go b/shared/pkg/openapi/order/v1/oas_unimplemented_gen.go new file mode 100644 index 0000000..9ebefb7 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_unimplemented_gen.go @@ -0,0 +1,58 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "context" + + ht "github.com/ogen-go/ogen/http" +) + +// UnimplementedHandler is no-op Handler which returns http.ErrNotImplemented. +type UnimplementedHandler struct{} + +var _ Handler = UnimplementedHandler{} + +// CancelOrder implements cancelOrder operation. +// +// Cancels an existing order. +// +// POST /api/v1/orders/{order_uuid}/cancel +func (UnimplementedHandler) CancelOrder(ctx context.Context, params CancelOrderParams) (r CancelOrderRes, _ error) { + return r, ht.ErrNotImplemented +} + +// CreateOrder implements createOrder operation. +// +// Creates a new order. +// +// POST /api/v1/orders +func (UnimplementedHandler) CreateOrder(ctx context.Context, req *OrderCreateRequest) (r CreateOrderRes, _ error) { + return r, ht.ErrNotImplemented +} + +// GetOrderByUuid implements getOrderByUuid operation. +// +// Retrieves order details by UUID. +// +// GET /api/v1/orders/{order_uuid} +func (UnimplementedHandler) GetOrderByUuid(ctx context.Context, params GetOrderByUuidParams) (r GetOrderByUuidRes, _ error) { + return r, ht.ErrNotImplemented +} + +// PayOrder implements payOrder operation. +// +// Processes payment for an existing order. +// +// POST /api/v1/orders/{order_uuid}/pay +func (UnimplementedHandler) PayOrder(ctx context.Context, req *OrderPayRequest, params PayOrderParams) (r PayOrderRes, _ error) { + return r, ht.ErrNotImplemented +} + +// NewError creates *GenericErrorStatusCode from error returned by handler. +// +// Used for common default response. +func (UnimplementedHandler) NewError(ctx context.Context, err error) (r *GenericErrorStatusCode) { + r = new(GenericErrorStatusCode) + return r +} diff --git a/shared/pkg/openapi/order/v1/oas_validators_gen.go b/shared/pkg/openapi/order/v1/oas_validators_gen.go new file mode 100644 index 0000000..1721994 --- /dev/null +++ b/shared/pkg/openapi/order/v1/oas_validators_gen.go @@ -0,0 +1,161 @@ +// Code generated by ogen, DO NOT EDIT. + +package orderv1 + +import ( + "github.com/go-faster/errors" + "github.com/google/uuid" + "github.com/ogen-go/ogen/validate" +) + +func (s *Order) Validate() error { + if s == nil { + return validate.ErrNilPointer + } + + var failures []validate.FieldError + if err := func() error { + if s.PartUuids == nil { + return errors.New("nil is invalid value") + } + if err := (validate.Array{ + MinLength: 1, + MinLengthSet: true, + MaxLength: 0, + MaxLengthSet: false, + }).ValidateLength(len(s.PartUuids)); err != nil { + return errors.Wrap(err, "array") + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "part_uuids", + Error: err, + }) + } + if err := func() error { + if value, ok := s.PaymentMethod.Get(); ok { + if err := func() error { + if err := value.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + return err + } + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "payment_method", + Error: err, + }) + } + if err := func() error { + if err := s.Status.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "status", + Error: err, + }) + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil +} + +func (s *OrderCreateRequest) Validate() error { + if s == nil { + return validate.ErrNilPointer + } + + var failures []validate.FieldError + if err := func() error { + if err := s.PartUuids.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "part_uuids", + Error: err, + }) + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil +} + +func (s *OrderPayRequest) Validate() error { + if s == nil { + return validate.ErrNilPointer + } + + var failures []validate.FieldError + if err := func() error { + if err := s.PaymentMethod.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "payment_method", + Error: err, + }) + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil +} + +func (s OrderStatus) Validate() error { + switch s { + case "STATUS_PENDING_PAYMENT": + return nil + case "STATUS_PAID": + return nil + case "STATUS_CANCELLED": + return nil + default: + return errors.Errorf("invalid value: %v", s) + } +} + +func (s PartUuids) Validate() error { + alias := ([]uuid.UUID)(s) + if alias == nil { + return errors.New("nil is invalid value") + } + if err := (validate.Array{ + MinLength: 1, + MinLengthSet: true, + MaxLength: 0, + MaxLengthSet: false, + }).ValidateLength(len(alias)); err != nil { + return errors.Wrap(err, "array") + } + return nil +} + +func (s PaymentMethod) Validate() error { + switch s { + case "PAYMENT_METHOD_UNSPECIFIED": + return nil + case "PAYMENT_METHOD_CARD": + return nil + case "PAYMENT_METHOD_SBP": + return nil + case "PAYMENT_METHOD_CREDIT_CARD": + return nil + case "PAYMENT_METHOD_INVESTOR_MONEY": + return nil + default: + return errors.Errorf("invalid value: %v", s) + } +} diff --git a/shared/pkg/proto/inventory/v1/inventory.pb.go b/shared/pkg/proto/inventory/v1/inventory.pb.go index ef7dd61..f7564d7 100644 --- a/shared/pkg/proto/inventory/v1/inventory.pb.go +++ b/shared/pkg/proto/inventory/v1/inventory.pb.go @@ -268,7 +268,7 @@ type Part struct { // Description of the part. Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` // Unit price. - Price int64 `protobuf:"varint,4,opt,name=price,proto3" json:"price,omitempty"` + PriceMinor int64 `protobuf:"varint,4,opt,name=price_minor,json=priceMinor,proto3" json:"price_minor,omitempty"` // Quantity available in stock. StockQuantity int64 `protobuf:"varint,5,opt,name=stock_quantity,json=stockQuantity,proto3" json:"stock_quantity,omitempty"` // Part category. @@ -340,9 +340,9 @@ func (x *Part) GetDescription() string { return "" } -func (x *Part) GetPrice() int64 { +func (x *Part) GetPriceMinor() int64 { if x != nil { - return x.Price + return x.PriceMinor } return 0 } @@ -738,12 +738,13 @@ const file_inventory_v1_inventory_proto_rawDesc = "" + "\x10ListPartsRequest\x121\n" + "\x06filter\x18\x01 \x01(\v2\x19.inventory.v1.PartsFilterR\x06filter\"=\n" + "\x11ListPartsResponse\x12(\n" + - "\x05parts\x18\x01 \x03(\v2\x12.inventory.v1.PartR\x05parts\"\xd5\x04\n" + + "\x05parts\x18\x01 \x03(\v2\x12.inventory.v1.PartR\x05parts\"\xe0\x04\n" + "\x04Part\x12\x12\n" + "\x04uuid\x18\x01 \x01(\tR\x04uuid\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12 \n" + - "\vdescription\x18\x03 \x01(\tR\vdescription\x12\x14\n" + - "\x05price\x18\x04 \x01(\x03R\x05price\x12%\n" + + "\vdescription\x18\x03 \x01(\tR\vdescription\x12\x1f\n" + + "\vprice_minor\x18\x04 \x01(\x03R\n" + + "priceMinor\x12%\n" + "\x0estock_quantity\x18\x05 \x01(\x03R\rstockQuantity\x122\n" + "\bcategory\x18\x06 \x01(\x0e2\x16.inventory.v1.CategoryR\bcategory\x128\n" + "\n" + diff --git a/shared/proto/inventory/v1/inventory.proto b/shared/proto/inventory/v1/inventory.proto index 5b39225..48d7e9c 100644 --- a/shared/proto/inventory/v1/inventory.proto +++ b/shared/proto/inventory/v1/inventory.proto @@ -47,7 +47,7 @@ message Part { string description = 3; // Unit price. - int64 price = 4; + int64 price_minor = 4; // Quantity available in stock. int64 stock_quantity = 5;