diff --git a/build/attraction.Dockerfile b/build/attraction.Dockerfile new file mode 100644 index 0000000..da2a43f --- /dev/null +++ b/build/attraction.Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.23.1-alpine AS builder +COPY . /github.com/go-park-mail-ru/2024_2_ThereWillBeName/attractions +WORKDIR /github.com/go-park-mail-ru/2024_2_ThereWillBeName/attractions +RUN go mod download +RUN go clean --modcache +RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -o ./.bin ./cmd/attractions/main.go +FROM scratch AS runner +WORKDIR /build +COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/attractions/.bin . +EXPOSE 8081 +ENTRYPOINT ["./.bin"] diff --git a/build/gateway.Dockerfile b/build/gateway.Dockerfile new file mode 100644 index 0000000..7bbb61c --- /dev/null +++ b/build/gateway.Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.23.1-alpine AS builder +COPY . /github.com/go-park-mail-ru/2024_2_ThereWillBeName/gateway +WORKDIR /github.com/go-park-mail-ru/2024_2_ThereWillBeName/gateway +RUN go mod download +RUN go clean --modcache +RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -o ./.bin ./cmd/gateway/main.go +FROM scratch AS runner +WORKDIR /build +COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/gateway/.bin . +EXPOSE 8080 +ENTRYPOINT ["./.bin"] diff --git a/build/survey.Dockerfile b/build/survey.Dockerfile new file mode 100644 index 0000000..d99dce5 --- /dev/null +++ b/build/survey.Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.23.1-alpine AS builder +COPY . /github.com/go-park-mail-ru/2024_2_ThereWillBeName/survey +WORKDIR /github.com/go-park-mail-ru/2024_2_ThereWillBeName/survey +RUN go mod download +RUN go clean --modcache +RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -o ./.bin ./cmd/survey/main.go +FROM scratch AS runner +WORKDIR /build +COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/survey/.bin . +EXPOSE 50054 +ENTRYPOINT ["./.bin"] diff --git a/build/trips.Dockerfile b/build/trips.Dockerfile new file mode 100644 index 0000000..b6f7752 --- /dev/null +++ b/build/trips.Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.23.1-alpine AS builder +COPY . /github.com/go-park-mail-ru/2024_2_ThereWillBeName/trips +WORKDIR /github.com/go-park-mail-ru/2024_2_ThereWillBeName/trips +RUN go mod download +RUN go clean --modcache +RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -o ./.bin ./cmd/trips/main.go +FROM scratch AS runner +WORKDIR /build +COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/trips/.bin . +EXPOSE 50053 +ENTRYPOINT ["./.bin"] diff --git a/build/users.Dockerfile b/build/users.Dockerfile new file mode 100644 index 0000000..395cabd --- /dev/null +++ b/build/users.Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.23.1-alpine AS builder +COPY . /github.com/go-park-mail-ru/2024_2_ThereWillBeName/users +WORKDIR /github.com/go-park-mail-ru/2024_2_ThereWillBeName/users +RUN go mod download +RUN go clean --modcache +RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -o ./.bin ./cmd/users/main.go +FROM scratch AS runner +WORKDIR /build +COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/users/.bin . +EXPOSE 50052 +ENTRYPOINT ["./.bin"] diff --git a/cmd/attractions/main.go b/cmd/attractions/main.go new file mode 100644 index 0000000..3bfdbeb --- /dev/null +++ b/cmd/attractions/main.go @@ -0,0 +1,122 @@ +package main + +import ( + "2024_2_ThereWillBeName/internal/models" + grpcAttractions "2024_2_ThereWillBeName/internal/pkg/attractions/delivery/grpc" + genPlaces "2024_2_ThereWillBeName/internal/pkg/attractions/delivery/grpc/gen" + placeRepo "2024_2_ThereWillBeName/internal/pkg/attractions/repo" + placeUsecase "2024_2_ThereWillBeName/internal/pkg/attractions/usecase" + grpcCategories "2024_2_ThereWillBeName/internal/pkg/categories/delivery/grpc" + genCategories "2024_2_ThereWillBeName/internal/pkg/categories/delivery/grpc/gen" + categoriesRepo "2024_2_ThereWillBeName/internal/pkg/categories/repo" + categoriesUsecase "2024_2_ThereWillBeName/internal/pkg/categories/usecase" + grpcCities "2024_2_ThereWillBeName/internal/pkg/cities/delivery/grpc" + genCities "2024_2_ThereWillBeName/internal/pkg/cities/delivery/grpc/gen" + citiesRepo "2024_2_ThereWillBeName/internal/pkg/cities/repo" + citiesUsecase "2024_2_ThereWillBeName/internal/pkg/cities/usecase" + "2024_2_ThereWillBeName/internal/pkg/logger" + grpcReviews "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/grpc" + genReviews "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/grpc/gen" + reviewRepo "2024_2_ThereWillBeName/internal/pkg/reviews/repo" + reviewUsecase "2024_2_ThereWillBeName/internal/pkg/reviews/usecase" + grpcSearch "2024_2_ThereWillBeName/internal/pkg/search/delivery/grpc" + genSearch "2024_2_ThereWillBeName/internal/pkg/search/delivery/grpc/gen" + searchRepo "2024_2_ThereWillBeName/internal/pkg/search/repo" + searchUsecase "2024_2_ThereWillBeName/internal/pkg/search/usecase" + "database/sql" + "flag" + "log" + "log/slog" + "net" + "os" + "os/signal" + "strconv" + "syscall" + + _ "github.com/lib/pq" + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +func main() { + var cfg models.ConfigGrpc + flag.IntVar(&cfg.Port, "grpc-port", 50051, "gRPC server port") + flag.StringVar(&cfg.ConnStr, "connStr", "host=tripdb port=5432 user=service password=test dbname=trip sslmode=disable", "PostgreSQL connection string") + flag.Parse() + + logger := setupLogger() + + db, err := sql.Open("postgres", cfg.ConnStr) + if err != nil { + log.Fatalf("failed to connect to database: %v", err) + } + defer db.Close() + + reviewsRepo := reviewRepo.NewReviewRepository(db) + reviewUsecase := reviewUsecase.NewReviewsUsecase(reviewsRepo) + placeRepo := placeRepo.NewPLaceRepository(db) + placeUsecase := placeUsecase.NewPlaceUsecase(placeRepo) + citiesRepo := citiesRepo.NewCitiesRepository(db) + citiesUsecase := citiesUsecase.NewCitiesUsecase(citiesRepo) + categoriesRepo := categoriesRepo.NewCategoriesRepo(db) + categoriesUsecase := categoriesUsecase.NewCategoriesUsecase(categoriesRepo) + searchRepo := searchRepo.NewSearchRepository(db) + searchUsecase := searchUsecase.NewSearchUsecase(searchRepo) + + grpcAttractionsServer := grpc.NewServer() + + attractionsHandler := grpcAttractions.NewGrpcAttractionsHandler(placeUsecase) + genPlaces.RegisterAttractionsServer(grpcAttractionsServer, attractionsHandler) + + citiesHandler := grpcCities.NewGrpcCitiesHandler(citiesUsecase) + genCities.RegisterCitiesServer(grpcAttractionsServer, citiesHandler) + + reviewsHandler := grpcReviews.NewGrpcReviewsHandler(reviewUsecase) + genReviews.RegisterReviewsServer(grpcAttractionsServer, reviewsHandler) + + categoriesHandler := grpcCategories.NewGrpcCategoriesHandler(categoriesUsecase) + genCategories.RegisterCategoriesServer(grpcAttractionsServer, categoriesHandler) + + searchHandler := grpcSearch.NewGrpcSearchHandler(searchUsecase, logger) + genSearch.RegisterSearchServer(grpcAttractionsServer, searchHandler) + + reflection.Register(grpcAttractionsServer) + + go func() { + listener, err := net.Listen("tcp", ":8081") + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + log.Printf("gRPC server listening on :%d", cfg.Port) + if err := grpcAttractionsServer.Serve(listener); err != nil { + log.Fatalf("failed to serve gRPC: %v", err) + } + }() + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + <-stop + + log.Println("Shutting down gRPC server...") + grpcAttractionsServer.GracefulStop() + log.Println("gRPC server gracefully stopped") +} + +func setupLogger() *slog.Logger { + + levelEnv := os.Getenv("LOG_LEVEL") + logLevel := slog.LevelDebug + if level, err := strconv.Atoi(levelEnv); err == nil { + logLevel = slog.Level(level) + } + + opts := logger.PrettyHandlerOptions{ + SlogOpts: slog.HandlerOptions{ + Level: logLevel, + }, + } + + handler := logger.NewPrettyHandler(os.Stdout, opts) + + return slog.New(handler) +} diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go new file mode 100644 index 0000000..bb693e6 --- /dev/null +++ b/cmd/gateway/main.go @@ -0,0 +1,219 @@ +package main + +import ( + "2024_2_ThereWillBeName/internal/models" + genAttractions "2024_2_ThereWillBeName/internal/pkg/attractions/delivery/grpc/gen" + httpPlaces "2024_2_ThereWillBeName/internal/pkg/attractions/delivery/http" + genCategories "2024_2_ThereWillBeName/internal/pkg/categories/delivery/grpc/gen" + httpCategories "2024_2_ThereWillBeName/internal/pkg/categories/delivery/http" + genCities "2024_2_ThereWillBeName/internal/pkg/cities/delivery/grpc/gen" + httpCities "2024_2_ThereWillBeName/internal/pkg/cities/delivery/http" + "2024_2_ThereWillBeName/internal/pkg/httpresponses" + httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" + "2024_2_ThereWillBeName/internal/pkg/jwt" + "2024_2_ThereWillBeName/internal/pkg/logger" + "2024_2_ThereWillBeName/internal/pkg/middleware" + genReviews "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/grpc/gen" + httpReviews "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/http" + genSearch "2024_2_ThereWillBeName/internal/pkg/search/delivery/grpc/gen" + httpSearch "2024_2_ThereWillBeName/internal/pkg/search/delivery/http" + genSurvey "2024_2_ThereWillBeName/internal/pkg/survey/delivery/grpc/gen" + httpSurvey "2024_2_ThereWillBeName/internal/pkg/survey/delivery/http" + genTrips "2024_2_ThereWillBeName/internal/pkg/trips/delivery/grpc/gen" + httpTrips "2024_2_ThereWillBeName/internal/pkg/trips/delivery/http" + genUsers "2024_2_ThereWillBeName/internal/pkg/user/delivery/grpc/gen" + httpUsers "2024_2_ThereWillBeName/internal/pkg/user/delivery/http" + "context" + "errors" + "flag" + "fmt" + "log" + "log/slog" + "net/http" + "os" + "os/signal" + "strconv" + "syscall" + + _ "github.com/lib/pq" + + "github.com/gorilla/mux" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func main() { + var cfg models.Config + flag.IntVar(&cfg.Port, "port", 8080, "API server port") + flag.StringVar(&cfg.Env, "env", "production", "Environment") + flag.StringVar(&cfg.AllowedOrigin, "allowed-origin", "*", "Allowed origin") + flag.Parse() + + logger := setupLogger() + + jwtSecret := os.Getenv("JWT_SECRET") + jwtHandler := jwt.NewJWT(jwtSecret, logger) + + attractionsConn, err := grpc.Dial("attractions:8081", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalf("did not connect to attractions service: %v", err) + } + defer attractionsConn.Close() + + attractionsClient := genAttractions.NewAttractionsClient(attractionsConn) + categoriesClient := genCategories.NewCategoriesClient(attractionsConn) + citiesClient := genCities.NewCitiesClient(attractionsConn) + reviewsClient := genReviews.NewReviewsClient(attractionsConn) + searchClient := genSearch.NewSearchClient(attractionsConn) + + usersConn, err := grpc.NewClient("users:50052", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalf("did not connect to users service: %v", err) + } + defer usersConn.Close() + usersClient := genUsers.NewUserServiceClient(usersConn) + + tripsConn, err := grpc.NewClient("trips:50053", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalf("did not connect to trips service: %v", err) + } + defer tripsConn.Close() + tripsClient := genTrips.NewTripsClient(tripsConn) + + surveyConn, err := grpc.NewClient("survey:50054", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatalf("did not connect to survey service: %v", err) + } + defer surveyConn.Close() + surveyClient := genSurvey.NewSurveyServiceClient(surveyConn) + + // Инициализация HTTP сервера + corsMiddleware := middleware.NewCORSMiddleware([]string{cfg.AllowedOrigin}) + r := mux.NewRouter().PathPrefix("/api/v1").Subrouter() + r.Use(corsMiddleware.CorsMiddleware) + + // Обработка ненайденных маршрутов + r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + response := httpresponses.ErrorResponse{ + Message: "Not found", + } + httpresponses.SendJSONResponse(w, response, http.StatusNotFound, logger) + }) + + // Маршрут для healthcheck + r.HandleFunc("/healthcheck", healthcheckHandler).Methods(http.MethodGet) + + // Маршруты для attractions + placesHandler := httpPlaces.NewPlacesHandler(attractionsClient, logger) + places := r.PathPrefix("/places").Subrouter() + places.HandleFunc("", placesHandler.GetPlacesHandler).Methods(http.MethodGet) + places.HandleFunc("/search", placesHandler.SearchPlacesHandler).Methods(http.MethodGet) + places.HandleFunc("/{id}", placesHandler.GetPlaceHandler).Methods(http.MethodGet) + places.HandleFunc("/category/{categoryName}", placesHandler.GetPlacesByCategoryHandler).Methods(http.MethodGet) + + categoriesHandler := httpCategories.NewCategoriesHandler(categoriesClient, logger) + categories := r.PathPrefix("/categories").Subrouter() + categories.HandleFunc("", categoriesHandler.GetCategoriesHandler).Methods(http.MethodGet) + + reviewsHandler := httpReviews.NewReviewHandler(reviewsClient, logger) + reviews := places.PathPrefix("/{placeID}/reviews").Subrouter() + reviews.Handle("", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(reviewsHandler.CreateReviewHandler), logger)).Methods(http.MethodPost) + reviews.Handle("/{reviewID}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(reviewsHandler.UpdateReviewHandler), logger)).Methods(http.MethodPut) + reviews.Handle("/{reviewID}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(reviewsHandler.DeleteReviewHandler), logger)).Methods(http.MethodDelete) + reviews.HandleFunc("/{reviewID}", reviewsHandler.GetReviewHandler).Methods(http.MethodGet) + reviews.HandleFunc("", reviewsHandler.GetReviewsByPlaceIDHandler).Methods(http.MethodGet) + + citiesHandler := httpCities.NewCitiesHandler(citiesClient, logger) + cities := r.PathPrefix("/cities").Subrouter() + cities.HandleFunc("/search", citiesHandler.SearchCitiesByNameHandler).Methods(http.MethodGet) + cities.HandleFunc("/{id}", citiesHandler.SearchCityByIDHandler).Methods(http.MethodGet) + + searchHandler := httpSearch.NewSearchHandler(searchClient, logger) + search := r.PathPrefix("/search").Subrouter() + search.HandleFunc("", searchHandler.Search).Methods(http.MethodGet) + + //Маршруты для Users + usersHandler := httpUsers.NewUserHandler(usersClient, jwtHandler, logger) + auth := r.PathPrefix("/auth").Subrouter() + auth.HandleFunc("/signup", usersHandler.SignUp).Methods(http.MethodPost) + auth.HandleFunc("/login", usersHandler.Login).Methods(http.MethodPost) + auth.Handle("/logout", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(usersHandler.Logout), logger)).Methods(http.MethodPost) + + users := r.PathPrefix("/users").Subrouter() + users.Handle("/me", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(usersHandler.CurrentUser), logger)).Methods(http.MethodGet) + + user := users.PathPrefix("/{userID}").Subrouter() + + user.Handle("/avatars", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(usersHandler.UploadAvatar), logger)).Methods(http.MethodPut) + user.Handle("/profile", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(usersHandler.GetProfile), logger)).Methods(http.MethodGet) + user.Handle("/update/password", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(usersHandler.UpdatePassword), logger)).Methods(http.MethodPut) + user.Handle("/profile", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(usersHandler.UpdateProfile), logger)).Methods(http.MethodPut) + + tripsHandler := httpTrips.NewTripHandler(tripsClient, logger) + trips := r.PathPrefix("/trips").Subrouter() + trips.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripsHandler.GetTripHandler), logger)).Methods(http.MethodGet) + trips.Handle("", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripsHandler.CreateTripHandler), logger)).Methods(http.MethodPost) + trips.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripsHandler.UpdateTripHandler), logger)).Methods(http.MethodPut) + trips.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripsHandler.DeleteTripHandler), logger)).Methods(http.MethodDelete) + trips.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripsHandler.AddPlaceToTripHandler), logger)).Methods(http.MethodPost) + user.Handle("/trips", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripsHandler.GetTripsByUserIDHandler), logger)).Methods(http.MethodGet) + + surveyHandler := httpSurvey.NewSurveyHandler(surveyClient, logger) + survey := r.PathPrefix("/survey").Subrouter() + survey.Handle("/stats/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(surveyHandler.GetSurveyStatsBySurveyId), logger)).Methods(http.MethodGet) + survey.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(surveyHandler.GetSurveyById), logger)).Methods(http.MethodGet) + survey.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(surveyHandler.CreateSurveyResponse), logger)).Methods(http.MethodPost) + survey.Handle("/users/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(surveyHandler.GetSurveyStatsByUserId), logger)).Methods(http.MethodGet) + + httpSrv := &http.Server{Handler: r, Addr: fmt.Sprintf(":%d", cfg.Port)} + go func() { + logger.Info("HTTP server listening on :%d", cfg.Port) + if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { + logger.Error("failed to serve HTTP: %d", err) + os.Exit(1) + } + }() + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + <-stop + + logger.Info("Shutting down HTTP server...") + if err := httpSrv.Shutdown(context.Background()); err != nil { + logger.Error("HTTP server shutdown failed: %v", err) + os.Exit(1) + } + logger.Info("HTTP server gracefully stopped") +} + +func healthcheckHandler(w http.ResponseWriter, r *http.Request) { + logger := setupLogger() + + _, err := fmt.Fprintf(w, "STATUS: OK") + if err != nil { + logger.Error("Failed to write healthcheck response", slog.Any("error", err)) + response := httpresponse.ErrorResponse{ + Message: "Invalid request", + } + httpresponse.SendJSONResponse(w, response, http.StatusBadRequest, logger) + } +} + +func setupLogger() *slog.Logger { + + levelEnv := os.Getenv("LOG_LEVEL") + logLevel := slog.LevelDebug + if level, err := strconv.Atoi(levelEnv); err == nil { + logLevel = slog.Level(level) + } + + opts := logger.PrettyHandlerOptions{ + SlogOpts: slog.HandlerOptions{ + Level: logLevel, + }, + } + + handler := logger.NewPrettyHandler(os.Stdout, opts) + + return slog.New(handler) +} diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index a9b782a..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,218 +0,0 @@ -package main - -import ( - "2024_2_ThereWillBeName/internal/models" - - httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" - "2024_2_ThereWillBeName/internal/pkg/jwt" - "2024_2_ThereWillBeName/internal/pkg/logger" - "2024_2_ThereWillBeName/internal/pkg/middleware" - httpHandler "2024_2_ThereWillBeName/internal/pkg/user/delivery/http" - userRepo "2024_2_ThereWillBeName/internal/pkg/user/repo" - userUsecase "2024_2_ThereWillBeName/internal/pkg/user/usecase" - "log/slog" - "strconv" - - categorieshandler "2024_2_ThereWillBeName/internal/pkg/categories/delivery/http" - categoriesrepo "2024_2_ThereWillBeName/internal/pkg/categories/repo" - categoriesusecase "2024_2_ThereWillBeName/internal/pkg/categories/usecase" - citieshandler "2024_2_ThereWillBeName/internal/pkg/cities/delivery/http" - citiesrepo "2024_2_ThereWillBeName/internal/pkg/cities/repo" - citiesusecase "2024_2_ThereWillBeName/internal/pkg/cities/usecase" - delivery "2024_2_ThereWillBeName/internal/pkg/places/delivery/http" - placeRepo "2024_2_ThereWillBeName/internal/pkg/places/repo" - placeUsecase "2024_2_ThereWillBeName/internal/pkg/places/usecase" - reviewhandler "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/http" - reviewrepo "2024_2_ThereWillBeName/internal/pkg/reviews/repo" - reviewusecase "2024_2_ThereWillBeName/internal/pkg/reviews/usecase" - triphandler "2024_2_ThereWillBeName/internal/pkg/trips/delivery/http" - triprepo "2024_2_ThereWillBeName/internal/pkg/trips/repo" - tripusecase "2024_2_ThereWillBeName/internal/pkg/trips/usecase" - - "database/sql" - "flag" - "fmt" - "net/http" - "os" - "time" - - _ "2024_2_ThereWillBeName/docs" - - "github.com/gorilla/mux" - _ "github.com/lib/pq" - httpSwagger "github.com/swaggo/http-swagger" -) - -func main() { - var cfg models.Config - flag.IntVar(&cfg.Port, "port", 8080, "API server port") - flag.StringVar(&cfg.Env, "env", "production", "Environment") - flag.StringVar(&cfg.AllowedOrigin, "allowed-origin", "*", "Allowed origin") - flag.StringVar(&cfg.ConnStr, "connStr", "host=tripdb port=5432 user=service password=test dbname=trip sslmode=disable", "PostgreSQL connection string") - flag.Parse() - - logger := setupLogger() - defer logger.Info("Server stopped") - - db, err := openDB(cfg.ConnStr) - if err != nil { - logger.Error("Failed to open database", slog.Any("error", err)) - panic(err) - } - logger.Info("Connected to database successfully") - defer db.Close() - - jwtSecret := os.Getenv("JWT_SECRET") - storagePath := os.Getenv("AVATAR_STORAGE_PATH") - - logger.Debug("avatar_storage_path", "path", storagePath) - - userRepo := userRepo.NewAuthRepository(db) - jwtHandler := jwt.NewJWT(string(jwtSecret), logger) - userUseCase := userUsecase.NewUserUsecase(userRepo, storagePath) - h := httpHandler.NewUserHandler(userUseCase, jwtHandler, logger) - - reviewsRepo := reviewrepo.NewReviewRepository(db) - reviewUsecase := reviewusecase.NewReviewsUsecase(reviewsRepo) - reviewHandler := reviewhandler.NewReviewHandler(reviewUsecase, logger) - placeRepo := placeRepo.NewPLaceRepository(db) - placeUsecase := placeUsecase.NewPlaceUsecase(placeRepo) - placeHandler := delivery.NewPlacesHandler(placeUsecase, logger) - tripsRepo := triprepo.NewTripRepository(db) - tripUsecase := tripusecase.NewTripsUsecase(tripsRepo) - tripHandler := triphandler.NewTripHandler(tripUsecase, logger) - citiesRepo := citiesrepo.NewCitiesRepository(db) - citiesUsecase := citiesusecase.NewCitiesUsecase(citiesRepo) - citiesHandler := citieshandler.NewCitiesHandler(citiesUsecase, logger) - categoriesRepo := categoriesrepo.NewCategoriesRepo(db) - categoriesUsecase := categoriesusecase.NewCategoriesUsecase(categoriesRepo) - categoriesHandler := categorieshandler.NewCategoriesHandler(categoriesUsecase, logger) - - corsMiddleware := middleware.NewCORSMiddleware([]string{cfg.AllowedOrigin}) - - r := mux.NewRouter().PathPrefix("/api/v1").Subrouter() - r.Use(corsMiddleware.CorsMiddleware) - r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - response := httpresponse.ErrorResponse{ - Message: "Not found", - } - httpresponse.SendJSONResponse(w, response, http.StatusNotFound, logger) - }) - r.HandleFunc("/healthcheck", healthcheckHandler).Methods(http.MethodGet) - - auth := r.PathPrefix("/auth").Subrouter() - auth.HandleFunc("/signup", h.SignUp).Methods(http.MethodPost) - auth.HandleFunc("/login", h.Login).Methods(http.MethodPost) - auth.Handle("/logout", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(h.Logout), logger)).Methods(http.MethodPost) - users := r.PathPrefix("/users").Subrouter() - users.Handle("/me", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(h.CurrentUser), logger)).Methods(http.MethodGet) - - user := users.PathPrefix("/{userID}").Subrouter() - - user.Handle("/avatars", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(h.UploadAvatar), logger)).Methods(http.MethodPut) - user.Handle("/profile", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(h.GetProfile), logger)).Methods(http.MethodGet) - user.Handle("/update/password", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(h.UpdatePassword), logger)).Methods(http.MethodPut) - user.Handle("/profile", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(h.UpdateProfile), logger)).Methods(http.MethodPut) - - places := r.PathPrefix("/places").Subrouter() - places.HandleFunc("", placeHandler.GetPlacesHandler).Methods(http.MethodGet) - places.Handle("", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(placeHandler.PostPlaceHandler), logger)).Methods(http.MethodPost) - places.HandleFunc("/search/{placeName}", placeHandler.SearchPlacesHandler).Methods(http.MethodGet) - places.HandleFunc("/{id}", placeHandler.GetPlaceHandler).Methods(http.MethodGet) - places.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(placeHandler.PutPlaceHandler), logger)).Methods(http.MethodPut) - places.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(placeHandler.DeletePlaceHandler), logger)).Methods(http.MethodDelete) - places.HandleFunc("/category/{categoryName}", placeHandler.GetPlacesByCategoryHandler).Methods(http.MethodGet) - - categories := r.PathPrefix("/categories").Subrouter() - categories.HandleFunc("", categoriesHandler.GetCategoriesHandler).Methods(http.MethodGet) - r.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler) - - reviews := places.PathPrefix("/{placeID}/reviews").Subrouter() - reviews.Handle("", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(reviewHandler.CreateReviewHandler), logger)).Methods(http.MethodPost) - reviews.Handle("/reviewID", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(reviewHandler.UpdateReviewHandler), logger)).Methods(http.MethodPut) - reviews.Handle("/reviewID", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(reviewHandler.DeleteReviewHandler), logger)).Methods(http.MethodDelete) - reviews.HandleFunc("/{reviewID}", reviewHandler.GetReviewHandler).Methods(http.MethodGet) - reviews.HandleFunc("", reviewHandler.GetReviewsByPlaceIDHandler).Methods(http.MethodGet) - - user.Handle("/reviews", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(reviewHandler.GetReviewsByUserIDHandler), logger)).Methods(http.MethodGet) - - trips := r.PathPrefix("/trips").Subrouter() - trips.Handle("", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripHandler.CreateTripHandler), logger)).Methods(http.MethodPost) - trips.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripHandler.UpdateTripHandler), logger)).Methods(http.MethodPut) - trips.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripHandler.DeleteTripHandler), logger)).Methods(http.MethodDelete) - trips.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripHandler.GetTripHandler), logger)).Methods(http.MethodGet) - trips.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripHandler.AddPlaceToTripHandler), logger)).Methods(http.MethodPost) - user.Handle("/trips", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(tripHandler.GetTripsByUserIDHandler), logger)).Methods(http.MethodGet) - - cities := r.PathPrefix("/cities").Subrouter() - cities.HandleFunc("/search", citiesHandler.SearchCitiesByNameHandler).Methods(http.MethodGet) - cities.HandleFunc("/{id}", citiesHandler.SearchCityByIDHandler).Methods(http.MethodGet) - - srv := &http.Server{ - Addr: fmt.Sprintf(":%d", cfg.Port), - Handler: r, - IdleTimeout: time.Minute, - ReadTimeout: 10 * time.Second, - WriteTimeout: 30 * time.Second, - } - - logger.Info("starting server", "environment", cfg.Env, "address", srv.Addr) - err = srv.ListenAndServe() - if err != nil { - logger.Error("Failed to start server", slog.Any("error", err)) - os.Exit(1) - } -} - -// healthcheckHandler godoc -// @Summary Health check -// @Description Check the health status of the service -// @Produce text/plain -// @Success 200 {string} string "STATUS: OK" -// @Failure 400 {object} httpresponses.ErrorResponse "Bad Request" -// @Router /healthcheck [get] -func healthcheckHandler(w http.ResponseWriter, r *http.Request) { - logger := setupLogger() - - _, err := fmt.Fprintf(w, "STATUS: OK") - if err != nil { - logger.Error("Failed to write healthcheck response", slog.Any("error", err)) - response := httpresponse.ErrorResponse{ - Message: "Invalid request", - } - httpresponse.SendJSONResponse(w, response, http.StatusBadRequest, logger) - } -} - -func openDB(connStr string) (*sql.DB, error) { - db, err := sql.Open("postgres", connStr) - if err != nil { - return nil, err - } - - err = db.Ping() - if err != nil { - return nil, err - } - - return db, nil -} - -func setupLogger() *slog.Logger { - - levelEnv := os.Getenv("LOG_LEVEL") - logLevel := slog.LevelDebug - if level, err := strconv.Atoi(levelEnv); err == nil { - logLevel = slog.Level(level) - } - - opts := logger.PrettyHandlerOptions{ - SlogOpts: slog.HandlerOptions{ - Level: logLevel, - }, - } - - handler := logger.NewPrettyHandler(os.Stdout, opts) - - return slog.New(handler) -} diff --git a/cmd/survey/main.go b/cmd/survey/main.go new file mode 100644 index 0000000..d0ffc19 --- /dev/null +++ b/cmd/survey/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/logger" + grpcSurvey "2024_2_ThereWillBeName/internal/pkg/survey/delivery/grpc" + "2024_2_ThereWillBeName/internal/pkg/survey/delivery/grpc/gen" + surveyRepo "2024_2_ThereWillBeName/internal/pkg/survey/repo" + surveyUsecase "2024_2_ThereWillBeName/internal/pkg/survey/usecase" + "database/sql" + "flag" + "fmt" + _ "github.com/lib/pq" + "log" + "log/slog" + "net" + "os" + "os/signal" + "strconv" + "syscall" + + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +func main() { + var cfg models.ConfigGrpc + flag.IntVar(&cfg.Port, "grpc-port", 50054, "gRPC server port") + flag.StringVar(&cfg.ConnStr, "connStr", "host=tripdb port=5432 user=service password=test dbname=trip sslmode=disable", "PostgreSQL connection string") + flag.Parse() + + logger := setupLogger() + + db, err := sql.Open("postgres", cfg.ConnStr) + if err != nil { + log.Fatalf("failed to connect to database: %v", err) + } + defer db.Close() + + surveyRepoImpl := surveyRepo.NewPLaceRepository(db) + surveyUsecaseImpl := surveyUsecase.NewSurveysUsecase(surveyRepoImpl) + + grpcSurveyServer := grpc.NewServer() + surveyHandler := grpcSurvey.NewGrpcSurveyHandler(surveyUsecaseImpl, logger) + gen.RegisterSurveyServiceServer(grpcSurveyServer, surveyHandler) + reflection.Register(grpcSurveyServer) + + go func() { + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Port)) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + log.Printf("gRPC server listening on :%d", cfg.Port) + if err := grpcSurveyServer.Serve(listener); err != nil { + log.Fatalf("failed to serve gRPC: %v", err) + } + }() + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + <-stop + + log.Println("Shutting down gRPC server...") + grpcSurveyServer.GracefulStop() + log.Println("gRPC server gracefully stopped") +} +func setupLogger() *slog.Logger { + + levelEnv := os.Getenv("LOG_LEVEL") + logLevel := slog.LevelDebug + if level, err := strconv.Atoi(levelEnv); err == nil { + logLevel = slog.Level(level) + } + + opts := logger.PrettyHandlerOptions{ + SlogOpts: slog.HandlerOptions{ + Level: logLevel, + }, + } + + handler := logger.NewPrettyHandler(os.Stdout, opts) + + return slog.New(handler) +} diff --git a/cmd/trips/main.go b/cmd/trips/main.go new file mode 100644 index 0000000..f23f0d7 --- /dev/null +++ b/cmd/trips/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/logger" + grpcTrips "2024_2_ThereWillBeName/internal/pkg/trips/delivery/grpc" + "2024_2_ThereWillBeName/internal/pkg/trips/delivery/grpc/gen" + tripRepo "2024_2_ThereWillBeName/internal/pkg/trips/repo" + tripUsecase "2024_2_ThereWillBeName/internal/pkg/trips/usecase" + "database/sql" + "flag" + "fmt" + _ "github.com/lib/pq" + "log" + "log/slog" + "net" + "os" + "os/signal" + "strconv" + "syscall" + + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +func main() { + var cfg models.ConfigGrpc + flag.IntVar(&cfg.Port, "grpc-port", 50053, "gRPC server port") + flag.StringVar(&cfg.ConnStr, "connStr", "host=tripdb port=5432 user=service password=test dbname=trip sslmode=disable", "PostgreSQL connection string") + flag.Parse() + + logger := setupLogger() + + db, err := sql.Open("postgres", cfg.ConnStr) + if err != nil { + log.Fatalf("failed to connect to database: %v", err) + } + defer db.Close() + + tripRepo := tripRepo.NewTripRepository(db) + tripUsecase := tripUsecase.NewTripsUsecase(tripRepo) + + grpcTripsServer := grpc.NewServer() + tripsHandler := grpcTrips.NewGrpcTripHandler(tripUsecase, logger) + gen.RegisterTripsServer(grpcTripsServer, tripsHandler) + reflection.Register(grpcTripsServer) + + go func() { + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Port)) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + log.Printf("gRPC server listening on :%d", cfg.Port) + if err := grpcTripsServer.Serve(listener); err != nil { + log.Fatalf("failed to serve gRPC: %v", err) + } + }() + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + <-stop + + log.Println("Shutting down gRPC server...") + grpcTripsServer.GracefulStop() + log.Println("gRPC server gracefully stopped") +} +func setupLogger() *slog.Logger { + + levelEnv := os.Getenv("LOG_LEVEL") + logLevel := slog.LevelDebug + if level, err := strconv.Atoi(levelEnv); err == nil { + logLevel = slog.Level(level) + } + + opts := logger.PrettyHandlerOptions{ + SlogOpts: slog.HandlerOptions{ + Level: logLevel, + }, + } + + handler := logger.NewPrettyHandler(os.Stdout, opts) + + return slog.New(handler) +} diff --git a/cmd/users/main.go b/cmd/users/main.go new file mode 100644 index 0000000..66cf8d2 --- /dev/null +++ b/cmd/users/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/logger" + grpcUsers "2024_2_ThereWillBeName/internal/pkg/user/delivery/grpc" + "2024_2_ThereWillBeName/internal/pkg/user/delivery/grpc/gen" + userRepo "2024_2_ThereWillBeName/internal/pkg/user/repo" + userUsecase "2024_2_ThereWillBeName/internal/pkg/user/usecase" + "database/sql" + "flag" + "fmt" + _ "github.com/lib/pq" + "log" + "log/slog" + "net" + "os" + "os/signal" + "strconv" + "syscall" + + "google.golang.org/grpc" + "google.golang.org/grpc/reflection" +) + +func main() { + var cfg models.ConfigGrpc + flag.IntVar(&cfg.Port, "grpc-port", 50052, "gRPC server port") + flag.StringVar(&cfg.ConnStr, "connStr", "host=tripdb port=5432 user=service password=test dbname=trip sslmode=disable", "PostgreSQL connection string") + flag.Parse() + + logger := setupLogger() + + storagePath := os.Getenv("AVATAR_STORAGE_PATH") + + db, err := sql.Open("postgres", cfg.ConnStr) + if err != nil { + log.Fatalf("failed to connect to database: %v", err) + } + defer db.Close() + + userRepo := userRepo.NewAuthRepository(db) + userUsecase := userUsecase.NewUserUsecase(userRepo, storagePath) + + grpcUsersServer := grpc.NewServer() + usersHandler := grpcUsers.NewGrpcUserHandler(userUsecase, logger) + gen.RegisterUserServiceServer(grpcUsersServer, usersHandler) + reflection.Register(grpcUsersServer) + + go func() { + listener, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Port)) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + log.Printf("gRPC server listening on :%d", cfg.Port) + if err := grpcUsersServer.Serve(listener); err != nil { + log.Fatalf("failed to serve gRPC: %v", err) + } + }() + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + <-stop + + log.Println("Shutting down gRPC server...") + grpcUsersServer.GracefulStop() + log.Println("gRPC server gracefully stopped") +} + +func setupLogger() *slog.Logger { + + levelEnv := os.Getenv("LOG_LEVEL") + logLevel := slog.LevelDebug + if level, err := strconv.Atoi(levelEnv); err == nil { + logLevel = slog.Level(level) + } + + opts := logger.PrettyHandlerOptions{ + SlogOpts: slog.HandlerOptions{ + Level: logLevel, + }, + } + + handler := logger.NewPrettyHandler(os.Stdout, opts) + + return slog.New(handler) +} diff --git a/db/migrations/01_up.sql b/db/migrations/01_up.sql index 505e95c..1fcd378 100644 --- a/db/migrations/01_up.sql +++ b/db/migrations/01_up.sql @@ -97,6 +97,21 @@ CREATE TABLE IF NOT EXISTS trip_place ( --таблица для сопостав CONSTRAINT uq_trip_place UNIQUE (trip_id, place_id) ); +CREATE TABLE IF NOT EXISTS survey ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + survey_text TEXT DEFAULT '', + max_rating INT NOT NULL CHECK (max_rating > 0) DEFAULT 1 +); + +CREATE TABLE IF NOT EXISTS user_survey ( + survey_id INT NOT NULL, + user_id INT NOT NULL, + PRIMARY KEY(survey_id, user_id), + rating INT NOT NULL CHECK (rating > 0) DEFAULT 1, + FOREIGN KEY (survey_id) REFERENCES survey(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE +); + COPY city(name) FROM '/docker-entrypoint-initdb.d/cities.csv' WITH (FORMAT csv, HEADER true); diff --git a/docker-compose.yml b/docker-compose.yml index 4a867d3..e8980f8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,15 +1,14 @@ version: "3.3" services: - main: - container_name: main-service + gateway: + container_name: gateway build: context: . - dockerfile: ./build/main.Dockerfile + dockerfile: build/gateway.Dockerfile env_file: - .env environment: - - AVATAR_STORAGE_PATH=/assets/avatars - LOG_LEVEL=Debug volumes: - ./assets/avatars:/assets/avatars @@ -17,6 +16,68 @@ services: - 8080:8080 depends_on: - tripdb + attractions: + container_name: attractions + build: + context: . + dockerfile: build/attraction.Dockerfile + env_file: + - .env + environment: + - LOG_LEVEL=Debug + volumes: + - ./assets/avatars:/assets/avatars + ports: + - 8081:8081 + depends_on: + - tripdb + trips: + container_name: trips + build: + context: . + dockerfile: build/trips.Dockerfile + env_file: + - .env + environment: + - LOG_LEVEL=Debug + volumes: + - ./assets/avatars:/assets/avatars + ports: + - 50053:50053 + depends_on: + - tripdb + users: + container_name: users + build: + context: . + dockerfile: build/users.Dockerfile + env_file: + - .env + environment: + - AVATAR_STORAGE_PATH=/assets/avatars + - LOG_LEVEL=Debug + volumes: + - ./assets/avatars:/assets/avatars + ports: + - 50052:50052 + depends_on: + - tripdb + survey: + container_name: survey + build: + context: . + dockerfile: build/survey.Dockerfile + env_file: + - .env + environment: + - AVATAR_STORAGE_PATH=/assets/avatars + - LOG_LEVEL=Debug + volumes: + - ./assets/avatars:/assets/avatars + ports: + - 50054:50054 + depends_on: + - tripdb tripdb: container_name: postgres image: postgres:15 diff --git a/docs/docs.go b/docs/docs.go index 16d19e7..4b706c9 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -192,16 +192,16 @@ const docTemplate = `{ } } }, - "/places": { + "/attractions": { "get": { - "description": "Retrieve a list of places from the database", + "description": "Retrieve a list of attractions from the database", "produces": [ "application/json" ], - "summary": "Get a list of places", + "summary": "Get a list of attractions", "responses": { "200": { - "description": "List of places", + "description": "List of attractions", "schema": { "type": "array", "items": { @@ -277,16 +277,16 @@ const docTemplate = `{ } } }, - "/places/search/{placeName}": { + "/attractions/search/{placeName}": { "get": { - "description": "Get a list of places from the database that match the provided search string", + "description": "Get a list of attractions from the database that match the provided search string", "produces": [ "application/json" ], - "summary": "Retrieve places by search string", + "summary": "Retrieve attractions by search string", "parameters": [ { - "description": "Name of the places to retrieve", + "description": "Name of the attractions to retrieve", "name": "searchString", "in": "body", "required": true, @@ -297,7 +297,7 @@ const docTemplate = `{ ], "responses": { "200": { - "description": "List of places matching the provided searchString", + "description": "List of attractions matching the provided searchString", "schema": { "$ref": "#/definitions/models.GetPlace" } @@ -317,7 +317,7 @@ const docTemplate = `{ } } }, - "/places/{id}": { + "/attractions/{id}": { "get": { "description": "Get details of a place from the database by its id", "produces": [ @@ -454,7 +454,7 @@ const docTemplate = `{ } } }, - "/places/{placeID}/reviews": { + "/attractions/{placeID}/reviews": { "get": { "description": "Get all reviews for a specific place", "produces": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 77b057d..3673d55 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -315,12 +315,12 @@ paths: summary: Logout a user /places: get: - description: Retrieve a list of places from the database + description: Retrieve a list of attractions from the database produces: - application/json responses: "200": - description: List of places + description: List of attractions schema: items: $ref: '#/definitions/models.GetPlace' @@ -333,7 +333,7 @@ paths: description: Internal Server Error schema: $ref: '#/definitions/httpresponses.ErrorResponse' - summary: Get a list of places + summary: Get a list of attractions post: consumes: - application/json @@ -492,10 +492,10 @@ paths: summary: Retrieve reviews by place ID /places/search/{placeName}: get: - description: Get a list of places from the database that match the provided + description: Get a list of attractions from the database that match the provided search string parameters: - - description: Name of the places to retrieve + - description: Name of the attractions to retrieve in: body name: searchString required: true @@ -505,7 +505,7 @@ paths: - application/json responses: "200": - description: List of places matching the provided searchString + description: List of attractions matching the provided searchString schema: $ref: '#/definitions/models.GetPlace' "400": @@ -516,7 +516,7 @@ paths: description: Internal Server Error schema: $ref: '#/definitions/httpresponses.ErrorResponse' - summary: Retrieve places by search string + summary: Retrieve attractions by search string /reviews: post: consumes: diff --git a/go.mod b/go.mod index fa3df86..51370c4 100644 --- a/go.mod +++ b/go.mod @@ -14,14 +14,24 @@ require ( require ( github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/fatih/color v1.18.0 - github.com/swaggo/http-swagger v1.3.4 + github.com/golang/protobuf v1.5.4 + github.com/prometheus/client_golang v1.20.5 + google.golang.org/grpc v1.68.0 + google.golang.org/protobuf v1.35.2 ) require ( - github.com/google/uuid v1.6.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect ) require ( @@ -37,7 +47,6 @@ require ( github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/swaggo/files v1.0.1 // indirect github.com/swaggo/swag v1.16.3 golang.org/x/net v0.30.0 // indirect golang.org/x/tools v0.26.0 // indirect diff --git a/go.sum b/go.sum index 754cf78..5712674 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= @@ -20,13 +24,17 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -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/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 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= @@ -44,41 +52,40 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 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/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= -github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= -github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww= -github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ= github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -86,30 +93,29 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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= diff --git a/internal/models/config.go b/internal/models/config.go index d9445ee..950d161 100644 --- a/internal/models/config.go +++ b/internal/models/config.go @@ -5,4 +5,10 @@ type Config struct { Env string AllowedOrigin string ConnStr string + GRPCPort int +} + +type ConfigGrpc struct { + Port int + ConnStr string } diff --git a/internal/models/search.go b/internal/models/search.go new file mode 100644 index 0000000..9090868 --- /dev/null +++ b/internal/models/search.go @@ -0,0 +1,7 @@ +package models + +type SearchResult struct { + Name string `json:"name"` + Id uint `json:"id"` + Type string `json:"type"` //"city" или "place" +} diff --git a/internal/models/survey.go b/internal/models/survey.go new file mode 100644 index 0000000..ad4adbe --- /dev/null +++ b/internal/models/survey.go @@ -0,0 +1,26 @@ +package models + +type Survey struct { + Id uint `json:"id"` + SurveyText string `json:"survey_text"` + MaxRating int `json:"max_rating"` +} + +type SurveyResponse struct { + SurveyId uint `json:"survey_id"` + UserId uint `json:"user_id"` + Rating int `json:"rating"` +} + +type SurveyStatsBySurvey struct { + SurveyId uint `json:"survey_id"` + SurveyText string `json:"survey_text"` + AvgRating float64 `json:"avg_rating"` + RatingsCount map[int]int `json:"ratings_count"` +} + +type UserSurveyStats struct { + SurveyId uint `json:"survey_id"` + SurveyText string `json:"survey_text"` + Answered bool `json:"answered"` +} diff --git a/internal/models/user.go b/internal/models/user.go index ce61e30..4a6707f 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -21,7 +21,7 @@ type UserProfile struct { } func ValidateUser(v *validator.Validator, user *User) { - v.Check(user.Login != "", "login", "must be provided") + // v.Check(user.Login != "", "login", "must be provided") v.Check(user.Password != "", "password", "must be provided") v.Check(user.Email != "", "email", "must be provided") v.Matches(user.Email, validator.EmailRX) diff --git a/internal/pkg/attractions/delivery/grpc/gen/attractions.pb.go b/internal/pkg/attractions/delivery/grpc/gen/attractions.pb.go new file mode 100644 index 0000000..fec3b8f --- /dev/null +++ b/internal/pkg/attractions/delivery/grpc/gen/attractions.pb.go @@ -0,0 +1,722 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.2 +// protoc v4.25.1 +// source: internal/pkg/attractions/delivery/grpc/proto/attractions.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Place struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + ImagePath string `protobuf:"bytes,3,opt,name=imagePath,proto3" json:"imagePath,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Rating int32 `protobuf:"varint,5,opt,name=rating,proto3" json:"rating,omitempty"` + Address string `protobuf:"bytes,6,opt,name=address,proto3" json:"address,omitempty"` + City string `protobuf:"bytes,7,opt,name=city,proto3" json:"city,omitempty"` + PhoneNumber string `protobuf:"bytes,8,opt,name=phoneNumber,proto3" json:"phoneNumber,omitempty"` + Categories []string `protobuf:"bytes,9,rep,name=categories,proto3" json:"categories,omitempty"` + Latitude float64 `protobuf:"fixed64,10,opt,name=latitude,proto3" json:"latitude,omitempty"` + Longitude float64 `protobuf:"fixed64,11,opt,name=longitude,proto3" json:"longitude,omitempty"` +} + +func (x *Place) Reset() { + *x = Place{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Place) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Place) ProtoMessage() {} + +func (x *Place) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Place.ProtoReflect.Descriptor instead. +func (*Place) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{0} +} + +func (x *Place) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Place) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Place) GetImagePath() string { + if x != nil { + return x.ImagePath + } + return "" +} + +func (x *Place) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Place) GetRating() int32 { + if x != nil { + return x.Rating + } + return 0 +} + +func (x *Place) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *Place) GetCity() string { + if x != nil { + return x.City + } + return "" +} + +func (x *Place) GetPhoneNumber() string { + if x != nil { + return x.PhoneNumber + } + return "" +} + +func (x *Place) GetCategories() []string { + if x != nil { + return x.Categories + } + return nil +} + +func (x *Place) GetLatitude() float64 { + if x != nil { + return x.Latitude + } + return 0 +} + +func (x *Place) GetLongitude() float64 { + if x != nil { + return x.Longitude + } + return 0 +} + +type GetPlacesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Limit int32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` +} + +func (x *GetPlacesRequest) Reset() { + *x = GetPlacesRequest{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPlacesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPlacesRequest) ProtoMessage() {} + +func (x *GetPlacesRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPlacesRequest.ProtoReflect.Descriptor instead. +func (*GetPlacesRequest) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{1} +} + +func (x *GetPlacesRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *GetPlacesRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +type GetPlacesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Places []*Place `protobuf:"bytes,1,rep,name=places,proto3" json:"places,omitempty"` +} + +func (x *GetPlacesResponse) Reset() { + *x = GetPlacesResponse{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPlacesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPlacesResponse) ProtoMessage() {} + +func (x *GetPlacesResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPlacesResponse.ProtoReflect.Descriptor instead. +func (*GetPlacesResponse) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{2} +} + +func (x *GetPlacesResponse) GetPlaces() []*Place { + if x != nil { + return x.Places + } + return nil +} + +type GetPlaceRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetPlaceRequest) Reset() { + *x = GetPlaceRequest{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPlaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPlaceRequest) ProtoMessage() {} + +func (x *GetPlaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPlaceRequest.ProtoReflect.Descriptor instead. +func (*GetPlaceRequest) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{3} +} + +func (x *GetPlaceRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +type GetPlaceResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Place *Place `protobuf:"bytes,1,opt,name=place,proto3" json:"place,omitempty"` +} + +func (x *GetPlaceResponse) Reset() { + *x = GetPlaceResponse{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPlaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPlaceResponse) ProtoMessage() {} + +func (x *GetPlaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPlaceResponse.ProtoReflect.Descriptor instead. +func (*GetPlaceResponse) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{4} +} + +func (x *GetPlaceResponse) GetPlace() *Place { + if x != nil { + return x.Place + } + return nil +} + +type SearchPlacesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Category int32 `protobuf:"varint,2,opt,name=category,proto3" json:"category,omitempty"` + City int32 `protobuf:"varint,3,opt,name=city,proto3" json:"city,omitempty"` + Limit int32 `protobuf:"varint,4,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,5,opt,name=offset,proto3" json:"offset,omitempty"` +} + +func (x *SearchPlacesRequest) Reset() { + *x = SearchPlacesRequest{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchPlacesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchPlacesRequest) ProtoMessage() {} + +func (x *SearchPlacesRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchPlacesRequest.ProtoReflect.Descriptor instead. +func (*SearchPlacesRequest) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{5} +} + +func (x *SearchPlacesRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *SearchPlacesRequest) GetCategory() int32 { + if x != nil { + return x.Category + } + return 0 +} + +func (x *SearchPlacesRequest) GetCity() int32 { + if x != nil { + return x.City + } + return 0 +} + +func (x *SearchPlacesRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *SearchPlacesRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +type SearchPlacesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Places []*Place `protobuf:"bytes,1,rep,name=places,proto3" json:"places,omitempty"` +} + +func (x *SearchPlacesResponse) Reset() { + *x = SearchPlacesResponse{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchPlacesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchPlacesResponse) ProtoMessage() {} + +func (x *SearchPlacesResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchPlacesResponse.ProtoReflect.Descriptor instead. +func (*SearchPlacesResponse) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{6} +} + +func (x *SearchPlacesResponse) GetPlaces() []*Place { + if x != nil { + return x.Places + } + return nil +} + +type GetPlacesByCategoryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Category string `protobuf:"bytes,1,opt,name=category,proto3" json:"category,omitempty"` + Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` +} + +func (x *GetPlacesByCategoryRequest) Reset() { + *x = GetPlacesByCategoryRequest{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPlacesByCategoryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPlacesByCategoryRequest) ProtoMessage() {} + +func (x *GetPlacesByCategoryRequest) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPlacesByCategoryRequest.ProtoReflect.Descriptor instead. +func (*GetPlacesByCategoryRequest) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{7} +} + +func (x *GetPlacesByCategoryRequest) GetCategory() string { + if x != nil { + return x.Category + } + return "" +} + +func (x *GetPlacesByCategoryRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *GetPlacesByCategoryRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +type GetPlacesByCategoryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Places []*Place `protobuf:"bytes,1,rep,name=places,proto3" json:"places,omitempty"` +} + +func (x *GetPlacesByCategoryResponse) Reset() { + *x = GetPlacesByCategoryResponse{} + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetPlacesByCategoryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetPlacesByCategoryResponse) ProtoMessage() {} + +func (x *GetPlacesByCategoryResponse) ProtoReflect() protoreflect.Message { + mi := &file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetPlacesByCategoryResponse.ProtoReflect.Descriptor instead. +func (*GetPlacesByCategoryResponse) Descriptor() ([]byte, []int) { + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP(), []int{8} +} + +func (x *GetPlacesByCategoryResponse) GetPlaces() []*Place { + if x != nil { + return x.Places + } + return nil +} + +var File_internal_pkg_attractions_delivery_grpc_proto_attractions_proto protoreflect.FileDescriptor + +var file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDesc = []byte{ + 0x0a, 0x3e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, + 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x64, 0x65, 0x6c, 0x69, 0x76, + 0x65, 0x72, 0x79, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x61, + 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x0b, 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xad, 0x02, + 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, + 0x6d, 0x61, 0x67, 0x65, 0x50, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x61, 0x74, + 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x63, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x69, 0x74, + 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x01, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x22, 0x40, 0x0a, + 0x10, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, + 0x3f, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, + 0x22, 0x21, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x02, 0x69, 0x64, 0x22, 0x3c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x22, 0x87, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x69, 0x74, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x42, 0x0a, 0x14, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x22, + 0x66, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x49, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x50, 0x6c, + 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x73, 0x32, 0xe9, 0x02, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x4c, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x12, + 0x1d, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, + 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, + 0x2e, 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x49, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x2e, 0x61, + 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, + 0x61, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x61, 0x74, 0x74, + 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x0c, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x61, 0x74, + 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, + 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x6a, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x42, + 0x79, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x27, 0x2e, 0x61, 0x74, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, + 0x73, 0x42, 0x79, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x61, 0x74, 0x74, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x47, 0x65, 0x74, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x73, 0x42, 0x79, 0x43, 0x61, 0x74, 0x65, + 0x67, 0x6f, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x07, + 0x5a, 0x05, 0x2e, 0x3b, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescOnce sync.Once + file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescData = file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDesc +) + +func file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescGZIP() []byte { + file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescOnce.Do(func() { + file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescData) + }) + return file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDescData +} + +var file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_goTypes = []any{ + (*Place)(nil), // 0: attractions.Place + (*GetPlacesRequest)(nil), // 1: attractions.GetPlacesRequest + (*GetPlacesResponse)(nil), // 2: attractions.GetPlacesResponse + (*GetPlaceRequest)(nil), // 3: attractions.GetPlaceRequest + (*GetPlaceResponse)(nil), // 4: attractions.GetPlaceResponse + (*SearchPlacesRequest)(nil), // 5: attractions.SearchPlacesRequest + (*SearchPlacesResponse)(nil), // 6: attractions.SearchPlacesResponse + (*GetPlacesByCategoryRequest)(nil), // 7: attractions.GetPlacesByCategoryRequest + (*GetPlacesByCategoryResponse)(nil), // 8: attractions.GetPlacesByCategoryResponse +} +var file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_depIdxs = []int32{ + 0, // 0: attractions.GetPlacesResponse.places:type_name -> attractions.Place + 0, // 1: attractions.GetPlaceResponse.place:type_name -> attractions.Place + 0, // 2: attractions.SearchPlacesResponse.places:type_name -> attractions.Place + 0, // 3: attractions.GetPlacesByCategoryResponse.places:type_name -> attractions.Place + 1, // 4: attractions.Attractions.GetPlaces:input_type -> attractions.GetPlacesRequest + 3, // 5: attractions.Attractions.GetPlace:input_type -> attractions.GetPlaceRequest + 5, // 6: attractions.Attractions.SearchPlaces:input_type -> attractions.SearchPlacesRequest + 7, // 7: attractions.Attractions.GetPlacesByCategory:input_type -> attractions.GetPlacesByCategoryRequest + 2, // 8: attractions.Attractions.GetPlaces:output_type -> attractions.GetPlacesResponse + 4, // 9: attractions.Attractions.GetPlace:output_type -> attractions.GetPlaceResponse + 6, // 10: attractions.Attractions.SearchPlaces:output_type -> attractions.SearchPlacesResponse + 8, // 11: attractions.Attractions.GetPlacesByCategory:output_type -> attractions.GetPlacesByCategoryResponse + 8, // [8:12] is the sub-list for method output_type + 4, // [4:8] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_init() } +func file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_init() { + if File_internal_pkg_attractions_delivery_grpc_proto_attractions_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDesc, + NumEnums: 0, + NumMessages: 9, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_goTypes, + DependencyIndexes: file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_depIdxs, + MessageInfos: file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_msgTypes, + }.Build() + File_internal_pkg_attractions_delivery_grpc_proto_attractions_proto = out.File + file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_rawDesc = nil + file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_goTypes = nil + file_internal_pkg_attractions_delivery_grpc_proto_attractions_proto_depIdxs = nil +} diff --git a/internal/pkg/attractions/delivery/grpc/gen/attractions_grpc.pb.go b/internal/pkg/attractions/delivery/grpc/gen/attractions_grpc.pb.go new file mode 100644 index 0000000..77b208e --- /dev/null +++ b/internal/pkg/attractions/delivery/grpc/gen/attractions_grpc.pb.go @@ -0,0 +1,241 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v4.25.1 +// source: internal/pkg/attractions/delivery/grpc/proto/attractions.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Attractions_GetPlaces_FullMethodName = "/attractions.Attractions/GetPlaces" + Attractions_GetPlace_FullMethodName = "/attractions.Attractions/GetPlace" + Attractions_SearchPlaces_FullMethodName = "/attractions.Attractions/SearchPlaces" + Attractions_GetPlacesByCategory_FullMethodName = "/attractions.Attractions/GetPlacesByCategory" +) + +// AttractionsClient is the client API for Attractions service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type AttractionsClient interface { + GetPlaces(ctx context.Context, in *GetPlacesRequest, opts ...grpc.CallOption) (*GetPlacesResponse, error) + // rpc CreatePlace(CreatePlaceRequest) returns (CreatePlaceResponse) {} + GetPlace(ctx context.Context, in *GetPlaceRequest, opts ...grpc.CallOption) (*GetPlaceResponse, error) + // rpc UpdatePlace(UpdatePlaceRequest) returns (UpdatePlaceResponse) {} + // rpc DeletePlace(DeletePlaceRequest) returns (DeletePlaceResponse) {} + SearchPlaces(ctx context.Context, in *SearchPlacesRequest, opts ...grpc.CallOption) (*SearchPlacesResponse, error) + GetPlacesByCategory(ctx context.Context, in *GetPlacesByCategoryRequest, opts ...grpc.CallOption) (*GetPlacesByCategoryResponse, error) +} + +type attractionsClient struct { + cc grpc.ClientConnInterface +} + +func NewAttractionsClient(cc grpc.ClientConnInterface) AttractionsClient { + return &attractionsClient{cc} +} + +func (c *attractionsClient) GetPlaces(ctx context.Context, in *GetPlacesRequest, opts ...grpc.CallOption) (*GetPlacesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetPlacesResponse) + err := c.cc.Invoke(ctx, Attractions_GetPlaces_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *attractionsClient) GetPlace(ctx context.Context, in *GetPlaceRequest, opts ...grpc.CallOption) (*GetPlaceResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetPlaceResponse) + err := c.cc.Invoke(ctx, Attractions_GetPlace_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *attractionsClient) SearchPlaces(ctx context.Context, in *SearchPlacesRequest, opts ...grpc.CallOption) (*SearchPlacesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SearchPlacesResponse) + err := c.cc.Invoke(ctx, Attractions_SearchPlaces_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *attractionsClient) GetPlacesByCategory(ctx context.Context, in *GetPlacesByCategoryRequest, opts ...grpc.CallOption) (*GetPlacesByCategoryResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetPlacesByCategoryResponse) + err := c.cc.Invoke(ctx, Attractions_GetPlacesByCategory_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AttractionsServer is the server API for Attractions service. +// All implementations must embed UnimplementedAttractionsServer +// for forward compatibility. +type AttractionsServer interface { + GetPlaces(context.Context, *GetPlacesRequest) (*GetPlacesResponse, error) + // rpc CreatePlace(CreatePlaceRequest) returns (CreatePlaceResponse) {} + GetPlace(context.Context, *GetPlaceRequest) (*GetPlaceResponse, error) + // rpc UpdatePlace(UpdatePlaceRequest) returns (UpdatePlaceResponse) {} + // rpc DeletePlace(DeletePlaceRequest) returns (DeletePlaceResponse) {} + SearchPlaces(context.Context, *SearchPlacesRequest) (*SearchPlacesResponse, error) + GetPlacesByCategory(context.Context, *GetPlacesByCategoryRequest) (*GetPlacesByCategoryResponse, error) + mustEmbedUnimplementedAttractionsServer() +} + +// UnimplementedAttractionsServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedAttractionsServer struct{} + +func (UnimplementedAttractionsServer) GetPlaces(context.Context, *GetPlacesRequest) (*GetPlacesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPlaces not implemented") +} +func (UnimplementedAttractionsServer) GetPlace(context.Context, *GetPlaceRequest) (*GetPlaceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPlace not implemented") +} +func (UnimplementedAttractionsServer) SearchPlaces(context.Context, *SearchPlacesRequest) (*SearchPlacesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SearchPlaces not implemented") +} +func (UnimplementedAttractionsServer) GetPlacesByCategory(context.Context, *GetPlacesByCategoryRequest) (*GetPlacesByCategoryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetPlacesByCategory not implemented") +} +func (UnimplementedAttractionsServer) mustEmbedUnimplementedAttractionsServer() {} +func (UnimplementedAttractionsServer) testEmbeddedByValue() {} + +// UnsafeAttractionsServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AttractionsServer will +// result in compilation errors. +type UnsafeAttractionsServer interface { + mustEmbedUnimplementedAttractionsServer() +} + +func RegisterAttractionsServer(s grpc.ServiceRegistrar, srv AttractionsServer) { + // If the following call pancis, it indicates UnimplementedAttractionsServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Attractions_ServiceDesc, srv) +} + +func _Attractions_GetPlaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPlacesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AttractionsServer).GetPlaces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Attractions_GetPlaces_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AttractionsServer).GetPlaces(ctx, req.(*GetPlacesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Attractions_GetPlace_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPlaceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AttractionsServer).GetPlace(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Attractions_GetPlace_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AttractionsServer).GetPlace(ctx, req.(*GetPlaceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Attractions_SearchPlaces_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SearchPlacesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AttractionsServer).SearchPlaces(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Attractions_SearchPlaces_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AttractionsServer).SearchPlaces(ctx, req.(*SearchPlacesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Attractions_GetPlacesByCategory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetPlacesByCategoryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AttractionsServer).GetPlacesByCategory(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Attractions_GetPlacesByCategory_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AttractionsServer).GetPlacesByCategory(ctx, req.(*GetPlacesByCategoryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Attractions_ServiceDesc is the grpc.ServiceDesc for Attractions service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Attractions_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "attractions.Attractions", + HandlerType: (*AttractionsServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetPlaces", + Handler: _Attractions_GetPlaces_Handler, + }, + { + MethodName: "GetPlace", + Handler: _Attractions_GetPlace_Handler, + }, + { + MethodName: "SearchPlaces", + Handler: _Attractions_SearchPlaces_Handler, + }, + { + MethodName: "GetPlacesByCategory", + Handler: _Attractions_GetPlacesByCategory_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "internal/pkg/attractions/delivery/grpc/proto/attractions.proto", +} diff --git a/internal/pkg/attractions/delivery/grpc/grpc_server.go b/internal/pkg/attractions/delivery/grpc/grpc_server.go new file mode 100644 index 0000000..40a77c2 --- /dev/null +++ b/internal/pkg/attractions/delivery/grpc/grpc_server.go @@ -0,0 +1,111 @@ +package grpc + +//go:generate protoc -I . proto/attractions.proto --go_out=./gen --go-grpc_out=./gen + +import ( + "2024_2_ThereWillBeName/internal/pkg/attractions" + "2024_2_ThereWillBeName/internal/pkg/attractions/delivery/grpc/gen" + "context" +) + +type GrpcAttractionsHandler struct { + gen.UnimplementedAttractionsServer + placeUsecase attractions.PlaceUsecase +} + +func NewGrpcAttractionsHandler(placeUsecase attractions.PlaceUsecase) *GrpcAttractionsHandler { + return &GrpcAttractionsHandler{placeUsecase: placeUsecase} +} + +func (s *GrpcAttractionsHandler) GetPlaces(ctx context.Context, req *gen.GetPlacesRequest) (*gen.GetPlacesResponse, error) { + places, err := s.placeUsecase.GetPlaces(ctx, int(req.Limit), int(req.Offset)) + if err != nil { + return nil, err + } + placesResponse := make([]*gen.Place, len(places)) + for i, place := range places { + placesResponse[i] = &gen.Place{ + Id: uint32(place.ID), + Name: place.Name, + ImagePath: place.ImagePath, + Description: place.Description, + Rating: int32(place.Rating), + Address: place.Address, + City: place.City, + PhoneNumber: place.PhoneNumber, + Categories: place.Categories, + Latitude: place.Latitude, + Longitude: place.Longitude, + } + } + return &gen.GetPlacesResponse{Places: placesResponse}, nil +} + +func (s *GrpcAttractionsHandler) GetPlace(ctx context.Context, req *gen.GetPlaceRequest) (*gen.GetPlaceResponse, error) { + place, err := s.placeUsecase.GetPlace(ctx, uint(req.Id)) + if err != nil { + return nil, err + } + placeResponse := &gen.Place{ + Id: uint32(place.ID), + Name: place.Name, + ImagePath: place.ImagePath, + Description: place.Description, + Rating: int32(place.Rating), + Address: place.Address, + City: place.City, + PhoneNumber: place.PhoneNumber, + Categories: place.Categories, + Latitude: place.Latitude, + Longitude: place.Longitude, + } + return &gen.GetPlaceResponse{Place: placeResponse}, nil +} + +func (s *GrpcAttractionsHandler) SearchPlaces(ctx context.Context, req *gen.SearchPlacesRequest) (*gen.SearchPlacesResponse, error) { + places, err := s.placeUsecase.SearchPlaces(ctx, req.Name, int(req.Category), int(req.City), int(req.Limit), int(req.Offset)) + if err != nil { + return nil, err + } + + placesResponse := make([]*gen.Place, len(places)) + for i, place := range places { + placesResponse[i] = &gen.Place{ + Id: uint32(place.ID), + Name: place.Name, + ImagePath: place.ImagePath, + Description: place.Description, + Rating: int32(place.Rating), + Address: place.Address, + City: place.City, + PhoneNumber: place.PhoneNumber, + Categories: place.Categories, + Latitude: place.Latitude, + Longitude: place.Longitude, + } + } + return &gen.SearchPlacesResponse{Places: placesResponse}, nil +} + +func (s *GrpcAttractionsHandler) GetPlacesByCategory(ctx context.Context, req *gen.GetPlacesByCategoryRequest) (*gen.GetPlacesByCategoryResponse, error) { + places, err := s.placeUsecase.GetPlacesByCategory(ctx, req.Category, int(req.Limit), int(req.Offset)) + if err != nil { + return nil, err + } + placesResponse := make([]*gen.Place, len(places)) + for i, place := range places { + placesResponse[i] = &gen.Place{ + Id: uint32(place.ID), + Name: place.Name, + Description: place.Description, + Rating: int32(place.Rating), + Address: place.Address, + City: place.City, + PhoneNumber: place.PhoneNumber, + Categories: place.Categories, + Latitude: place.Latitude, + Longitude: place.Longitude, + } + } + return &gen.GetPlacesByCategoryResponse{Places: placesResponse}, nil +} diff --git a/internal/pkg/attractions/delivery/grpc/proto/attractions.proto b/internal/pkg/attractions/delivery/grpc/proto/attractions.proto new file mode 100644 index 0000000..43f27b2 --- /dev/null +++ b/internal/pkg/attractions/delivery/grpc/proto/attractions.proto @@ -0,0 +1,67 @@ +syntax = "proto3"; + +package attractions; +option go_package = ".;gen"; + +service Attractions { + rpc GetPlaces(GetPlacesRequest) returns (GetPlacesResponse) {} + //rpc CreatePlace(CreatePlaceRequest) returns (CreatePlaceResponse) {} + rpc GetPlace(GetPlaceRequest) returns (GetPlaceResponse) {} + //rpc UpdatePlace(UpdatePlaceRequest) returns (UpdatePlaceResponse) {} + //rpc DeletePlace(DeletePlaceRequest) returns (DeletePlaceResponse) {} + rpc SearchPlaces(SearchPlacesRequest) returns (SearchPlacesResponse) {} + rpc GetPlacesByCategory(GetPlacesByCategoryRequest) returns (GetPlacesByCategoryResponse) {} +} + +message Place { + uint32 id = 1; + string name = 2; + string imagePath = 3; + string description = 4; + int32 rating = 5; + string address = 6; + string city = 7; + string phoneNumber = 8; + repeated string categories = 9; + double latitude = 10; + double longitude = 11; +} + +message GetPlacesRequest { + int32 limit = 1; + int32 offset = 2; +} + +message GetPlacesResponse { + repeated Place places = 1; +} + +message GetPlaceRequest { + uint32 id = 1; +} + +message GetPlaceResponse { + Place place = 1; +} + +message SearchPlacesRequest { + string name = 1; + int32 category = 2; + int32 city = 3; + int32 limit = 4; + int32 offset = 5; +} + +message SearchPlacesResponse { + repeated Place places = 1; +} + +message GetPlacesByCategoryRequest { + string category = 1; + int32 limit = 2; + int32 offset = 3; +} + +message GetPlacesByCategoryResponse { + repeated Place places = 1; +} diff --git a/internal/pkg/attractions/delivery/http/handler.go b/internal/pkg/attractions/delivery/http/handler.go new file mode 100644 index 0000000..5e878a8 --- /dev/null +++ b/internal/pkg/attractions/delivery/http/handler.go @@ -0,0 +1,345 @@ +package http + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/attractions/delivery/grpc/gen" + httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" + log "2024_2_ThereWillBeName/internal/pkg/logger" + "errors" + "html/template" + "log/slog" + "net/http" + "strconv" + + "github.com/gorilla/mux" +) + +type PlacesHandler struct { + client gen.AttractionsClient + logger *slog.Logger +} + +func NewPlacesHandler(client gen.AttractionsClient, logger *slog.Logger) *PlacesHandler { + return &PlacesHandler{client, logger} +} + +// GetPlaceHandler godoc +// @Summary Get a list of attractions +// @Description Retrieve a list of attractions from the database +// @Produce json +// @Success 200 {array} models.GetPlace "List of attractions" +// @Failure 400 {object} httpresponses.ErrorResponse "Bad request" +// @Failure 500 {object} httpresponses.ErrorResponse "Internal Server Error" +// @Router /attractions [get] +func (h *PlacesHandler) GetPlacesHandler(w http.ResponseWriter, r *http.Request) { + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request for searching attractions") + + offset, err := strconv.Atoi(r.URL.Query().Get("offset")) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + h.logger.Warn("Invalid offset parameter", slog.String("error", err.Error())) + return + } + limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + h.logger.Warn("Invalid limit parameter", slog.String("error", err.Error())) + return + } + places, err := h.client.GetPlaces(r.Context(), &gen.GetPlacesRequest{Limit: int32(limit), Offset: int32(offset)}) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) + h.logger.Error("Couldn't get list of attractions", + slog.Int("limit", limit), + slog.Int("offset", offset), + slog.String("error", err.Error())) + return + } + + h.logger.DebugContext(logCtx, "Successfully retrieved attractions") + + httpresponse.SendJSONResponse(w, places.Places, http.StatusOK, h.logger) +} + +// PostPlaceHandler godoc +// @Summary Create a new place +// @Description Add a new place to the database +// @Accept json +// @Produce json +// @Param place body models.CreatePlace true "Place data" +// @Success 201 {object} httpresponses.ErrorResponse "Place successfully created" +// @Failure 400 {object} httpresponses.ErrorResponse +// @Failure 403 {object} httpresponses.ErrorResponse "Token is missing" +// @Failure 403 {object} httpresponses.ErrorResponse "Invalid token" +// @Failure 422 {object} httpresponses.ErrorResponse +// @Failure 500 {object} httpresponses.ErrorResponse +// @Router /attractions [post] +//func (h *PlacesHandler) PostPlaceHandler(w http.ResponseWriter, r *http.Request) { +// w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';") +// logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) +// h.logger.DebugContext(logCtx, "Handling request for creating a place") +// +// var place models.CreatePlace +// if err := json.NewDecoder(r.Body).Decode(&place); err != nil { +// httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) +// h.logger.Warn("Failed to decode place data", +// slog.String("error", err.Error()), +// slog.String("place_data", fmt.Sprintf("%+v", place))) +// return +// } +// v := validator.New() +// if models.ValidateCreatePlace(v, &place); !v.Valid() { +// httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger) +// return +// } +// +// place.Name = template.HTMLEscapeString(place.Name) +// place.ImagePath = template.HTMLEscapeString(place.ImagePath) +// place.Description = template.HTMLEscapeString(place.Description) +// place.Address = template.HTMLEscapeString(place.Address) +// place.PhoneNumber = template.HTMLEscapeString(place.PhoneNumber) +// +// if err := h.uc.CreatePlace(r.Context(), place); err != nil { +// httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) +// h.logger.Error("Failed to create place", +// slog.String("placeName", place.Name), +// slog.String("error", err.Error())) +// return +// } +// +// h.logger.DebugContext(logCtx, "Successfully created place") +// +// httpresponse.SendJSONResponse(w, "Place succesfully created", http.StatusCreated, h.logger) +//} +// +//// PutPlaceHandler godoc +//// @Summary Update an existing place +//// @Description Update the details of an existing place in the database +//// @Accept json +//// @Produce json +//// @Param place body models.UpdatePlace true "Updated place data" +//// @Success 200 {object} httpresponses.ErrorResponse "Place successfully updated" +//// @Failure 400 {object} httpresponses.ErrorResponse +//// @Failure 403 {object} httpresponses.ErrorResponse "Token is missing" +//// @Failure 403 {object} httpresponses.ErrorResponse "Invalid token" +//// @Failure 422 {object} httpresponses.ErrorResponse +//// @Failure 500 {object} httpresponses.ErrorResponse +//// @Router /attractions/{id} [put] +//func (h *PlacesHandler) PutPlaceHandler(w http.ResponseWriter, r *http.Request) { +// w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';") +// logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) +// h.logger.DebugContext(logCtx, "Handling request for updating a place") +// +// var place models.UpdatePlace +// +// if err := json.NewDecoder(r.Body).Decode(&place); err != nil { +// httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) +// h.logger.Warn("Failed to decode place data", +// slog.String("error", err.Error()), +// slog.String("place_data", fmt.Sprintf("%+v", place))) +// return +// } +// +// v := validator.New() +// if models.ValidateUpdatePlace(v, &place); !v.Valid() { +// httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger) +// return +// } +// +// place.Name = template.HTMLEscapeString(place.Name) +// place.ImagePath = template.HTMLEscapeString(place.ImagePath) +// place.Description = template.HTMLEscapeString(place.Description) +// place.Address = template.HTMLEscapeString(place.Address) +// place.PhoneNumber = template.HTMLEscapeString(place.PhoneNumber) +// +// if err := h.uc.UpdatePlace(r.Context(), place); err != nil { +// httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) +// h.logger.Error("Failed to update place", +// slog.Int("placeID", place.ID), +// slog.String("error", err.Error())) +// return +// } +// +// h.logger.DebugContext(logCtx, "Successfully updated place") +// +// httpresponse.SendJSONResponse(w, "place successfully updated", http.StatusOK, h.logger) +//} +// +//// DeletePlaceHandler godoc +//// @Summary Delete an existing place +//// @Description Remove a place from the database by its name +//// @Produce json +//// @Param name body string true "Name of the place to be deleted" +//// @Success 200 {object} httpresponses.ErrorResponse "Place successfully deleted" +//// @Failure 400 {object} httpresponses.ErrorResponse +//// @Failure 403 {object} httpresponses.ErrorResponse "Token is missing" +//// @Failure 403 {object} httpresponses.ErrorResponse "Invalid token" +//// @Failure 500 {object} httpresponses.ErrorResponse +//// @Router /attractions/{id} [delete] +//func (h *PlacesHandler) DeletePlaceHandler(w http.ResponseWriter, r *http.Request) { +// +// idStr := mux.Vars(r)["id"] +// +// logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) +// h.logger.DebugContext(logCtx, "Handling request for deleting a place by ID", slog.String("placeID", idStr)) +// +// id, err := strconv.ParseUint(idStr, 10, 64) +// if err != nil { +// httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) +// h.logger.Warn("Failed to parse place ID", slog.String("placeID", idStr), slog.String("error", err.Error())) +// return +// } +// if err := h.uc.DeletePlace(r.Context(), uint(id)); err != nil { +// if errors.Is(err, models.ErrNotFound) { +// httpresponse.SendJSONResponse(w, nil, http.StatusNotFound, h.logger) +// h.logger.Warn("Place not found", slog.String("placeID", idStr), slog.String("error", err.Error())) +// return +// } +// httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) +// h.logger.Error("Failed to delete a place", slog.String("placeID", idStr), slog.String("error", err.Error())) +// return +// } +// httpresponse.SendJSONResponse(w, "place successfully deleted", http.StatusOK, h.logger) +// +// h.logger.DebugContext(logCtx, "Successfully updated place") +//} + +// GetPlaceHandler godoc +// @Summary Retrieve an existing place +// @Description Get details of a place from the database by its id +// @Produce json +// @Param id body int true "ID of the place to retrieve" +// @Success 200 {object} models.GetPlace "Details of the requested place" +// @Failure 400 {object} httpresponses.ErrorResponse +// @Failure 500 {object} httpresponses.ErrorResponse +// @Router /attractions/{id} [get] +func (h *PlacesHandler) GetPlaceHandler(w http.ResponseWriter, r *http.Request) { + idStr := mux.Vars(r)["id"] + + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request for getting a place by ID", slog.String("placeID", idStr)) + + id, err := strconv.ParseUint(idStr, 10, 64) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + h.logger.Warn("Failed to parse place ID", slog.String("placeID", idStr), slog.String("error", err.Error())) + return + } + place, err := h.client.GetPlace(r.Context(), &gen.GetPlaceRequest{Id: uint32(id)}) + if err != nil { + if errors.Is(err, models.ErrNotFound) { + response := httpresponse.ErrorResponse{ + Message: "place not found", + } + httpresponse.SendJSONResponse(w, response, http.StatusNotFound, h.logger) + h.logger.Warn("Place not found", slog.String("placeID", idStr), slog.String("error", err.Error())) + return + } + httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) + h.logger.Error("Failed to get a place", slog.String("placeID", idStr), slog.String("error", err.Error())) + return + } + httpresponse.SendJSONResponse(w, place.Place, http.StatusOK, h.logger) + + h.logger.DebugContext(logCtx, "Successfully getting place") +} + +// GetPlacesByNameHandler godoc +// @Summary Retrieve attractions by search string +// @Description Get a list of attractions from the database that match the provided search string +// @Produce json +// @Param searchString body string true "Name of the attractions to retrieve" +// @Success 200 {object} models.GetPlace "List of attractions matching the provided searchString" +// @Failure 400 {object} httpresponses.ErrorResponse +// @Failure 500 {object} httpresponses.ErrorResponse +// @Router /attractions/search/{placeName} [get] +func (h *PlacesHandler) SearchPlacesHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';") + placeName := r.URL.Query().Get("placeName") + placeName = template.HTMLEscapeString(placeName) + + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request for searching attractions by place name", slog.String("placeName", placeName)) + + offset, err := strconv.Atoi(r.URL.Query().Get("offset")) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + h.logger.Warn("Invalid offset parameter", slog.String("error", err.Error())) + return + } + limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + h.logger.Warn("Invalid limit parameter", slog.String("error", err.Error())) + return + } + + cityStr := r.URL.Query().Get("city") + categoryStr := r.URL.Query().Get("category") + + var city, category int + if cityStr != "" { + city, err = strconv.Atoi(cityStr) + if err != nil { + h.logger.Warn("Invalid city parameter", slog.String("error", err.Error())) + httpresponse.SendJSONResponse(w, httpresponse.ErrorResponse{ + Message: "Invalid city parameter", + }, http.StatusBadRequest, h.logger) + return + } + } + + if categoryStr != "" { + category, err = strconv.Atoi(categoryStr) + if err != nil { + h.logger.Warn("Invalid category parameter", slog.String("error", err.Error())) + httpresponse.SendJSONResponse(w, httpresponse.ErrorResponse{ + Message: "Invalid category parameter", + }, http.StatusBadRequest, h.logger) + return + } + } + + places, err := h.client.SearchPlaces(r.Context(), &gen.SearchPlacesRequest{Name: placeName, Category: int32(category), City: int32(city), Limit: int32(limit), Offset: int32(offset)}) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) + h.logger.Error("Failed to search attractions", slog.String("placeName", placeName), slog.String("error", err.Error())) + return + } + httpresponse.SendJSONResponse(w, places.Places, http.StatusOK, h.logger) + + h.logger.DebugContext(logCtx, "Successfully getting attractions by name") +} + +func (h *PlacesHandler) GetPlacesByCategoryHandler(w http.ResponseWriter, r *http.Request) { + categoryName := mux.Vars(r)["categoryName"] + + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request for searching attractions by category", slog.String("categoryName", categoryName)) + + offset, err := strconv.Atoi(r.URL.Query().Get("offset")) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + h.logger.Warn("Invalid offset parameter", slog.String("error", err.Error())) + return + } + limit, err := strconv.Atoi(r.URL.Query().Get("limit")) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + h.logger.Warn("Invalid limit parameter", slog.String("error", err.Error())) + return + } + + places, err := h.client.GetPlacesByCategory(r.Context(), &gen.GetPlacesByCategoryRequest{Category: categoryName, Limit: int32(limit), Offset: int32(offset)}) + if err != nil { + httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) + h.logger.Error("Error getting attractions by category", + slog.Int("limit", limit), + slog.Int("offset", offset), + slog.String("categoryName", categoryName), + slog.String("error", err.Error())) + return + } + httpresponse.SendJSONResponse(w, places.Places, http.StatusOK, h.logger) +} diff --git a/internal/pkg/attractions/delivery/http/handler_test.go b/internal/pkg/attractions/delivery/http/handler_test.go new file mode 100644 index 0000000..872c5d3 --- /dev/null +++ b/internal/pkg/attractions/delivery/http/handler_test.go @@ -0,0 +1,671 @@ +package http + +// import ( +// "2024_2_ThereWillBeName/internal/models" +// mockplaces "2024_2_ThereWillBeName/internal/pkg/attractions/mocks" +// "bytes" +// "context" +// "encoding/json" +// "log" + +// "errors" +// "log/slog" +// "net/http" +// "net/http/httptest" +// "net/url" +// "os" +// "strconv" + +// "github.com/gorilla/mux" + +// "testing" + +// "github.com/golang/mock/gomock" +// "github.com/stretchr/testify/assert" +// ) + +// const errorParse = "ErrorParse" + +// func TestGetPlacesHandler(t *testing.T) { +// places := []models.GetPlace{ +// { +// ID: 1, +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// NumberOfReviews: 2500, +// Address: "59th St to 110th St, New York, NY 10022", +// City: "New York", +// PhoneNumber: "+1 212-310-6600", +// Categories: []string{"Park", "Recreation", "Nature"}, +// }, +// { +// ID: 2, +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// NumberOfReviews: 2500, +// Address: "59th St to 110th St, New York, NY 10022", +// City: "New York", +// PhoneNumber: "+1 212-310-6600", +// Categories: []string{"Park", "Recreation", "Nature"}, +// }, +// } + +// jsonPlaces, _ := json.Marshal(places) +// stringPlaces := string(jsonPlaces) + +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// h := slog.NewJSONHandler(os.Stdout, nil) + +// logger := slog.New(h) + +// mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) +// handler := NewPlacesHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// offset string +// limit string +// mockPlaces []models.GetPlace +// mockError error +// expectedCode int +// expectedBody string +// }{ +// { +// name: "Valid request", +// offset: "0", +// limit: "10", +// mockPlaces: places, +// mockError: nil, +// expectedCode: http.StatusOK, +// expectedBody: stringPlaces + "\n", +// }, +// { +// name: "Invalid offset", +// offset: "invalid", +// limit: "10", +// mockPlaces: nil, +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: ``, +// }, +// { +// name: "Invalid limit", +// offset: "0", +// limit: "invalid", +// mockPlaces: nil, +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: ``, +// }, +// { +// name: "Internal server error", +// offset: "0", +// limit: "10", +// mockPlaces: nil, +// mockError: errors.New("internal server error"), +// expectedCode: http.StatusInternalServerError, +// expectedBody: ``, +// }, +// } + +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// if tt.name != "Invalid offset" && tt.name != "Invalid limit" { +// offset, _ := strconv.Atoi(tt.offset) +// limit, _ := strconv.Atoi(tt.limit) +// mockUsecase.EXPECT().GetPlaces(gomock.Any(), limit, offset).Return(tt.mockPlaces, tt.mockError) +// } + +// req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/places", nil) +// assert.NoError(t, err) + +// query := url.Values{} +// query.Add("offset", tt.offset) +// query.Add("limit", tt.limit) +// req.URL.RawQuery = query.Encode() + +// rr := httptest.NewRecorder() +// router := mux.NewRouter() +// router.HandleFunc("/attractions", handler.GetPlacesHandler) +// router.ServeHTTP(rr, req) + +// assert.Equal(t, tt.expectedCode, rr.Code) +// assert.Equal(t, tt.expectedBody, rr.Body.String()) +// }) +// } +// } + +// func TestPostPlacesHandler(t *testing.T) { +// place := models.CreatePlace{ +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// NumberOfReviews: 2500, +// Address: "59th St to 110th St, New York, NY 10022", +// CityId: 1, +// PhoneNumber: "+1 212-310-6600", +// CategoriesId: []int{1, 2}, +// } + +// jsonPlace, _ := json.Marshal(place) +// //reader := bytes.NewReader(jsonPlace) +// //stringPlace := string(jsonPlace) + +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// h := slog.NewJSONHandler(os.Stdout, nil) + +// logger := slog.New(h) + +// mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) +// handler := NewPlacesHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// mockReturn []byte +// mockError error +// expectedCode int +// expectedBody string +// }{ +// { +// name: "Success", +// mockReturn: jsonPlace, +// mockError: nil, +// expectedCode: http.StatusCreated, +// expectedBody: "\"Place succesfully created\"\n", +// }, +// { +// name: "ErrorINternal", +// mockReturn: jsonPlace, +// mockError: errors.New("dcd"), +// expectedCode: http.StatusInternalServerError, +// expectedBody: "", +// }, +// { +// name: errorParse, +// mockReturn: []byte(`{ "name": "Test Place", "imagePath": "/path/to/image", "description": "Test Description", "rating": 4.5, "numberOfReviews": 10, "address": "Test Address", "cityId": 1, "phoneNumber": "1234567890", "categoriesId": [1, 2, 3]`), +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: "", +// }, +// } + +// for _, testcase := range tests { +// t.Run(testcase.name, func(t *testing.T) { +// if testcase.name != errorParse { +// mockUsecase.EXPECT().CreatePlace(context.Background(), gomock.Any()).Return(testcase.mockError) +// } + +// req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, "/places", bytes.NewBuffer(testcase.mockReturn)) +// assert.NoError(t, err) + +// rr := httptest.NewRecorder() +// handler.PostPlaceHandler(rr, req) + +// assert.Equal(t, testcase.expectedCode, rr.Code) +// assert.Equal(t, testcase.expectedBody, rr.Body.String()) +// }) +// } +// } + +// func TestPutPlacesHandler(t *testing.T) { +// place := models.UpdatePlace{ +// ID: 1, +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// NumberOfReviews: 2500, +// Address: "59th St to 110th St, New York, NY 10022", +// CityId: 1, +// PhoneNumber: "+1 212-310-6600", +// CategoriesId: []int{1, 2}, +// } + +// jsonPlace, _ := json.Marshal(place) +// //reader := bytes.NewReader(jsonPlace) +// //stringPlace := string(jsonPlace) + +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// h := slog.NewJSONHandler(os.Stdout, nil) + +// logger := slog.New(h) +// mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) +// handler := NewPlacesHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// mockReturn []byte +// mockError error +// expectedCode int +// expectedBody string +// }{ +// { +// name: "Success", +// mockReturn: jsonPlace, +// mockError: nil, +// expectedCode: http.StatusOK, +// expectedBody: "\"place successfully updated\"\n", +// }, +// { +// name: "ErrorINternal", +// mockReturn: jsonPlace, +// mockError: errors.New("dcd"), +// expectedCode: http.StatusInternalServerError, +// expectedBody: "", +// }, +// { +// name: errorParse, +// mockReturn: []byte(`{ "name": "Test Place", "imagePath": "/path/to/image", "description": "Test Description", "rating": 4.5, "numberOfReviews": 10, "address": "Test Address", "cityId": 1, "phoneNumber": "1234567890", "categoriesId": [1, 2, 3]`), +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: "", +// }, +// } + +// for _, testcase := range tests { +// t.Run(testcase.name, func(t *testing.T) { +// if testcase.name != errorParse { +// mockUsecase.EXPECT().UpdatePlace(context.Background(), gomock.Any()).Return(testcase.mockError) +// } + +// req, err := http.NewRequestWithContext(context.Background(), http.MethodPut, "/places/1", bytes.NewBuffer(testcase.mockReturn)) +// assert.NoError(t, err) + +// rr := httptest.NewRecorder() +// handler.PutPlaceHandler(rr, req) + +// assert.Equal(t, testcase.expectedCode, rr.Code) +// assert.Equal(t, testcase.expectedBody, rr.Body.String()) +// }) +// } +// } + +// func TestDeletePlacesHandler(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// h := slog.NewJSONHandler(os.Stdout, nil) + +// logger := slog.New(h) +// mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) +// handler := NewPlacesHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// id int +// mockError error +// expectedCode int +// expectedBody string +// }{ +// { +// name: "Success", +// id: 1, +// mockError: nil, +// expectedCode: http.StatusOK, +// expectedBody: "\"place successfully deleted\"\n", +// }, +// { +// name: "ErrorINternal", +// id: 1, +// mockError: errors.New("dcd"), +// expectedCode: http.StatusInternalServerError, +// expectedBody: "", +// }, +// { +// name: errorParse, +// id: -1, +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: "", +// }, +// } + +// for _, testcase := range tests { +// t.Run(testcase.name, func(t *testing.T) { +// if testcase.name != errorParse { +// mockUsecase.EXPECT().DeletePlace(gomock.Any(), uint(testcase.id)).Return(testcase.mockError) +// } + +// req, err := http.NewRequestWithContext(context.Background(), http.MethodDelete, "/attractions/"+strconv.Itoa(int(testcase.id)), nil) +// assert.NoError(t, err) + +// rr := httptest.NewRecorder() +// router := mux.NewRouter() +// router.HandleFunc("/attractions/{id}", handler.DeletePlaceHandler).Methods(http.MethodDelete) +// router.ServeHTTP(rr, req) + +// assert.Equal(t, testcase.expectedCode, rr.Code) +// assert.Equal(t, testcase.expectedBody, rr.Body.String()) +// }) +// } +// } + +// func TestGetPlaceHandler(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// h := slog.NewJSONHandler(os.Stdout, nil) + +// logger := slog.New(h) +// mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) +// handler := NewPlacesHandler(mockUsecase, logger) + +// place := models.GetPlace{ +// ID: 1, +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// NumberOfReviews: 2500, +// Address: "59th St to 110th St, New York, NY 10022", +// City: "New York", +// PhoneNumber: "+1 212-310-6600", +// Categories: []string{"Park", "Recreation", "Nature"}, +// } + +// jsonPlaces, _ := json.Marshal(place) +// stringPlace := string(jsonPlaces) + +// tests := []struct { +// name string +// id int +// mockPlace models.GetPlace +// mockError error +// expectedCode int +// expectedBody string +// }{ +// { +// name: "Success", +// id: 1, +// mockPlace: place, +// mockError: nil, +// expectedCode: http.StatusOK, +// expectedBody: stringPlace + "\n", +// }, +// { +// name: "NotFound", +// id: 2, +// mockPlace: models.GetPlace{}, +// mockError: models.ErrNotFound, +// expectedCode: http.StatusNotFound, +// expectedBody: `{"message":"place not found"}` + "\n", +// }, +// { +// name: "InternalServerError", +// id: 3, +// mockPlace: models.GetPlace{}, +// mockError: errors.New("internal server error"), +// expectedCode: http.StatusInternalServerError, +// expectedBody: ``, +// }, +// { +// name: "InvalidID", +// id: -1, +// mockPlace: models.GetPlace{}, +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: "", +// }, +// } + +// for _, testcase := range tests { +// t.Run(testcase.name, func(t *testing.T) { +// if testcase.name != "InvalidID" { +// mockUsecase.EXPECT().GetPlace(gomock.Any(), uint(testcase.id)).Return(testcase.mockPlace, testcase.mockError) +// } + +// req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/attractions/"+strconv.Itoa(int(testcase.id)), nil) +// assert.NoError(t, err) + +// rr := httptest.NewRecorder() +// router := mux.NewRouter() +// router.HandleFunc("/attractions/{id}", handler.GetPlaceHandler).Methods(http.MethodGet) +// router.ServeHTTP(rr, req) + +// assert.Equal(t, testcase.expectedCode, rr.Code) +// assert.Equal(t, testcase.expectedBody, rr.Body.String()) +// }) +// } +// } + +// func TestSearchPlaceHandler(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// h := slog.NewJSONHandler(os.Stdout, nil) + +// logger := slog.New(h) +// mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) +// handler := NewPlacesHandler(mockUsecase, logger) + +// places := []models.GetPlace{ +// { +// ID: 1, +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// NumberOfReviews: 2500, +// Address: "59th St to 110th St, New York, NY 10022", +// City: "New York", +// PhoneNumber: "+1 212-310-6600", +// Categories: []string{"Park", "Recreation", "Nature"}, +// }, +// { +// ID: 2, +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// NumberOfReviews: 2500, +// Address: "59th St to 110th St, New York, NY 10022", +// City: "New York", +// PhoneNumber: "+1 212-310-6600", +// Categories: []string{"Park", "Recreation", "Nature"}, +// }, +// } + +// jsonPlaces, _ := json.Marshal(places) +// stringPlace := string(jsonPlaces) + +// tests := []struct { +// name string +// limit string +// offset string +// search string +// mockPlaces []models.GetPlace +// mockError error +// expectedCode int +// expectedBody string +// }{ +// { +// name: "Success", +// search: "test", +// limit: "10", +// offset: "0", +// mockPlaces: places, +// mockError: nil, +// expectedCode: http.StatusOK, +// expectedBody: stringPlace + "\n", +// }, +// { +// name: "InternalServerError", +// search: "badSearch", +// limit: "10", +// offset: "0", +// mockPlaces: []models.GetPlace{}, +// mockError: errors.New("internal server error"), +// expectedCode: http.StatusInternalServerError, +// expectedBody: ``, +// }, +// { +// name: "InvalidOffset", +// search: "test", +// offset: "invalid", +// limit: "10", +// mockPlaces: []models.GetPlace{}, +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: ``, +// }, +// { +// name: "InvalidLimit", +// search: "test", +// offset: "0", +// limit: "invalid", +// mockPlaces: []models.GetPlace{}, +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: ``, +// }, +// } + +// for _, testcase := range tests { +// t.Run(testcase.name, func(t *testing.T) { +// if testcase.name != "InvalidOffset" && testcase.name != "InvalidLimit" && testcase.name != "MissingPlaceName" { +// mockUsecase.EXPECT().SearchPlaces(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testcase.mockPlaces, testcase.mockError) +// } +// urlStr := "/attractions/search/" + testcase.search +// log.Println(urlStr) +// req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, urlStr, nil) +// assert.NoError(t, err) + +// query := url.Values{} +// query.Add("offset", testcase.offset) +// query.Add("limit", testcase.limit) +// req.URL.RawQuery = query.Encode() + +// rr := httptest.NewRecorder() +// router := mux.NewRouter() +// router.HandleFunc("/attractions/search/{search}", handler.SearchPlacesHandler).Methods(http.MethodGet) +// router.ServeHTTP(rr, req) + +// assert.Equal(t, testcase.expectedCode, rr.Code) +// assert.Equal(t, testcase.expectedBody, rr.Body.String()) +// }) +// } +// } + +// func TestGetPlacesByCategoryHandler(t *testing.T) { +// places := []models.GetPlace{ +// { +// ID: 1, +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// Address: "59th St to 110th St, New York, NY 10022", +// City: "New York", +// PhoneNumber: "+1 212-310-6600", +// Categories: []string{"Park", "Recreation", "Nature"}, +// }, +// { +// ID: 2, +// Name: "Central Park", +// ImagePath: "/images/central_park.jpg", +// Description: "A large public park in New York City, offering a variety of recreational activities.", +// Rating: 5, +// Address: "59th St to 110th St, New York, NY 10022", +// City: "New York", +// PhoneNumber: "+1 212-310-6600", +// Categories: []string{"Park", "Recreation", "Nature"}, +// }, +// } + +// jsonPlaces, _ := json.Marshal(places) +// stringPlaces := string(jsonPlaces) + +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// h := slog.NewJSONHandler(os.Stdout, nil) + +// logger := slog.New(h) + +// mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) +// handler := NewPlacesHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// offset string +// limit string +// mockPlaces []models.GetPlace +// mockError error +// expectedCode int +// expectedBody string +// }{ +// { +// name: "Valid request", +// offset: "0", +// limit: "10", +// mockPlaces: places, +// mockError: nil, +// expectedCode: http.StatusOK, +// expectedBody: stringPlaces + "\n", +// }, +// { +// name: "Invalid offset", +// offset: "invalid", +// limit: "10", +// mockPlaces: nil, +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: ``, +// }, +// { +// name: "Invalid limit", +// offset: "0", +// limit: "invalid", +// mockPlaces: nil, +// mockError: nil, +// expectedCode: http.StatusBadRequest, +// expectedBody: ``, +// }, +// { +// name: "Internal server error", +// offset: "0", +// limit: "10", +// mockPlaces: nil, +// mockError: errors.New("internal server error"), +// expectedCode: http.StatusInternalServerError, +// expectedBody: ``, +// }, +// } + +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// if tt.name != "Invalid offset" && tt.name != "Invalid limit" { +// offset, _ := strconv.Atoi(tt.offset) +// limit, _ := strconv.Atoi(tt.limit) +// mockUsecase.EXPECT().GetPlacesByCategory(gomock.Any(), gomock.Any(), limit, offset).Return(tt.mockPlaces, tt.mockError) +// } + +// req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/places/category/{categoryName}", nil) +// assert.NoError(t, err) + +// query := url.Values{} +// query.Add("offset", tt.offset) +// query.Add("limit", tt.limit) +// req.URL.RawQuery = query.Encode() + +// rr := httptest.NewRecorder() +// router := mux.NewRouter() +// router.HandleFunc("/attractions/category/{categoryName}", handler.GetPlacesByCategoryHandler) +// router.ServeHTTP(rr, req) + +// assert.Equal(t, tt.expectedCode, rr.Code) +// assert.Equal(t, tt.expectedBody, rr.Body.String()) +// }) +// } +// } diff --git a/internal/pkg/places/interfaces.go b/internal/pkg/attractions/interfaces.go similarity index 81% rename from internal/pkg/places/interfaces.go rename to internal/pkg/attractions/interfaces.go index 98e77f9..7a974d0 100644 --- a/internal/pkg/places/interfaces.go +++ b/internal/pkg/attractions/interfaces.go @@ -1,4 +1,4 @@ -package places +package attractions import ( "2024_2_ThereWillBeName/internal/models" @@ -13,7 +13,7 @@ type PlaceRepo interface { GetPlace(ctx context.Context, id uint) (models.GetPlace, error) UpdatePlace(ctx context.Context, place models.UpdatePlace) error DeletePlace(ctx context.Context, id uint) error - SearchPlaces(ctx context.Context, name string, limit, offset int) ([]models.GetPlace, error) + SearchPlaces(ctx context.Context, name string, category, city, limit, offset int) ([]models.GetPlace, error) GetPlacesByCategory(ctx context.Context, category string, limit, offset int) ([]models.GetPlace, error) } @@ -23,6 +23,6 @@ type PlaceUsecase interface { GetPlace(ctx context.Context, id uint) (models.GetPlace, error) UpdatePlace(ctx context.Context, place models.UpdatePlace) error DeletePlace(ctx context.Context, id uint) error - SearchPlaces(ctx context.Context, name string, limit, offset int) ([]models.GetPlace, error) + SearchPlaces(ctx context.Context, name string, category, city, limit, offset int) ([]models.GetPlace, error) GetPlacesByCategory(ctx context.Context, category string, limit, offset int) ([]models.GetPlace, error) } diff --git a/internal/pkg/places/mocks/mock.go b/internal/pkg/attractions/mocks/mock.go similarity index 99% rename from internal/pkg/places/mocks/mock.go rename to internal/pkg/attractions/mocks/mock.go index 2a97461..cfdb0ab 100644 --- a/internal/pkg/places/mocks/mock.go +++ b/internal/pkg/attractions/mocks/mock.go @@ -1,8 +1,8 @@ // Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go -// Package mock_places is a generated GoMock package. -package mock_places +// Package mock_attractions is a generated GoMock package. +package mock_attractions import ( models "2024_2_ThereWillBeName/internal/models" diff --git a/internal/pkg/places/repo/place_repository.go b/internal/pkg/attractions/repo/place_repository.go similarity index 85% rename from internal/pkg/places/repo/place_repository.go rename to internal/pkg/attractions/repo/place_repository.go index 2737707..b615b6c 100644 --- a/internal/pkg/places/repo/place_repository.go +++ b/internal/pkg/attractions/repo/place_repository.go @@ -23,7 +23,7 @@ func (r *PlaceRepository) GetPlaces(ctx context.Context, limit, offset int) ([]m query := "SELECT p.id, p.name, p.image_path, p.description, p.rating, p.address, p.phone_number, p.latitude, p.longitude, c.name AS city_name, ARRAY_AGG(ca.name) AS categories FROM place p JOIN city c ON p.city_id = c.id JOIN place_category pc ON p.id = pc.place_id JOIN category ca ON pc.category_id = ca.id GROUP BY p.id, c.name ORDER BY p.id LIMIT $1 OFFSET $2" rows, err := r.db.QueryContext(ctx, query, limit, offset) if err != nil { - return nil, fmt.Errorf("couldn't get places: %w", err) + return nil, fmt.Errorf("couldn't get attractions: %w", err) } defer rows.Close() var places []models.GetPlace @@ -31,7 +31,7 @@ func (r *PlaceRepository) GetPlaces(ctx context.Context, limit, offset int) ([]m var place models.GetPlace err := rows.Scan(&place.ID, &place.Name, &place.ImagePath, &place.Description, &place.Rating, &place.Address, &place.PhoneNumber, &place.Latitude, &place.Longitude, &place.City, pq.Array(&place.Categories)) if err != nil { - return nil, fmt.Errorf("couldn't unmarshal list of places: %w", err) + return nil, fmt.Errorf("couldn't unmarshal list of attractions: %w", err) } places = append(places, place) } @@ -128,15 +128,52 @@ func (r *PlaceRepository) DeletePlace(ctx context.Context, id uint) error { return nil } -func (r *PlaceRepository) SearchPlaces(ctx context.Context, name string, limit, offset int) ([]models.GetPlace, error) { +func (r *PlaceRepository) SearchPlaces(ctx context.Context, name string, category, city, limit, offset int) ([]models.GetPlace, error) { var places []models.GetPlace - query := "SELECT p.id, p.name, p.image_path, p.description, p.rating, p.address, p.phone_number, p.latitude, p.longitude, c.name AS city_name, ARRAY_AGG(ca.name) AS categories FROM place p JOIN city c ON p.city_id = c.id JOIN place_category pc ON p.id = pc.place_id JOIN category ca ON pc.category_id = ca.id WHERE p.name LIKE '%' || $1 || '%' GROUP BY p.id, c.name ORDER BY p.id LIMIT $2 OFFSET $3" + + query := ` + SELECT + p.id, p.name, p.image_path, p.description, p.rating, + p.address, p.phone_number, c.name AS city_name, + ARRAY_AGG(ca.name) AS categories + FROM + place p + JOIN + city c ON p.city_id = c.id + JOIN + place_category pc ON p.id = pc.place_id + JOIN + category ca ON pc.category_id = ca.id + WHERE + p.name LIKE '%' || $1 || '%'` + + args := []interface{}{name} + + if category > 0 { + query += " AND pc.category_id = $2" + args = append(args, category) + } + + if city > 0 { + query += " AND p.city_id = $" + fmt.Sprint(len(args)+1) + args = append(args, city) + } + + query += ` + GROUP BY + p.id, c.name + ORDER BY + p.id + LIMIT $` + fmt.Sprint(len(args)+1) + ` OFFSET $` + fmt.Sprint(len(args)+2) + args = append(args, limit, offset) + stmt, err := r.db.PrepareContext(ctx, query) if err != nil { return nil, fmt.Errorf("couldn't prepare query: %w", err) } defer stmt.Close() - rows, err := stmt.QueryContext(ctx, name, limit, offset) + + rows, err := stmt.QueryContext(ctx, args...) if err != nil { return nil, fmt.Errorf("couldn't get places: %w", err) } @@ -144,12 +181,13 @@ func (r *PlaceRepository) SearchPlaces(ctx context.Context, name string, limit, for rows.Next() { var place models.GetPlace - err := rows.Scan(&place.ID, &place.Name, &place.ImagePath, &place.Description, &place.Rating, &place.Address, &place.PhoneNumber, &place.Latitude, &place.Longitude, &place.City, pq.Array(&place.Categories)) + err := rows.Scan(&place.ID, &place.Name, &place.ImagePath, &place.Description, &place.Rating, &place.Address, &place.PhoneNumber, &place.City, pq.Array(&place.Categories)) if err != nil { return nil, fmt.Errorf("couldn't unmarshal list of places: %w", err) } places = append(places, place) } + return places, nil } @@ -179,7 +217,7 @@ func (r *PlaceRepository) GetPlacesByCategory(ctx context.Context, category stri OFFSET $3` rows, err := r.db.QueryContext(ctx, query, category, limit, offset) if err != nil { - return nil, fmt.Errorf("couldn't get places by category: %w", err) + return nil, fmt.Errorf("couldn't get attractions by category: %w", err) } defer rows.Close() var places []models.GetPlace @@ -187,7 +225,7 @@ func (r *PlaceRepository) GetPlacesByCategory(ctx context.Context, category stri var place models.GetPlace err := rows.Scan(&place.ID, &place.Name, &place.ImagePath, &place.Description, &place.Rating, &place.Address, &place.PhoneNumber, &place.Latitude, &place.Longitude, &place.City, pq.Array(&place.Categories)) if err != nil { - return nil, fmt.Errorf("couldn't unmarshal list of places: %w", err) + return nil, fmt.Errorf("couldn't unmarshal list of attractions: %w", err) } places = append(places, place) } diff --git a/internal/pkg/places/repo/place_repository_test.go b/internal/pkg/attractions/repo/place_repository_test.go similarity index 98% rename from internal/pkg/places/repo/place_repository_test.go rename to internal/pkg/attractions/repo/place_repository_test.go index ccac6b3..a275b5d 100644 --- a/internal/pkg/places/repo/place_repository_test.go +++ b/internal/pkg/attractions/repo/place_repository_test.go @@ -56,7 +56,7 @@ func TestPlaceRepository_GetPlaces_DbError(t *testing.T) { defer db.Close() mock.ExpectQuery("SELECT id, name, image, description FROM place"). - WillReturnError(fmt.Errorf("couldn't get places: %w", err)) + WillReturnError(fmt.Errorf("couldn't get attractions: %w", err)) r := NewPLaceRepository(db) places, err := r.GetPlaces(context.Background(), 10, 0) @@ -147,7 +147,7 @@ func TestPlaceRepository_GetPlacesByCategory_DbError(t *testing.T) { defer db.Close() mock.ExpectQuery("SELECT id, name, image, description FROM place"). - WillReturnError(fmt.Errorf("couldn't get places: %w", err)) + WillReturnError(fmt.Errorf("couldn't get attractions: %w", err)) r := NewPLaceRepository(db) places, err := r.GetPlacesByCategory(context.Background(), "Park", 10, 0) diff --git a/internal/pkg/places/usecase/usecase.go b/internal/pkg/attractions/usecase/usecase.go similarity index 79% rename from internal/pkg/places/usecase/usecase.go rename to internal/pkg/attractions/usecase/usecase.go index 9482905..1ead9c1 100644 --- a/internal/pkg/places/usecase/usecase.go +++ b/internal/pkg/attractions/usecase/usecase.go @@ -2,15 +2,15 @@ package usecase import ( "2024_2_ThereWillBeName/internal/models" - "2024_2_ThereWillBeName/internal/pkg/places" + "2024_2_ThereWillBeName/internal/pkg/attractions" "context" ) type PlaceUsecaseImpl struct { - repo places.PlaceRepo + repo attractions.PlaceRepo } -func NewPlaceUsecase(repo places.PlaceRepo) *PlaceUsecaseImpl { +func NewPlaceUsecase(repo attractions.PlaceRepo) *PlaceUsecaseImpl { return &PlaceUsecaseImpl{repo: repo} } @@ -39,10 +39,9 @@ func (i *PlaceUsecaseImpl) GetPlace(ctx context.Context, id uint) (models.GetPla return i.repo.GetPlace(ctx, id) } -func (i *PlaceUsecaseImpl) SearchPlaces(ctx context.Context, name string, limit, offset int) ([]models.GetPlace, error) { - return i.repo.SearchPlaces(ctx, name, limit, offset) +func (i *PlaceUsecaseImpl) SearchPlaces(ctx context.Context, name string, category, city, limit, offset int) ([]models.GetPlace, error) { + return i.repo.SearchPlaces(ctx, name, category, city, limit, offset) } - func (i *PlaceUsecaseImpl) GetPlacesByCategory(ctx context.Context, category string, limit, offset int) ([]models.GetPlace, error) { return i.repo.GetPlacesByCategory(ctx, category, limit, offset) } diff --git a/internal/pkg/places/usecase/usecase_test.go b/internal/pkg/attractions/usecase/usecase_test.go similarity index 99% rename from internal/pkg/places/usecase/usecase_test.go rename to internal/pkg/attractions/usecase/usecase_test.go index 10569d9..05f3253 100644 --- a/internal/pkg/places/usecase/usecase_test.go +++ b/internal/pkg/attractions/usecase/usecase_test.go @@ -2,7 +2,7 @@ package usecase import ( "2024_2_ThereWillBeName/internal/models" - mock_places "2024_2_ThereWillBeName/internal/pkg/places/mocks" + mock_places "2024_2_ThereWillBeName/internal/pkg/attractions/mocks" "context" "errors" "github.com/golang/mock/gomock" diff --git a/internal/pkg/categories/delivery/grpc/gen/categories.pb.go b/internal/pkg/categories/delivery/grpc/gen/categories.pb.go new file mode 100644 index 0000000..1787e19 --- /dev/null +++ b/internal/pkg/categories/delivery/grpc/gen/categories.pb.go @@ -0,0 +1,253 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.2 +// protoc v5.29.0--rc2 +// source: proto/categories.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Category struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *Category) Reset() { + *x = Category{} + mi := &file_proto_categories_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Category) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Category) ProtoMessage() {} + +func (x *Category) ProtoReflect() protoreflect.Message { + mi := &file_proto_categories_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Category.ProtoReflect.Descriptor instead. +func (*Category) Descriptor() ([]byte, []int) { + return file_proto_categories_proto_rawDescGZIP(), []int{0} +} + +func (x *Category) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Category) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GetCategoriesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Limit int32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` +} + +func (x *GetCategoriesRequest) Reset() { + *x = GetCategoriesRequest{} + mi := &file_proto_categories_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetCategoriesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCategoriesRequest) ProtoMessage() {} + +func (x *GetCategoriesRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_categories_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCategoriesRequest.ProtoReflect.Descriptor instead. +func (*GetCategoriesRequest) Descriptor() ([]byte, []int) { + return file_proto_categories_proto_rawDescGZIP(), []int{1} +} + +func (x *GetCategoriesRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *GetCategoriesRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +type GetCategoriesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Categories []*Category `protobuf:"bytes,1,rep,name=categories,proto3" json:"categories,omitempty"` +} + +func (x *GetCategoriesResponse) Reset() { + *x = GetCategoriesResponse{} + mi := &file_proto_categories_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetCategoriesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetCategoriesResponse) ProtoMessage() {} + +func (x *GetCategoriesResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_categories_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetCategoriesResponse.ProtoReflect.Descriptor instead. +func (*GetCategoriesResponse) Descriptor() ([]byte, []int) { + return file_proto_categories_proto_rawDescGZIP(), []int{2} +} + +func (x *GetCategoriesResponse) GetCategories() []*Category { + if x != nil { + return x.Categories + } + return nil +} + +var File_proto_categories_proto protoreflect.FileDescriptor + +var file_proto_categories_proto_rawDesc = []byte{ + 0x0a, 0x16, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, + 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, + 0x72, 0x69, 0x65, 0x73, 0x22, 0x2e, 0x0a, 0x08, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x44, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, + 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x4d, 0x0a, 0x15, 0x47, 0x65, + 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a, 0x0a, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, + 0x72, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x52, 0x0a, 0x63, + 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x32, 0x64, 0x0a, 0x0a, 0x43, 0x61, 0x74, + 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x43, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x74, + 0x65, 0x67, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x07, 0x5a, 0x05, 0x2e, 0x3b, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_categories_proto_rawDescOnce sync.Once + file_proto_categories_proto_rawDescData = file_proto_categories_proto_rawDesc +) + +func file_proto_categories_proto_rawDescGZIP() []byte { + file_proto_categories_proto_rawDescOnce.Do(func() { + file_proto_categories_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_categories_proto_rawDescData) + }) + return file_proto_categories_proto_rawDescData +} + +var file_proto_categories_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_proto_categories_proto_goTypes = []any{ + (*Category)(nil), // 0: categories.Category + (*GetCategoriesRequest)(nil), // 1: categories.GetCategoriesRequest + (*GetCategoriesResponse)(nil), // 2: categories.GetCategoriesResponse +} +var file_proto_categories_proto_depIdxs = []int32{ + 0, // 0: categories.GetCategoriesResponse.categories:type_name -> categories.Category + 1, // 1: categories.Categories.GetCategories:input_type -> categories.GetCategoriesRequest + 2, // 2: categories.Categories.GetCategories:output_type -> categories.GetCategoriesResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_proto_categories_proto_init() } +func file_proto_categories_proto_init() { + if File_proto_categories_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_categories_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_categories_proto_goTypes, + DependencyIndexes: file_proto_categories_proto_depIdxs, + MessageInfos: file_proto_categories_proto_msgTypes, + }.Build() + File_proto_categories_proto = out.File + file_proto_categories_proto_rawDesc = nil + file_proto_categories_proto_goTypes = nil + file_proto_categories_proto_depIdxs = nil +} diff --git a/internal/pkg/categories/delivery/grpc/gen/categories_grpc.pb.go b/internal/pkg/categories/delivery/grpc/gen/categories_grpc.pb.go new file mode 100644 index 0000000..4f239c7 --- /dev/null +++ b/internal/pkg/categories/delivery/grpc/gen/categories_grpc.pb.go @@ -0,0 +1,121 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.0--rc2 +// source: proto/categories.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Categories_GetCategories_FullMethodName = "/categories.Categories/GetCategories" +) + +// CategoriesClient is the client API for Categories service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type CategoriesClient interface { + GetCategories(ctx context.Context, in *GetCategoriesRequest, opts ...grpc.CallOption) (*GetCategoriesResponse, error) +} + +type categoriesClient struct { + cc grpc.ClientConnInterface +} + +func NewCategoriesClient(cc grpc.ClientConnInterface) CategoriesClient { + return &categoriesClient{cc} +} + +func (c *categoriesClient) GetCategories(ctx context.Context, in *GetCategoriesRequest, opts ...grpc.CallOption) (*GetCategoriesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetCategoriesResponse) + err := c.cc.Invoke(ctx, Categories_GetCategories_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CategoriesServer is the server API for Categories service. +// All implementations must embed UnimplementedCategoriesServer +// for forward compatibility. +type CategoriesServer interface { + GetCategories(context.Context, *GetCategoriesRequest) (*GetCategoriesResponse, error) + mustEmbedUnimplementedCategoriesServer() +} + +// UnimplementedCategoriesServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedCategoriesServer struct{} + +func (UnimplementedCategoriesServer) GetCategories(context.Context, *GetCategoriesRequest) (*GetCategoriesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCategories not implemented") +} +func (UnimplementedCategoriesServer) mustEmbedUnimplementedCategoriesServer() {} +func (UnimplementedCategoriesServer) testEmbeddedByValue() {} + +// UnsafeCategoriesServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CategoriesServer will +// result in compilation errors. +type UnsafeCategoriesServer interface { + mustEmbedUnimplementedCategoriesServer() +} + +func RegisterCategoriesServer(s grpc.ServiceRegistrar, srv CategoriesServer) { + // If the following call pancis, it indicates UnimplementedCategoriesServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Categories_ServiceDesc, srv) +} + +func _Categories_GetCategories_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetCategoriesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CategoriesServer).GetCategories(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Categories_GetCategories_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CategoriesServer).GetCategories(ctx, req.(*GetCategoriesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Categories_ServiceDesc is the grpc.ServiceDesc for Categories service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Categories_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "categories.Categories", + HandlerType: (*CategoriesServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetCategories", + Handler: _Categories_GetCategories_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/categories.proto", +} diff --git a/internal/pkg/categories/delivery/grpc/grpc_server.go b/internal/pkg/categories/delivery/grpc/grpc_server.go new file mode 100644 index 0000000..f2c8720 --- /dev/null +++ b/internal/pkg/categories/delivery/grpc/grpc_server.go @@ -0,0 +1,34 @@ +package grpc + +import ( + "2024_2_ThereWillBeName/internal/pkg/categories" + "2024_2_ThereWillBeName/internal/pkg/categories/delivery/grpc/gen" + "context" +) + +//go:generate protoc -I . proto/categories.proto --go_out=./gen --go-grpc_out=./gen + +type GrpcCategoriesHandler struct { + gen.UnimplementedCategoriesServer + uc categories.CategoriesUsecase +} + +func NewGrpcCategoriesHandler(uc categories.CategoriesUsecase) *GrpcCategoriesHandler { + return &GrpcCategoriesHandler{uc: uc} +} + +func (s *GrpcCategoriesHandler) GetCategories(ctx context.Context, req *gen.GetCategoriesRequest) (*gen.GetCategoriesResponse, error) { + categories, err := s.uc.GetCategories(ctx, int(req.Limit), int(req.Offset)) + if err != nil { + return nil, err + } + + categoriesResponse := make([]*gen.Category, len(categories)) + for i, category := range categories { + categoriesResponse[i] = &gen.Category{ + Id: uint32(category.ID), + Name: category.Name, + } + } + return &gen.GetCategoriesResponse{Categories: categoriesResponse}, nil +} diff --git a/internal/pkg/categories/delivery/grpc/proto/categories.proto b/internal/pkg/categories/delivery/grpc/proto/categories.proto new file mode 100644 index 0000000..ca19872 --- /dev/null +++ b/internal/pkg/categories/delivery/grpc/proto/categories.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package categories; +option go_package = ".;gen"; + +service Categories { + rpc GetCategories(GetCategoriesRequest) returns (GetCategoriesResponse) {} +} + +message Category { + uint32 id = 1; + string name = 2; +} + +message GetCategoriesRequest { + int32 limit = 1; + int32 offset = 2; +} + +message GetCategoriesResponse { + repeated Category categories = 1; +} diff --git a/internal/pkg/categories/delivery/http/handler.go b/internal/pkg/categories/delivery/http/handler.go index 88c9086..499e7c5 100644 --- a/internal/pkg/categories/delivery/http/handler.go +++ b/internal/pkg/categories/delivery/http/handler.go @@ -1,7 +1,7 @@ package http import ( - "2024_2_ThereWillBeName/internal/pkg/categories" + "2024_2_ThereWillBeName/internal/pkg/categories/delivery/grpc/gen" httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" log "2024_2_ThereWillBeName/internal/pkg/logger" "log/slog" @@ -10,12 +10,12 @@ import ( ) type CategoriesHandler struct { - uc categories.CategoriesUsecase + client gen.CategoriesClient logger *slog.Logger } -func NewCategoriesHandler(uc categories.CategoriesUsecase, logger *slog.Logger) *CategoriesHandler { - return &CategoriesHandler{uc: uc, logger: logger} +func NewCategoriesHandler(client gen.CategoriesClient, logger *slog.Logger) *CategoriesHandler { + return &CategoriesHandler{client, logger} } func (h *CategoriesHandler) GetCategoriesHandler(w http.ResponseWriter, r *http.Request) { @@ -35,7 +35,7 @@ func (h *CategoriesHandler) GetCategoriesHandler(w http.ResponseWriter, r *http. return } - categories, err := h.uc.GetCategories(logCtx, limit, offset) + categories, err := h.client.GetCategories(logCtx, &gen.GetCategoriesRequest{Limit: int32(limit), Offset: int32(offset)}) if err != nil { httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) h.logger.Error("Error getting categories", @@ -47,5 +47,5 @@ func (h *CategoriesHandler) GetCategoriesHandler(w http.ResponseWriter, r *http. h.logger.DebugContext(logCtx, "Successfully retrieved categories", slog.Any("categories", categories), slog.Int("limit", limit), slog.Int("offset", offset)) - httpresponse.SendJSONResponse(w, categories, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, categories.Categories, http.StatusOK, h.logger) } diff --git a/internal/pkg/cities/delivery/grpc/gen/cities.pb.go b/internal/pkg/cities/delivery/grpc/gen/cities.pb.go new file mode 100644 index 0000000..5b96de3 --- /dev/null +++ b/internal/pkg/cities/delivery/grpc/gen/cities.pb.go @@ -0,0 +1,350 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.2 +// protoc v5.29.0--rc2 +// source: proto/cities.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type City struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *City) Reset() { + *x = City{} + mi := &file_proto_cities_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *City) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*City) ProtoMessage() {} + +func (x *City) ProtoReflect() protoreflect.Message { + mi := &file_proto_cities_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use City.ProtoReflect.Descriptor instead. +func (*City) Descriptor() ([]byte, []int) { + return file_proto_cities_proto_rawDescGZIP(), []int{0} +} + +func (x *City) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *City) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type SearchCitiesByNameRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` +} + +func (x *SearchCitiesByNameRequest) Reset() { + *x = SearchCitiesByNameRequest{} + mi := &file_proto_cities_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchCitiesByNameRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchCitiesByNameRequest) ProtoMessage() {} + +func (x *SearchCitiesByNameRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_cities_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchCitiesByNameRequest.ProtoReflect.Descriptor instead. +func (*SearchCitiesByNameRequest) Descriptor() ([]byte, []int) { + return file_proto_cities_proto_rawDescGZIP(), []int{1} +} + +func (x *SearchCitiesByNameRequest) GetQuery() string { + if x != nil { + return x.Query + } + return "" +} + +type SearchCitiesByNameResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Cities []*City `protobuf:"bytes,1,rep,name=cities,proto3" json:"cities,omitempty"` +} + +func (x *SearchCitiesByNameResponse) Reset() { + *x = SearchCitiesByNameResponse{} + mi := &file_proto_cities_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchCitiesByNameResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchCitiesByNameResponse) ProtoMessage() {} + +func (x *SearchCitiesByNameResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_cities_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchCitiesByNameResponse.ProtoReflect.Descriptor instead. +func (*SearchCitiesByNameResponse) Descriptor() ([]byte, []int) { + return file_proto_cities_proto_rawDescGZIP(), []int{2} +} + +func (x *SearchCitiesByNameResponse) GetCities() []*City { + if x != nil { + return x.Cities + } + return nil +} + +type SearchCityByIDRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *SearchCityByIDRequest) Reset() { + *x = SearchCityByIDRequest{} + mi := &file_proto_cities_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchCityByIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchCityByIDRequest) ProtoMessage() {} + +func (x *SearchCityByIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_cities_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchCityByIDRequest.ProtoReflect.Descriptor instead. +func (*SearchCityByIDRequest) Descriptor() ([]byte, []int) { + return file_proto_cities_proto_rawDescGZIP(), []int{3} +} + +func (x *SearchCityByIDRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +type SearchCityByIDResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + City *City `protobuf:"bytes,1,opt,name=city,proto3" json:"city,omitempty"` +} + +func (x *SearchCityByIDResponse) Reset() { + *x = SearchCityByIDResponse{} + mi := &file_proto_cities_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchCityByIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchCityByIDResponse) ProtoMessage() {} + +func (x *SearchCityByIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_cities_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchCityByIDResponse.ProtoReflect.Descriptor instead. +func (*SearchCityByIDResponse) Descriptor() ([]byte, []int) { + return file_proto_cities_proto_rawDescGZIP(), []int{4} +} + +func (x *SearchCityByIDResponse) GetCity() *City { + if x != nil { + return x.City + } + return nil +} + +var File_proto_cities_proto protoreflect.FileDescriptor + +var file_proto_cities_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, 0x2a, 0x0a, 0x04, + 0x43, 0x69, 0x74, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x31, 0x0a, 0x19, 0x53, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x43, 0x69, 0x74, 0x69, 0x65, 0x73, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x42, 0x0a, 0x1a, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, 0x69, 0x74, 0x69, 0x65, 0x73, 0x42, 0x79, 0x4e, 0x61, 0x6d, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x06, 0x63, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x63, 0x69, 0x74, 0x69, + 0x65, 0x73, 0x2e, 0x43, 0x69, 0x74, 0x79, 0x52, 0x06, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x22, + 0x27, 0x0a, 0x15, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, 0x69, 0x74, 0x79, 0x42, 0x79, 0x49, + 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3a, 0x0a, 0x16, 0x53, 0x65, 0x61, 0x72, + 0x63, 0x68, 0x43, 0x69, 0x74, 0x79, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x20, 0x0a, 0x04, 0x63, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0c, 0x2e, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x43, 0x69, 0x74, 0x79, 0x52, 0x04, + 0x63, 0x69, 0x74, 0x79, 0x32, 0xba, 0x01, 0x0a, 0x06, 0x43, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, + 0x5d, 0x0a, 0x12, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, 0x69, 0x74, 0x69, 0x65, 0x73, 0x42, + 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x2e, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, 0x69, 0x74, 0x69, 0x65, 0x73, 0x42, 0x79, 0x4e, 0x61, 0x6d, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x63, 0x69, 0x74, 0x69, 0x65, + 0x73, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, 0x69, 0x74, 0x69, 0x65, 0x73, 0x42, 0x79, + 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, + 0x0a, 0x0e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, 0x69, 0x74, 0x79, 0x42, 0x79, 0x49, 0x44, + 0x12, 0x1d, 0x2e, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x43, 0x69, 0x74, 0x79, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x63, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, + 0x69, 0x74, 0x79, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x3b, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_proto_cities_proto_rawDescOnce sync.Once + file_proto_cities_proto_rawDescData = file_proto_cities_proto_rawDesc +) + +func file_proto_cities_proto_rawDescGZIP() []byte { + file_proto_cities_proto_rawDescOnce.Do(func() { + file_proto_cities_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_cities_proto_rawDescData) + }) + return file_proto_cities_proto_rawDescData +} + +var file_proto_cities_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_proto_cities_proto_goTypes = []any{ + (*City)(nil), // 0: cities.City + (*SearchCitiesByNameRequest)(nil), // 1: cities.SearchCitiesByNameRequest + (*SearchCitiesByNameResponse)(nil), // 2: cities.SearchCitiesByNameResponse + (*SearchCityByIDRequest)(nil), // 3: cities.SearchCityByIDRequest + (*SearchCityByIDResponse)(nil), // 4: cities.SearchCityByIDResponse +} +var file_proto_cities_proto_depIdxs = []int32{ + 0, // 0: cities.SearchCitiesByNameResponse.cities:type_name -> cities.City + 0, // 1: cities.SearchCityByIDResponse.city:type_name -> cities.City + 1, // 2: cities.Cities.SearchCitiesByName:input_type -> cities.SearchCitiesByNameRequest + 3, // 3: cities.Cities.SearchCityByID:input_type -> cities.SearchCityByIDRequest + 2, // 4: cities.Cities.SearchCitiesByName:output_type -> cities.SearchCitiesByNameResponse + 4, // 5: cities.Cities.SearchCityByID:output_type -> cities.SearchCityByIDResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_proto_cities_proto_init() } +func file_proto_cities_proto_init() { + if File_proto_cities_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_cities_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_cities_proto_goTypes, + DependencyIndexes: file_proto_cities_proto_depIdxs, + MessageInfos: file_proto_cities_proto_msgTypes, + }.Build() + File_proto_cities_proto = out.File + file_proto_cities_proto_rawDesc = nil + file_proto_cities_proto_goTypes = nil + file_proto_cities_proto_depIdxs = nil +} diff --git a/internal/pkg/cities/delivery/grpc/gen/cities_grpc.pb.go b/internal/pkg/cities/delivery/grpc/gen/cities_grpc.pb.go new file mode 100644 index 0000000..a69bd46 --- /dev/null +++ b/internal/pkg/cities/delivery/grpc/gen/cities_grpc.pb.go @@ -0,0 +1,159 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.0--rc2 +// source: proto/cities.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Cities_SearchCitiesByName_FullMethodName = "/cities.Cities/SearchCitiesByName" + Cities_SearchCityByID_FullMethodName = "/cities.Cities/SearchCityByID" +) + +// CitiesClient is the client API for Cities service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type CitiesClient interface { + SearchCitiesByName(ctx context.Context, in *SearchCitiesByNameRequest, opts ...grpc.CallOption) (*SearchCitiesByNameResponse, error) + SearchCityByID(ctx context.Context, in *SearchCityByIDRequest, opts ...grpc.CallOption) (*SearchCityByIDResponse, error) +} + +type citiesClient struct { + cc grpc.ClientConnInterface +} + +func NewCitiesClient(cc grpc.ClientConnInterface) CitiesClient { + return &citiesClient{cc} +} + +func (c *citiesClient) SearchCitiesByName(ctx context.Context, in *SearchCitiesByNameRequest, opts ...grpc.CallOption) (*SearchCitiesByNameResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SearchCitiesByNameResponse) + err := c.cc.Invoke(ctx, Cities_SearchCitiesByName_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *citiesClient) SearchCityByID(ctx context.Context, in *SearchCityByIDRequest, opts ...grpc.CallOption) (*SearchCityByIDResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SearchCityByIDResponse) + err := c.cc.Invoke(ctx, Cities_SearchCityByID_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CitiesServer is the server API for Cities service. +// All implementations must embed UnimplementedCitiesServer +// for forward compatibility. +type CitiesServer interface { + SearchCitiesByName(context.Context, *SearchCitiesByNameRequest) (*SearchCitiesByNameResponse, error) + SearchCityByID(context.Context, *SearchCityByIDRequest) (*SearchCityByIDResponse, error) + mustEmbedUnimplementedCitiesServer() +} + +// UnimplementedCitiesServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedCitiesServer struct{} + +func (UnimplementedCitiesServer) SearchCitiesByName(context.Context, *SearchCitiesByNameRequest) (*SearchCitiesByNameResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SearchCitiesByName not implemented") +} +func (UnimplementedCitiesServer) SearchCityByID(context.Context, *SearchCityByIDRequest) (*SearchCityByIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SearchCityByID not implemented") +} +func (UnimplementedCitiesServer) mustEmbedUnimplementedCitiesServer() {} +func (UnimplementedCitiesServer) testEmbeddedByValue() {} + +// UnsafeCitiesServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to CitiesServer will +// result in compilation errors. +type UnsafeCitiesServer interface { + mustEmbedUnimplementedCitiesServer() +} + +func RegisterCitiesServer(s grpc.ServiceRegistrar, srv CitiesServer) { + // If the following call pancis, it indicates UnimplementedCitiesServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Cities_ServiceDesc, srv) +} + +func _Cities_SearchCitiesByName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SearchCitiesByNameRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CitiesServer).SearchCitiesByName(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cities_SearchCitiesByName_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CitiesServer).SearchCitiesByName(ctx, req.(*SearchCitiesByNameRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Cities_SearchCityByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SearchCityByIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CitiesServer).SearchCityByID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Cities_SearchCityByID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CitiesServer).SearchCityByID(ctx, req.(*SearchCityByIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Cities_ServiceDesc is the grpc.ServiceDesc for Cities service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Cities_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "cities.Cities", + HandlerType: (*CitiesServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SearchCitiesByName", + Handler: _Cities_SearchCitiesByName_Handler, + }, + { + MethodName: "SearchCityByID", + Handler: _Cities_SearchCityByID_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/cities.proto", +} diff --git a/internal/pkg/cities/delivery/grpc/grpc_server.go b/internal/pkg/cities/delivery/grpc/grpc_server.go new file mode 100644 index 0000000..05f5ff9 --- /dev/null +++ b/internal/pkg/cities/delivery/grpc/grpc_server.go @@ -0,0 +1,43 @@ +package grpc + +import ( + "2024_2_ThereWillBeName/internal/pkg/cities" + "2024_2_ThereWillBeName/internal/pkg/cities/delivery/grpc/gen" + "context" +) + +//go:generate protoc -I . proto/cities.proto --go_out=./gen --go-grpc_out=./gen + +type GrpcCitiesHandler struct { + gen.UnimplementedCitiesServer + uc cities.CitiesUsecase +} + +func NewGrpcCitiesHandler(uc cities.CitiesUsecase) *GrpcCitiesHandler { + return &GrpcCitiesHandler{uc: uc} +} + +func (s *GrpcCitiesHandler) SearchCitiesByName(ctx context.Context, req *gen.SearchCitiesByNameRequest) (*gen.SearchCitiesByNameResponse, error) { + cities, err := s.uc.SearchCitiesByName(ctx, req.Query) + if err != nil { + return nil, err + } + + citiesResponse := make([]*gen.City, len(cities)) + for i, city := range cities { + citiesResponse[i] = &gen.City{ + Id: uint32(city.ID), + Name: city.Name, + } + } + return &gen.SearchCitiesByNameResponse{Cities: citiesResponse}, nil +} + +func (s *GrpcCitiesHandler) SearchCityByID(ctx context.Context, req *gen.SearchCityByIDRequest) (*gen.SearchCityByIDResponse, error) { + city, err := s.uc.SearchCityByID(ctx, uint(req.Id)) + if err != nil { + return nil, err + } + + return &gen.SearchCityByIDResponse{City: &gen.City{Id: uint32(city.ID), Name: city.Name}}, nil +} diff --git a/internal/pkg/cities/delivery/grpc/proto/cities.proto b/internal/pkg/cities/delivery/grpc/proto/cities.proto new file mode 100644 index 0000000..1bfa64c --- /dev/null +++ b/internal/pkg/cities/delivery/grpc/proto/cities.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package cities; +option go_package = ".;gen"; + +service Cities { + rpc SearchCitiesByName(SearchCitiesByNameRequest) returns (SearchCitiesByNameResponse) {} + rpc SearchCityByID(SearchCityByIDRequest) returns (SearchCityByIDResponse) {} +} + +message City { + uint32 id = 1; + string name = 2; +} + +message SearchCitiesByNameRequest { + string query = 1; +} + +message SearchCitiesByNameResponse { + repeated City cities = 1; +} + +message SearchCityByIDRequest { + uint32 id = 1; +} + +message SearchCityByIDResponse { + City city = 1; +} diff --git a/internal/pkg/cities/delivery/http/handler.go b/internal/pkg/cities/delivery/http/handler.go index 57b323c..1ba0c3f 100644 --- a/internal/pkg/cities/delivery/http/handler.go +++ b/internal/pkg/cities/delivery/http/handler.go @@ -2,7 +2,7 @@ package http import ( "2024_2_ThereWillBeName/internal/models" - "2024_2_ThereWillBeName/internal/pkg/cities" + "2024_2_ThereWillBeName/internal/pkg/cities/delivery/grpc/gen" httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" log "2024_2_ThereWillBeName/internal/pkg/logger" "context" @@ -16,12 +16,12 @@ import ( ) type CitiesHandler struct { - uc cities.CitiesUsecase + client gen.CitiesClient logger *slog.Logger } -func NewCitiesHandler(uc cities.CitiesUsecase, logger *slog.Logger) *CitiesHandler { - return &CitiesHandler{uc, logger} +func NewCitiesHandler(client gen.CitiesClient, logger *slog.Logger) *CitiesHandler { + return &CitiesHandler{client, logger} } func ErrorCheck(err error, action string, logger *slog.Logger, ctx context.Context) (httpresponse.ErrorResponse, int) { @@ -58,7 +58,7 @@ func (h *CitiesHandler) SearchCitiesByNameHandler(w http.ResponseWriter, r *http query := r.URL.Query().Get("q") logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) - h.logger.DebugContext(logCtx, "Handling request for searching places by name", slog.String("name", query)) + h.logger.DebugContext(logCtx, "Handling request for searching attractions by name", slog.String("name", query)) if query == "" { h.logger.Error("Search query parameter is empty") @@ -69,7 +69,7 @@ func (h *CitiesHandler) SearchCitiesByNameHandler(w http.ResponseWriter, r *http return } - cities, err := h.uc.SearchCitiesByName(context.Background(), query) + cities, err := h.client.SearchCitiesByName(context.Background(), &gen.SearchCitiesByNameRequest{Query: query}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.String("name", query)) response, status := ErrorCheck(err, "retrieve", h.logger, logCtx) @@ -78,7 +78,7 @@ func (h *CitiesHandler) SearchCitiesByNameHandler(w http.ResponseWriter, r *http } h.logger.DebugContext(logCtx, "Successfully retrieved cities") - httpresponse.SendJSONResponse(w, cities, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, cities.Cities, http.StatusOK, h.logger) } // SearchCityByIDHandler godoc @@ -120,7 +120,7 @@ func (h *CitiesHandler) SearchCityByIDHandler(w http.ResponseWriter, r *http.Req return } - city, err := h.uc.SearchCityByID(context.Background(), uint(cityID)) + city, err := h.client.SearchCityByID(context.Background(), &gen.SearchCityByIDRequest{Id: uint32(cityID)}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.Uint64("ID", (cityID))) response, status := ErrorCheck(err, "retrieve", h.logger, logCtx) @@ -130,5 +130,5 @@ func (h *CitiesHandler) SearchCityByIDHandler(w http.ResponseWriter, r *http.Req h.logger.DebugContext(logCtx, "Successfully retrieved cities") - httpresponse.SendJSONResponse(w, city, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, city.City, http.StatusOK, h.logger) } diff --git a/internal/pkg/metrics/prometheus.go b/internal/pkg/metrics/prometheus.go new file mode 100644 index 0000000..162a404 --- /dev/null +++ b/internal/pkg/metrics/prometheus.go @@ -0,0 +1,58 @@ +package metrics + +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" +) + +type PrometheusMetrics struct { + hits *prometheus.CounterVec + errors *prometheus.CounterVec + durations *prometheus.HistogramVec +} + +func NewPrometheusMetrics() *PrometheusMetrics { + hits := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "grpc_method_hits_total", + Help: "Total number of gRPC method calls.", + }, + []string{"method"}, + ) + errors := prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "grpc_method_errors_total", + Help: "Total number of gRPC method errors.", + }, + []string{"method"}, + ) + durations := prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "grpc_method_duration_seconds", + Help: "Histogram of gRPC method call durations.", + Buckets: prometheus.DefBuckets, + }, + []string{"method"}, + ) + + prometheus.MustRegister(hits, errors, durations) + + return &PrometheusMetrics{ + hits: hits, + errors: errors, + durations: durations, + } +} + +func (m *PrometheusMetrics) IncreaseHits(method string) { + m.hits.WithLabelValues(method).Inc() +} + +func (m *PrometheusMetrics) IncreaseErr(method string) { + m.errors.WithLabelValues(method).Inc() +} + +func (m *PrometheusMetrics) AddDurationToHistogram(method string, duration time.Duration) { + m.durations.WithLabelValues(method).Observe(duration.Seconds()) +} diff --git a/internal/pkg/middleware/interceptor.go b/internal/pkg/middleware/interceptor.go new file mode 100644 index 0000000..966fc09 --- /dev/null +++ b/internal/pkg/middleware/interceptor.go @@ -0,0 +1,45 @@ +package middleware + +import ( + "context" + "time" + + "google.golang.org/grpc" +) + +type MetricsInterface interface { + IncreaseHits(method string) + IncreaseErr(method string) + AddDurationToHistogram(method string, duration time.Duration) +} + +type GrpcMiddleware struct { + metrics MetricsInterface +} + +func NewGrpcMiddleware(metrics MetricsInterface) *GrpcMiddleware { + return &GrpcMiddleware{ + metrics: metrics, + } +} +func (m *GrpcMiddleware) ServerMetricsInterceptor( + ctx context.Context, + req interface{}, + info *grpc.UnaryServerInfo, + handler grpc.UnaryHandler, +) (interface{}, error) { + start := time.Now() + + resp, err := handler(ctx, req) + + duration := time.Since(start) + + m.metrics.IncreaseHits(info.FullMethod) + + if err != nil { + m.metrics.IncreaseErr(info.FullMethod) + } + m.metrics.AddDurationToHistogram(info.FullMethod, duration) + + return resp, err +} diff --git a/internal/pkg/places/delivery/http/handler.go b/internal/pkg/places/delivery/http/handler.go deleted file mode 100644 index 0b448ff..0000000 --- a/internal/pkg/places/delivery/http/handler.go +++ /dev/null @@ -1,323 +0,0 @@ -package http - -import ( - "2024_2_ThereWillBeName/internal/models" - httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" - log "2024_2_ThereWillBeName/internal/pkg/logger" - "2024_2_ThereWillBeName/internal/pkg/places" - "2024_2_ThereWillBeName/internal/validator" - "encoding/json" - "errors" - "fmt" - "html/template" - "log/slog" - "net/http" - "strconv" - - "github.com/gorilla/mux" -) - -type PlacesHandler struct { - uc places.PlaceUsecase - logger *slog.Logger -} - -func NewPlacesHandler(uc places.PlaceUsecase, logger *slog.Logger) *PlacesHandler { - return &PlacesHandler{uc, logger} -} - -// GetPlaceHandler godoc -// @Summary Get a list of places -// @Description Retrieve a list of places from the database -// @Produce json -// @Success 200 {array} models.GetPlace "List of places" -// @Failure 400 {object} httpresponses.ErrorResponse "Bad request" -// @Failure 500 {object} httpresponses.ErrorResponse "Internal Server Error" -// @Router /places [get] -func (h *PlacesHandler) GetPlacesHandler(w http.ResponseWriter, r *http.Request) { - logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) - h.logger.DebugContext(logCtx, "Handling request for searching places") - - offset, err := strconv.Atoi(r.URL.Query().Get("offset")) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Invalid offset parameter", slog.String("error", err.Error())) - return - } - limit, err := strconv.Atoi(r.URL.Query().Get("limit")) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Invalid limit parameter", slog.String("error", err.Error())) - return - } - places, err := h.uc.GetPlaces(r.Context(), limit, offset) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) - h.logger.Error("Couldn't get list of places", - slog.Int("limit", limit), - slog.Int("offset", offset), - slog.String("error", err.Error())) - return - } - - h.logger.DebugContext(logCtx, "Successfully retrieved places") - - httpresponse.SendJSONResponse(w, places, http.StatusOK, h.logger) -} - -// PostPlaceHandler godoc -// @Summary Create a new place -// @Description Add a new place to the database -// @Accept json -// @Produce json -// @Param place body models.CreatePlace true "Place data" -// @Success 201 {object} httpresponses.ErrorResponse "Place successfully created" -// @Failure 400 {object} httpresponses.ErrorResponse -// @Failure 403 {object} httpresponses.ErrorResponse "Token is missing" -// @Failure 403 {object} httpresponses.ErrorResponse "Invalid token" -// @Failure 422 {object} httpresponses.ErrorResponse -// @Failure 500 {object} httpresponses.ErrorResponse -// @Router /places [post] -func (h *PlacesHandler) PostPlaceHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';") - logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) - h.logger.DebugContext(logCtx, "Handling request for creating a place") - - var place models.CreatePlace - if err := json.NewDecoder(r.Body).Decode(&place); err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Failed to decode place data", - slog.String("error", err.Error()), - slog.String("place_data", fmt.Sprintf("%+v", place))) - return - } - v := validator.New() - if models.ValidateCreatePlace(v, &place); !v.Valid() { - httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger) - return - } - - place.Name = template.HTMLEscapeString(place.Name) - place.ImagePath = template.HTMLEscapeString(place.ImagePath) - place.Description = template.HTMLEscapeString(place.Description) - place.Address = template.HTMLEscapeString(place.Address) - place.PhoneNumber = template.HTMLEscapeString(place.PhoneNumber) - - if err := h.uc.CreatePlace(r.Context(), place); err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) - h.logger.Error("Failed to create place", - slog.String("placeName", place.Name), - slog.String("error", err.Error())) - return - } - - h.logger.DebugContext(logCtx, "Successfully created place") - - httpresponse.SendJSONResponse(w, "Place succesfully created", http.StatusCreated, h.logger) -} - -// PutPlaceHandler godoc -// @Summary Update an existing place -// @Description Update the details of an existing place in the database -// @Accept json -// @Produce json -// @Param place body models.UpdatePlace true "Updated place data" -// @Success 200 {object} httpresponses.ErrorResponse "Place successfully updated" -// @Failure 400 {object} httpresponses.ErrorResponse -// @Failure 403 {object} httpresponses.ErrorResponse "Token is missing" -// @Failure 403 {object} httpresponses.ErrorResponse "Invalid token" -// @Failure 422 {object} httpresponses.ErrorResponse -// @Failure 500 {object} httpresponses.ErrorResponse -// @Router /places/{id} [put] -func (h *PlacesHandler) PutPlaceHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';") - logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) - h.logger.DebugContext(logCtx, "Handling request for updating a place") - - var place models.UpdatePlace - - if err := json.NewDecoder(r.Body).Decode(&place); err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Failed to decode place data", - slog.String("error", err.Error()), - slog.String("place_data", fmt.Sprintf("%+v", place))) - return - } - - v := validator.New() - if models.ValidateUpdatePlace(v, &place); !v.Valid() { - httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger) - return - } - - place.Name = template.HTMLEscapeString(place.Name) - place.ImagePath = template.HTMLEscapeString(place.ImagePath) - place.Description = template.HTMLEscapeString(place.Description) - place.Address = template.HTMLEscapeString(place.Address) - place.PhoneNumber = template.HTMLEscapeString(place.PhoneNumber) - - if err := h.uc.UpdatePlace(r.Context(), place); err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) - h.logger.Error("Failed to update place", - slog.Int("placeID", place.ID), - slog.String("error", err.Error())) - return - } - - h.logger.DebugContext(logCtx, "Successfully updated place") - - httpresponse.SendJSONResponse(w, "place successfully updated", http.StatusOK, h.logger) -} - -// DeletePlaceHandler godoc -// @Summary Delete an existing place -// @Description Remove a place from the database by its name -// @Produce json -// @Param name body string true "Name of the place to be deleted" -// @Success 200 {object} httpresponses.ErrorResponse "Place successfully deleted" -// @Failure 400 {object} httpresponses.ErrorResponse -// @Failure 403 {object} httpresponses.ErrorResponse "Token is missing" -// @Failure 403 {object} httpresponses.ErrorResponse "Invalid token" -// @Failure 500 {object} httpresponses.ErrorResponse -// @Router /places/{id} [delete] -func (h *PlacesHandler) DeletePlaceHandler(w http.ResponseWriter, r *http.Request) { - - idStr := mux.Vars(r)["id"] - - logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) - h.logger.DebugContext(logCtx, "Handling request for deleting a place by ID", slog.String("placeID", idStr)) - - id, err := strconv.ParseUint(idStr, 10, 64) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Failed to parse place ID", slog.String("placeID", idStr), slog.String("error", err.Error())) - return - } - if err := h.uc.DeletePlace(r.Context(), uint(id)); err != nil { - if errors.Is(err, models.ErrNotFound) { - httpresponse.SendJSONResponse(w, nil, http.StatusNotFound, h.logger) - h.logger.Warn("Place not found", slog.String("placeID", idStr), slog.String("error", err.Error())) - return - } - httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) - h.logger.Error("Failed to delete a place", slog.String("placeID", idStr), slog.String("error", err.Error())) - return - } - httpresponse.SendJSONResponse(w, "place successfully deleted", http.StatusOK, h.logger) - - h.logger.DebugContext(logCtx, "Successfully updated place") -} - -// GetPlaceHandler godoc -// @Summary Retrieve an existing place -// @Description Get details of a place from the database by its id -// @Produce json -// @Param id body int true "ID of the place to retrieve" -// @Success 200 {object} models.GetPlace "Details of the requested place" -// @Failure 400 {object} httpresponses.ErrorResponse -// @Failure 500 {object} httpresponses.ErrorResponse -// @Router /places/{id} [get] -func (h *PlacesHandler) GetPlaceHandler(w http.ResponseWriter, r *http.Request) { - idStr := mux.Vars(r)["id"] - - logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) - h.logger.DebugContext(logCtx, "Handling request for getting a place by ID", slog.String("placeID", idStr)) - - id, err := strconv.ParseUint(idStr, 10, 64) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Failed to parse place ID", slog.String("placeID", idStr), slog.String("error", err.Error())) - return - } - place, err := h.uc.GetPlace(r.Context(), uint(id)) - if err != nil { - if errors.Is(err, models.ErrNotFound) { - response := httpresponse.ErrorResponse{ - Message: "place not found", - } - httpresponse.SendJSONResponse(w, response, http.StatusNotFound, h.logger) - h.logger.Warn("Place not found", slog.String("placeID", idStr), slog.String("error", err.Error())) - return - } - httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) - h.logger.Error("Failed to get a place", slog.String("placeID", idStr), slog.String("error", err.Error())) - return - } - httpresponse.SendJSONResponse(w, place, http.StatusOK, h.logger) - - h.logger.DebugContext(logCtx, "Successfully getting place") -} - -// GetPlacesByNameHandler godoc -// @Summary Retrieve places by search string -// @Description Get a list of places from the database that match the provided search string -// @Produce json -// @Param searchString body string true "Name of the places to retrieve" -// @Success 200 {object} models.GetPlace "List of places matching the provided searchString" -// @Failure 400 {object} httpresponses.ErrorResponse -// @Failure 500 {object} httpresponses.ErrorResponse -// @Router /places/search/{placeName} [get] -func (h *PlacesHandler) SearchPlacesHandler(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';") - placeName := mux.Vars(r)["placeName"] - - logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) - h.logger.DebugContext(logCtx, "Handling request for searching places by place name", slog.String("placeName", placeName)) - - offset, err := strconv.Atoi(r.URL.Query().Get("offset")) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Invalid offset parameter", slog.String("error", err.Error())) - return - } - limit, err := strconv.Atoi(r.URL.Query().Get("limit")) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Invalid limit parameter", slog.String("error", err.Error())) - return - } - - placeName = template.HTMLEscapeString(placeName) - - places, err := h.uc.SearchPlaces(r.Context(), placeName, limit, offset) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) - h.logger.Error("Failed to search places", slog.String("placeName", placeName), slog.String("error", err.Error())) - return - } - httpresponse.SendJSONResponse(w, places, http.StatusOK, h.logger) - - h.logger.DebugContext(logCtx, "Successfully getting places by name") -} - -func (h *PlacesHandler) GetPlacesByCategoryHandler(w http.ResponseWriter, r *http.Request) { - categoryName := mux.Vars(r)["categoryName"] - - logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) - h.logger.DebugContext(logCtx, "Handling request for searching places by category", slog.String("categoryName", categoryName)) - - offset, err := strconv.Atoi(r.URL.Query().Get("offset")) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Invalid offset parameter", slog.String("error", err.Error())) - return - } - limit, err := strconv.Atoi(r.URL.Query().Get("limit")) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) - h.logger.Warn("Invalid limit parameter", slog.String("error", err.Error())) - return - } - - places, err := h.uc.GetPlacesByCategory(r.Context(), categoryName, limit, offset) - if err != nil { - httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) - h.logger.Error("Error getting places by category", - slog.Int("limit", limit), - slog.Int("offset", offset), - slog.String("categoryName", categoryName), - slog.String("error", err.Error())) - return - } - httpresponse.SendJSONResponse(w, places, http.StatusOK, h.logger) -} diff --git a/internal/pkg/places/delivery/http/handler_test.go b/internal/pkg/places/delivery/http/handler_test.go deleted file mode 100644 index 034e473..0000000 --- a/internal/pkg/places/delivery/http/handler_test.go +++ /dev/null @@ -1,671 +0,0 @@ -package http - -import ( - "2024_2_ThereWillBeName/internal/models" - mockplaces "2024_2_ThereWillBeName/internal/pkg/places/mocks" - "bytes" - "context" - "encoding/json" - "log" - - "errors" - "log/slog" - "net/http" - "net/http/httptest" - "net/url" - "os" - "strconv" - - "github.com/gorilla/mux" - - "testing" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" -) - -const errorParse = "ErrorParse" - -func TestGetPlacesHandler(t *testing.T) { - places := []models.GetPlace{ - { - ID: 1, - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - NumberOfReviews: 2500, - Address: "59th St to 110th St, New York, NY 10022", - City: "New York", - PhoneNumber: "+1 212-310-6600", - Categories: []string{"Park", "Recreation", "Nature"}, - }, - { - ID: 2, - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - NumberOfReviews: 2500, - Address: "59th St to 110th St, New York, NY 10022", - City: "New York", - PhoneNumber: "+1 212-310-6600", - Categories: []string{"Park", "Recreation", "Nature"}, - }, - } - - jsonPlaces, _ := json.Marshal(places) - stringPlaces := string(jsonPlaces) - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - h := slog.NewJSONHandler(os.Stdout, nil) - - logger := slog.New(h) - - mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) - handler := NewPlacesHandler(mockUsecase, logger) - - tests := []struct { - name string - offset string - limit string - mockPlaces []models.GetPlace - mockError error - expectedCode int - expectedBody string - }{ - { - name: "Valid request", - offset: "0", - limit: "10", - mockPlaces: places, - mockError: nil, - expectedCode: http.StatusOK, - expectedBody: stringPlaces + "\n", - }, - { - name: "Invalid offset", - offset: "invalid", - limit: "10", - mockPlaces: nil, - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: ``, - }, - { - name: "Invalid limit", - offset: "0", - limit: "invalid", - mockPlaces: nil, - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: ``, - }, - { - name: "Internal server error", - offset: "0", - limit: "10", - mockPlaces: nil, - mockError: errors.New("internal server error"), - expectedCode: http.StatusInternalServerError, - expectedBody: ``, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.name != "Invalid offset" && tt.name != "Invalid limit" { - offset, _ := strconv.Atoi(tt.offset) - limit, _ := strconv.Atoi(tt.limit) - mockUsecase.EXPECT().GetPlaces(gomock.Any(), limit, offset).Return(tt.mockPlaces, tt.mockError) - } - - req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/places", nil) - assert.NoError(t, err) - - query := url.Values{} - query.Add("offset", tt.offset) - query.Add("limit", tt.limit) - req.URL.RawQuery = query.Encode() - - rr := httptest.NewRecorder() - router := mux.NewRouter() - router.HandleFunc("/places", handler.GetPlacesHandler) - router.ServeHTTP(rr, req) - - assert.Equal(t, tt.expectedCode, rr.Code) - assert.Equal(t, tt.expectedBody, rr.Body.String()) - }) - } -} - -func TestPostPlacesHandler(t *testing.T) { - place := models.CreatePlace{ - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - NumberOfReviews: 2500, - Address: "59th St to 110th St, New York, NY 10022", - CityId: 1, - PhoneNumber: "+1 212-310-6600", - CategoriesId: []int{1, 2}, - } - - jsonPlace, _ := json.Marshal(place) - //reader := bytes.NewReader(jsonPlace) - //stringPlace := string(jsonPlace) - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - h := slog.NewJSONHandler(os.Stdout, nil) - - logger := slog.New(h) - - mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) - handler := NewPlacesHandler(mockUsecase, logger) - - tests := []struct { - name string - mockReturn []byte - mockError error - expectedCode int - expectedBody string - }{ - { - name: "Success", - mockReturn: jsonPlace, - mockError: nil, - expectedCode: http.StatusCreated, - expectedBody: "\"Place succesfully created\"\n", - }, - { - name: "ErrorINternal", - mockReturn: jsonPlace, - mockError: errors.New("dcd"), - expectedCode: http.StatusInternalServerError, - expectedBody: "", - }, - { - name: errorParse, - mockReturn: []byte(`{ "name": "Test Place", "imagePath": "/path/to/image", "description": "Test Description", "rating": 4.5, "numberOfReviews": 10, "address": "Test Address", "cityId": 1, "phoneNumber": "1234567890", "categoriesId": [1, 2, 3]`), - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: "", - }, - } - - for _, testcase := range tests { - t.Run(testcase.name, func(t *testing.T) { - if testcase.name != errorParse { - mockUsecase.EXPECT().CreatePlace(context.Background(), gomock.Any()).Return(testcase.mockError) - } - - req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, "/places", bytes.NewBuffer(testcase.mockReturn)) - assert.NoError(t, err) - - rr := httptest.NewRecorder() - handler.PostPlaceHandler(rr, req) - - assert.Equal(t, testcase.expectedCode, rr.Code) - assert.Equal(t, testcase.expectedBody, rr.Body.String()) - }) - } -} - -func TestPutPlacesHandler(t *testing.T) { - place := models.UpdatePlace{ - ID: 1, - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - NumberOfReviews: 2500, - Address: "59th St to 110th St, New York, NY 10022", - CityId: 1, - PhoneNumber: "+1 212-310-6600", - CategoriesId: []int{1, 2}, - } - - jsonPlace, _ := json.Marshal(place) - //reader := bytes.NewReader(jsonPlace) - //stringPlace := string(jsonPlace) - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - h := slog.NewJSONHandler(os.Stdout, nil) - - logger := slog.New(h) - mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) - handler := NewPlacesHandler(mockUsecase, logger) - - tests := []struct { - name string - mockReturn []byte - mockError error - expectedCode int - expectedBody string - }{ - { - name: "Success", - mockReturn: jsonPlace, - mockError: nil, - expectedCode: http.StatusOK, - expectedBody: "\"place successfully updated\"\n", - }, - { - name: "ErrorINternal", - mockReturn: jsonPlace, - mockError: errors.New("dcd"), - expectedCode: http.StatusInternalServerError, - expectedBody: "", - }, - { - name: errorParse, - mockReturn: []byte(`{ "name": "Test Place", "imagePath": "/path/to/image", "description": "Test Description", "rating": 4.5, "numberOfReviews": 10, "address": "Test Address", "cityId": 1, "phoneNumber": "1234567890", "categoriesId": [1, 2, 3]`), - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: "", - }, - } - - for _, testcase := range tests { - t.Run(testcase.name, func(t *testing.T) { - if testcase.name != errorParse { - mockUsecase.EXPECT().UpdatePlace(context.Background(), gomock.Any()).Return(testcase.mockError) - } - - req, err := http.NewRequestWithContext(context.Background(), http.MethodPut, "/places/1", bytes.NewBuffer(testcase.mockReturn)) - assert.NoError(t, err) - - rr := httptest.NewRecorder() - handler.PutPlaceHandler(rr, req) - - assert.Equal(t, testcase.expectedCode, rr.Code) - assert.Equal(t, testcase.expectedBody, rr.Body.String()) - }) - } -} - -func TestDeletePlacesHandler(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - h := slog.NewJSONHandler(os.Stdout, nil) - - logger := slog.New(h) - mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) - handler := NewPlacesHandler(mockUsecase, logger) - - tests := []struct { - name string - id int - mockError error - expectedCode int - expectedBody string - }{ - { - name: "Success", - id: 1, - mockError: nil, - expectedCode: http.StatusOK, - expectedBody: "\"place successfully deleted\"\n", - }, - { - name: "ErrorINternal", - id: 1, - mockError: errors.New("dcd"), - expectedCode: http.StatusInternalServerError, - expectedBody: "", - }, - { - name: errorParse, - id: -1, - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: "", - }, - } - - for _, testcase := range tests { - t.Run(testcase.name, func(t *testing.T) { - if testcase.name != errorParse { - mockUsecase.EXPECT().DeletePlace(gomock.Any(), uint(testcase.id)).Return(testcase.mockError) - } - - req, err := http.NewRequestWithContext(context.Background(), http.MethodDelete, "/places/"+strconv.Itoa(int(testcase.id)), nil) - assert.NoError(t, err) - - rr := httptest.NewRecorder() - router := mux.NewRouter() - router.HandleFunc("/places/{id}", handler.DeletePlaceHandler).Methods(http.MethodDelete) - router.ServeHTTP(rr, req) - - assert.Equal(t, testcase.expectedCode, rr.Code) - assert.Equal(t, testcase.expectedBody, rr.Body.String()) - }) - } -} - -func TestGetPlaceHandler(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - h := slog.NewJSONHandler(os.Stdout, nil) - - logger := slog.New(h) - mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) - handler := NewPlacesHandler(mockUsecase, logger) - - place := models.GetPlace{ - ID: 1, - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - NumberOfReviews: 2500, - Address: "59th St to 110th St, New York, NY 10022", - City: "New York", - PhoneNumber: "+1 212-310-6600", - Categories: []string{"Park", "Recreation", "Nature"}, - } - - jsonPlaces, _ := json.Marshal(place) - stringPlace := string(jsonPlaces) - - tests := []struct { - name string - id int - mockPlace models.GetPlace - mockError error - expectedCode int - expectedBody string - }{ - { - name: "Success", - id: 1, - mockPlace: place, - mockError: nil, - expectedCode: http.StatusOK, - expectedBody: stringPlace + "\n", - }, - { - name: "NotFound", - id: 2, - mockPlace: models.GetPlace{}, - mockError: models.ErrNotFound, - expectedCode: http.StatusNotFound, - expectedBody: `{"message":"place not found"}` + "\n", - }, - { - name: "InternalServerError", - id: 3, - mockPlace: models.GetPlace{}, - mockError: errors.New("internal server error"), - expectedCode: http.StatusInternalServerError, - expectedBody: ``, - }, - { - name: "InvalidID", - id: -1, - mockPlace: models.GetPlace{}, - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: "", - }, - } - - for _, testcase := range tests { - t.Run(testcase.name, func(t *testing.T) { - if testcase.name != "InvalidID" { - mockUsecase.EXPECT().GetPlace(gomock.Any(), uint(testcase.id)).Return(testcase.mockPlace, testcase.mockError) - } - - req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/places/"+strconv.Itoa(int(testcase.id)), nil) - assert.NoError(t, err) - - rr := httptest.NewRecorder() - router := mux.NewRouter() - router.HandleFunc("/places/{id}", handler.GetPlaceHandler).Methods(http.MethodGet) - router.ServeHTTP(rr, req) - - assert.Equal(t, testcase.expectedCode, rr.Code) - assert.Equal(t, testcase.expectedBody, rr.Body.String()) - }) - } -} - -func TestSearchPlaceHandler(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - h := slog.NewJSONHandler(os.Stdout, nil) - - logger := slog.New(h) - mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) - handler := NewPlacesHandler(mockUsecase, logger) - - places := []models.GetPlace{ - { - ID: 1, - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - NumberOfReviews: 2500, - Address: "59th St to 110th St, New York, NY 10022", - City: "New York", - PhoneNumber: "+1 212-310-6600", - Categories: []string{"Park", "Recreation", "Nature"}, - }, - { - ID: 2, - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - NumberOfReviews: 2500, - Address: "59th St to 110th St, New York, NY 10022", - City: "New York", - PhoneNumber: "+1 212-310-6600", - Categories: []string{"Park", "Recreation", "Nature"}, - }, - } - - jsonPlaces, _ := json.Marshal(places) - stringPlace := string(jsonPlaces) - - tests := []struct { - name string - limit string - offset string - search string - mockPlaces []models.GetPlace - mockError error - expectedCode int - expectedBody string - }{ - { - name: "Success", - search: "test", - limit: "10", - offset: "0", - mockPlaces: places, - mockError: nil, - expectedCode: http.StatusOK, - expectedBody: stringPlace + "\n", - }, - { - name: "InternalServerError", - search: "badSearch", - limit: "10", - offset: "0", - mockPlaces: []models.GetPlace{}, - mockError: errors.New("internal server error"), - expectedCode: http.StatusInternalServerError, - expectedBody: ``, - }, - { - name: "InvalidOffset", - search: "test", - offset: "invalid", - limit: "10", - mockPlaces: []models.GetPlace{}, - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: ``, - }, - { - name: "InvalidLimit", - search: "test", - offset: "0", - limit: "invalid", - mockPlaces: []models.GetPlace{}, - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: ``, - }, - } - - for _, testcase := range tests { - t.Run(testcase.name, func(t *testing.T) { - if testcase.name != "InvalidOffset" && testcase.name != "InvalidLimit" && testcase.name != "MissingPlaceName" { - mockUsecase.EXPECT().SearchPlaces(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testcase.mockPlaces, testcase.mockError) - } - urlStr := "/places/search/" + testcase.search - log.Println(urlStr) - req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, urlStr, nil) - assert.NoError(t, err) - - query := url.Values{} - query.Add("offset", testcase.offset) - query.Add("limit", testcase.limit) - req.URL.RawQuery = query.Encode() - - rr := httptest.NewRecorder() - router := mux.NewRouter() - router.HandleFunc("/places/search/{search}", handler.SearchPlacesHandler).Methods(http.MethodGet) - router.ServeHTTP(rr, req) - - assert.Equal(t, testcase.expectedCode, rr.Code) - assert.Equal(t, testcase.expectedBody, rr.Body.String()) - }) - } -} - -func TestGetPlacesByCategoryHandler(t *testing.T) { - places := []models.GetPlace{ - { - ID: 1, - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - Address: "59th St to 110th St, New York, NY 10022", - City: "New York", - PhoneNumber: "+1 212-310-6600", - Categories: []string{"Park", "Recreation", "Nature"}, - }, - { - ID: 2, - Name: "Central Park", - ImagePath: "/images/central_park.jpg", - Description: "A large public park in New York City, offering a variety of recreational activities.", - Rating: 5, - Address: "59th St to 110th St, New York, NY 10022", - City: "New York", - PhoneNumber: "+1 212-310-6600", - Categories: []string{"Park", "Recreation", "Nature"}, - }, - } - - jsonPlaces, _ := json.Marshal(places) - stringPlaces := string(jsonPlaces) - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - h := slog.NewJSONHandler(os.Stdout, nil) - - logger := slog.New(h) - - mockUsecase := mockplaces.NewMockPlaceUsecase(ctrl) - handler := NewPlacesHandler(mockUsecase, logger) - - tests := []struct { - name string - offset string - limit string - mockPlaces []models.GetPlace - mockError error - expectedCode int - expectedBody string - }{ - { - name: "Valid request", - offset: "0", - limit: "10", - mockPlaces: places, - mockError: nil, - expectedCode: http.StatusOK, - expectedBody: stringPlaces + "\n", - }, - { - name: "Invalid offset", - offset: "invalid", - limit: "10", - mockPlaces: nil, - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: ``, - }, - { - name: "Invalid limit", - offset: "0", - limit: "invalid", - mockPlaces: nil, - mockError: nil, - expectedCode: http.StatusBadRequest, - expectedBody: ``, - }, - { - name: "Internal server error", - offset: "0", - limit: "10", - mockPlaces: nil, - mockError: errors.New("internal server error"), - expectedCode: http.StatusInternalServerError, - expectedBody: ``, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.name != "Invalid offset" && tt.name != "Invalid limit" { - offset, _ := strconv.Atoi(tt.offset) - limit, _ := strconv.Atoi(tt.limit) - mockUsecase.EXPECT().GetPlacesByCategory(gomock.Any(), gomock.Any(), limit, offset).Return(tt.mockPlaces, tt.mockError) - } - - req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/places/category/{categoryName}", nil) - assert.NoError(t, err) - - query := url.Values{} - query.Add("offset", tt.offset) - query.Add("limit", tt.limit) - req.URL.RawQuery = query.Encode() - - rr := httptest.NewRecorder() - router := mux.NewRouter() - router.HandleFunc("/places/category/{categoryName}", handler.GetPlacesByCategoryHandler) - router.ServeHTTP(rr, req) - - assert.Equal(t, tt.expectedCode, rr.Code) - assert.Equal(t, tt.expectedBody, rr.Body.String()) - }) - } -} diff --git a/internal/pkg/reviews/delivery/grpc/gen/reviews.pb.go b/internal/pkg/reviews/delivery/grpc/gen/reviews.pb.go new file mode 100644 index 0000000..dff409f --- /dev/null +++ b/internal/pkg/reviews/delivery/grpc/gen/reviews.pb.go @@ -0,0 +1,1013 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.2 +// protoc v5.29.0--rc2 +// source: proto/reviews.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Review struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + UserId uint32 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + PlaceId uint32 `protobuf:"varint,3,opt,name=place_id,json=placeId,proto3" json:"place_id,omitempty"` + Rating int32 `protobuf:"varint,4,opt,name=rating,proto3" json:"rating,omitempty"` + ReviewText string `protobuf:"bytes,5,opt,name=review_text,json=reviewText,proto3" json:"review_text,omitempty"` +} + +func (x *Review) Reset() { + *x = Review{} + mi := &file_proto_reviews_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Review) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Review) ProtoMessage() {} + +func (x *Review) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Review.ProtoReflect.Descriptor instead. +func (*Review) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{0} +} + +func (x *Review) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Review) GetUserId() uint32 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *Review) GetPlaceId() uint32 { + if x != nil { + return x.PlaceId + } + return 0 +} + +func (x *Review) GetRating() int32 { + if x != nil { + return x.Rating + } + return 0 +} + +func (x *Review) GetReviewText() string { + if x != nil { + return x.ReviewText + } + return "" +} + +type GetReview struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + UserLogin string `protobuf:"bytes,2,opt,name=user_login,json=userLogin,proto3" json:"user_login,omitempty"` + AvatarPath string `protobuf:"bytes,3,opt,name=avatar_path,json=avatarPath,proto3" json:"avatar_path,omitempty"` + Rating int32 `protobuf:"varint,4,opt,name=rating,proto3" json:"rating,omitempty"` + ReviewText string `protobuf:"bytes,5,opt,name=review_text,json=reviewText,proto3" json:"review_text,omitempty"` +} + +func (x *GetReview) Reset() { + *x = GetReview{} + mi := &file_proto_reviews_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetReview) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReview) ProtoMessage() {} + +func (x *GetReview) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReview.ProtoReflect.Descriptor instead. +func (*GetReview) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{1} +} + +func (x *GetReview) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *GetReview) GetUserLogin() string { + if x != nil { + return x.UserLogin + } + return "" +} + +func (x *GetReview) GetAvatarPath() string { + if x != nil { + return x.AvatarPath + } + return "" +} + +func (x *GetReview) GetRating() int32 { + if x != nil { + return x.Rating + } + return 0 +} + +func (x *GetReview) GetReviewText() string { + if x != nil { + return x.ReviewText + } + return "" +} + +type GetReviewByUserID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + PlaceName string `protobuf:"bytes,2,opt,name=place_name,json=placeName,proto3" json:"place_name,omitempty"` + Rating int32 `protobuf:"varint,3,opt,name=rating,proto3" json:"rating,omitempty"` + ReviewText string `protobuf:"bytes,4,opt,name=review_text,json=reviewText,proto3" json:"review_text,omitempty"` +} + +func (x *GetReviewByUserID) Reset() { + *x = GetReviewByUserID{} + mi := &file_proto_reviews_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetReviewByUserID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReviewByUserID) ProtoMessage() {} + +func (x *GetReviewByUserID) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReviewByUserID.ProtoReflect.Descriptor instead. +func (*GetReviewByUserID) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{2} +} + +func (x *GetReviewByUserID) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *GetReviewByUserID) GetPlaceName() string { + if x != nil { + return x.PlaceName + } + return "" +} + +func (x *GetReviewByUserID) GetRating() int32 { + if x != nil { + return x.Rating + } + return 0 +} + +func (x *GetReviewByUserID) GetReviewText() string { + if x != nil { + return x.ReviewText + } + return "" +} + +type CreateReviewRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Review *Review `protobuf:"bytes,1,opt,name=review,proto3" json:"review,omitempty"` +} + +func (x *CreateReviewRequest) Reset() { + *x = CreateReviewRequest{} + mi := &file_proto_reviews_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateReviewRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateReviewRequest) ProtoMessage() {} + +func (x *CreateReviewRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateReviewRequest.ProtoReflect.Descriptor instead. +func (*CreateReviewRequest) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateReviewRequest) GetReview() *Review { + if x != nil { + return x.Review + } + return nil +} + +type CreateReviewResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Review *GetReview `protobuf:"bytes,1,opt,name=review,proto3" json:"review,omitempty"` +} + +func (x *CreateReviewResponse) Reset() { + *x = CreateReviewResponse{} + mi := &file_proto_reviews_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateReviewResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateReviewResponse) ProtoMessage() {} + +func (x *CreateReviewResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateReviewResponse.ProtoReflect.Descriptor instead. +func (*CreateReviewResponse) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateReviewResponse) GetReview() *GetReview { + if x != nil { + return x.Review + } + return nil +} + +type UpdateReviewRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Review *Review `protobuf:"bytes,1,opt,name=review,proto3" json:"review,omitempty"` +} + +func (x *UpdateReviewRequest) Reset() { + *x = UpdateReviewRequest{} + mi := &file_proto_reviews_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateReviewRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateReviewRequest) ProtoMessage() {} + +func (x *UpdateReviewRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateReviewRequest.ProtoReflect.Descriptor instead. +func (*UpdateReviewRequest) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{5} +} + +func (x *UpdateReviewRequest) GetReview() *Review { + if x != nil { + return x.Review + } + return nil +} + +type UpdateReviewResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` +} + +func (x *UpdateReviewResponse) Reset() { + *x = UpdateReviewResponse{} + mi := &file_proto_reviews_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateReviewResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateReviewResponse) ProtoMessage() {} + +func (x *UpdateReviewResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateReviewResponse.ProtoReflect.Descriptor instead. +func (*UpdateReviewResponse) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{6} +} + +func (x *UpdateReviewResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type DeleteReviewRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteReviewRequest) Reset() { + *x = DeleteReviewRequest{} + mi := &file_proto_reviews_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteReviewRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteReviewRequest) ProtoMessage() {} + +func (x *DeleteReviewRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteReviewRequest.ProtoReflect.Descriptor instead. +func (*DeleteReviewRequest) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{7} +} + +func (x *DeleteReviewRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +type DeleteReviewResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` +} + +func (x *DeleteReviewResponse) Reset() { + *x = DeleteReviewResponse{} + mi := &file_proto_reviews_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteReviewResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteReviewResponse) ProtoMessage() {} + +func (x *DeleteReviewResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteReviewResponse.ProtoReflect.Descriptor instead. +func (*DeleteReviewResponse) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteReviewResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type GetReviewsByPlaceIDRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PlaceId uint32 `protobuf:"varint,1,opt,name=place_id,json=placeId,proto3" json:"place_id,omitempty"` + Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` +} + +func (x *GetReviewsByPlaceIDRequest) Reset() { + *x = GetReviewsByPlaceIDRequest{} + mi := &file_proto_reviews_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetReviewsByPlaceIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReviewsByPlaceIDRequest) ProtoMessage() {} + +func (x *GetReviewsByPlaceIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReviewsByPlaceIDRequest.ProtoReflect.Descriptor instead. +func (*GetReviewsByPlaceIDRequest) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{9} +} + +func (x *GetReviewsByPlaceIDRequest) GetPlaceId() uint32 { + if x != nil { + return x.PlaceId + } + return 0 +} + +func (x *GetReviewsByPlaceIDRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *GetReviewsByPlaceIDRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +type GetReviewsByPlaceIDResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Reviews []*GetReview `protobuf:"bytes,1,rep,name=reviews,proto3" json:"reviews,omitempty"` +} + +func (x *GetReviewsByPlaceIDResponse) Reset() { + *x = GetReviewsByPlaceIDResponse{} + mi := &file_proto_reviews_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetReviewsByPlaceIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReviewsByPlaceIDResponse) ProtoMessage() {} + +func (x *GetReviewsByPlaceIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReviewsByPlaceIDResponse.ProtoReflect.Descriptor instead. +func (*GetReviewsByPlaceIDResponse) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{10} +} + +func (x *GetReviewsByPlaceIDResponse) GetReviews() []*GetReview { + if x != nil { + return x.Reviews + } + return nil +} + +type GetReviewsByUserIDRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId uint32 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` +} + +func (x *GetReviewsByUserIDRequest) Reset() { + *x = GetReviewsByUserIDRequest{} + mi := &file_proto_reviews_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetReviewsByUserIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReviewsByUserIDRequest) ProtoMessage() {} + +func (x *GetReviewsByUserIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReviewsByUserIDRequest.ProtoReflect.Descriptor instead. +func (*GetReviewsByUserIDRequest) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{11} +} + +func (x *GetReviewsByUserIDRequest) GetUserId() uint32 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *GetReviewsByUserIDRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *GetReviewsByUserIDRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +type GetReviewsByUserIDResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Reviews []*GetReviewByUserID `protobuf:"bytes,1,rep,name=reviews,proto3" json:"reviews,omitempty"` +} + +func (x *GetReviewsByUserIDResponse) Reset() { + *x = GetReviewsByUserIDResponse{} + mi := &file_proto_reviews_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetReviewsByUserIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReviewsByUserIDResponse) ProtoMessage() {} + +func (x *GetReviewsByUserIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReviewsByUserIDResponse.ProtoReflect.Descriptor instead. +func (*GetReviewsByUserIDResponse) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{12} +} + +func (x *GetReviewsByUserIDResponse) GetReviews() []*GetReviewByUserID { + if x != nil { + return x.Reviews + } + return nil +} + +type GetReviewRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetReviewRequest) Reset() { + *x = GetReviewRequest{} + mi := &file_proto_reviews_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetReviewRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReviewRequest) ProtoMessage() {} + +func (x *GetReviewRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReviewRequest.ProtoReflect.Descriptor instead. +func (*GetReviewRequest) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{13} +} + +func (x *GetReviewRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +type GetReviewResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Review *GetReview `protobuf:"bytes,1,opt,name=review,proto3" json:"review,omitempty"` +} + +func (x *GetReviewResponse) Reset() { + *x = GetReviewResponse{} + mi := &file_proto_reviews_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetReviewResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetReviewResponse) ProtoMessage() {} + +func (x *GetReviewResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_reviews_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetReviewResponse.ProtoReflect.Descriptor instead. +func (*GetReviewResponse) Descriptor() ([]byte, []int) { + return file_proto_reviews_proto_rawDescGZIP(), []int{14} +} + +func (x *GetReviewResponse) GetReview() *GetReview { + if x != nil { + return x.Review + } + return nil +} + +var File_proto_reviews_proto protoreflect.FileDescriptor + +var file_proto_reviews_proto_rawDesc = []byte{ + 0x0a, 0x13, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x22, 0x85, + 0x01, 0x0a, 0x06, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, + 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, + 0x74, 0x65, 0x78, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x76, 0x69, + 0x65, 0x77, 0x54, 0x65, 0x78, 0x74, 0x22, 0x94, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, + 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x6f, + 0x67, 0x69, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x54, 0x65, 0x78, 0x74, 0x22, 0x7b, 0x0a, + 0x11, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x44, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x06, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x54, 0x65, 0x78, 0x74, 0x22, 0x3e, 0x0a, 0x13, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x27, 0x0a, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x52, 0x65, 0x76, 0x69, + 0x65, 0x77, 0x52, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, 0x42, 0x0a, 0x14, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, 0x3e, + 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, 0x30, + 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x22, 0x25, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x30, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x65, 0x0a, 0x1a, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x49, 0x44, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, + 0x22, 0x4b, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x42, 0x79, + 0x50, 0x6c, 0x61, 0x63, 0x65, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2c, 0x0a, 0x07, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x52, 0x07, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x22, 0x62, 0x0a, + 0x19, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, + 0x72, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, + 0x74, 0x22, 0x52, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x42, + 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x34, 0x0a, 0x07, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x07, 0x72, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x73, 0x22, 0x22, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x3f, 0x0a, 0x11, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, + 0x0a, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, + 0x65, 0x77, 0x52, 0x06, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x32, 0x81, 0x04, 0x0a, 0x07, 0x52, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x12, 0x4d, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x1c, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x1c, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x12, 0x1c, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x73, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x49, 0x44, 0x12, 0x23, 0x2e, 0x72, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x42, + 0x79, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, + 0x69, 0x65, 0x77, 0x73, 0x42, 0x79, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x49, 0x44, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x22, 0x2e, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, + 0x77, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x23, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x19, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, + 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x07, + 0x5a, 0x05, 0x2e, 0x3b, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_proto_reviews_proto_rawDescOnce sync.Once + file_proto_reviews_proto_rawDescData = file_proto_reviews_proto_rawDesc +) + +func file_proto_reviews_proto_rawDescGZIP() []byte { + file_proto_reviews_proto_rawDescOnce.Do(func() { + file_proto_reviews_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_reviews_proto_rawDescData) + }) + return file_proto_reviews_proto_rawDescData +} + +var file_proto_reviews_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_proto_reviews_proto_goTypes = []any{ + (*Review)(nil), // 0: reviews.Review + (*GetReview)(nil), // 1: reviews.GetReview + (*GetReviewByUserID)(nil), // 2: reviews.GetReviewByUserID + (*CreateReviewRequest)(nil), // 3: reviews.CreateReviewRequest + (*CreateReviewResponse)(nil), // 4: reviews.CreateReviewResponse + (*UpdateReviewRequest)(nil), // 5: reviews.UpdateReviewRequest + (*UpdateReviewResponse)(nil), // 6: reviews.UpdateReviewResponse + (*DeleteReviewRequest)(nil), // 7: reviews.DeleteReviewRequest + (*DeleteReviewResponse)(nil), // 8: reviews.DeleteReviewResponse + (*GetReviewsByPlaceIDRequest)(nil), // 9: reviews.GetReviewsByPlaceIDRequest + (*GetReviewsByPlaceIDResponse)(nil), // 10: reviews.GetReviewsByPlaceIDResponse + (*GetReviewsByUserIDRequest)(nil), // 11: reviews.GetReviewsByUserIDRequest + (*GetReviewsByUserIDResponse)(nil), // 12: reviews.GetReviewsByUserIDResponse + (*GetReviewRequest)(nil), // 13: reviews.GetReviewRequest + (*GetReviewResponse)(nil), // 14: reviews.GetReviewResponse +} +var file_proto_reviews_proto_depIdxs = []int32{ + 0, // 0: reviews.CreateReviewRequest.review:type_name -> reviews.Review + 1, // 1: reviews.CreateReviewResponse.review:type_name -> reviews.GetReview + 0, // 2: reviews.UpdateReviewRequest.review:type_name -> reviews.Review + 1, // 3: reviews.GetReviewsByPlaceIDResponse.reviews:type_name -> reviews.GetReview + 2, // 4: reviews.GetReviewsByUserIDResponse.reviews:type_name -> reviews.GetReviewByUserID + 1, // 5: reviews.GetReviewResponse.review:type_name -> reviews.GetReview + 3, // 6: reviews.Reviews.CreateReview:input_type -> reviews.CreateReviewRequest + 5, // 7: reviews.Reviews.UpdateReview:input_type -> reviews.UpdateReviewRequest + 7, // 8: reviews.Reviews.DeleteReview:input_type -> reviews.DeleteReviewRequest + 9, // 9: reviews.Reviews.GetReviewsByPlaceID:input_type -> reviews.GetReviewsByPlaceIDRequest + 11, // 10: reviews.Reviews.GetReviewsByUserID:input_type -> reviews.GetReviewsByUserIDRequest + 13, // 11: reviews.Reviews.GetReview:input_type -> reviews.GetReviewRequest + 4, // 12: reviews.Reviews.CreateReview:output_type -> reviews.CreateReviewResponse + 6, // 13: reviews.Reviews.UpdateReview:output_type -> reviews.UpdateReviewResponse + 8, // 14: reviews.Reviews.DeleteReview:output_type -> reviews.DeleteReviewResponse + 10, // 15: reviews.Reviews.GetReviewsByPlaceID:output_type -> reviews.GetReviewsByPlaceIDResponse + 12, // 16: reviews.Reviews.GetReviewsByUserID:output_type -> reviews.GetReviewsByUserIDResponse + 14, // 17: reviews.Reviews.GetReview:output_type -> reviews.GetReviewResponse + 12, // [12:18] is the sub-list for method output_type + 6, // [6:12] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_proto_reviews_proto_init() } +func file_proto_reviews_proto_init() { + if File_proto_reviews_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_reviews_proto_rawDesc, + NumEnums: 0, + NumMessages: 15, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_reviews_proto_goTypes, + DependencyIndexes: file_proto_reviews_proto_depIdxs, + MessageInfos: file_proto_reviews_proto_msgTypes, + }.Build() + File_proto_reviews_proto = out.File + file_proto_reviews_proto_rawDesc = nil + file_proto_reviews_proto_goTypes = nil + file_proto_reviews_proto_depIdxs = nil +} diff --git a/internal/pkg/reviews/delivery/grpc/gen/reviews_grpc.pb.go b/internal/pkg/reviews/delivery/grpc/gen/reviews_grpc.pb.go new file mode 100644 index 0000000..c8890b5 --- /dev/null +++ b/internal/pkg/reviews/delivery/grpc/gen/reviews_grpc.pb.go @@ -0,0 +1,311 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.0--rc2 +// source: proto/reviews.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Reviews_CreateReview_FullMethodName = "/reviews.Reviews/CreateReview" + Reviews_UpdateReview_FullMethodName = "/reviews.Reviews/UpdateReview" + Reviews_DeleteReview_FullMethodName = "/reviews.Reviews/DeleteReview" + Reviews_GetReviewsByPlaceID_FullMethodName = "/reviews.Reviews/GetReviewsByPlaceID" + Reviews_GetReviewsByUserID_FullMethodName = "/reviews.Reviews/GetReviewsByUserID" + Reviews_GetReview_FullMethodName = "/reviews.Reviews/GetReview" +) + +// ReviewsClient is the client API for Reviews service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ReviewsClient interface { + CreateReview(ctx context.Context, in *CreateReviewRequest, opts ...grpc.CallOption) (*CreateReviewResponse, error) + UpdateReview(ctx context.Context, in *UpdateReviewRequest, opts ...grpc.CallOption) (*UpdateReviewResponse, error) + DeleteReview(ctx context.Context, in *DeleteReviewRequest, opts ...grpc.CallOption) (*DeleteReviewResponse, error) + GetReviewsByPlaceID(ctx context.Context, in *GetReviewsByPlaceIDRequest, opts ...grpc.CallOption) (*GetReviewsByPlaceIDResponse, error) + GetReviewsByUserID(ctx context.Context, in *GetReviewsByUserIDRequest, opts ...grpc.CallOption) (*GetReviewsByUserIDResponse, error) + GetReview(ctx context.Context, in *GetReviewRequest, opts ...grpc.CallOption) (*GetReviewResponse, error) +} + +type reviewsClient struct { + cc grpc.ClientConnInterface +} + +func NewReviewsClient(cc grpc.ClientConnInterface) ReviewsClient { + return &reviewsClient{cc} +} + +func (c *reviewsClient) CreateReview(ctx context.Context, in *CreateReviewRequest, opts ...grpc.CallOption) (*CreateReviewResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateReviewResponse) + err := c.cc.Invoke(ctx, Reviews_CreateReview_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reviewsClient) UpdateReview(ctx context.Context, in *UpdateReviewRequest, opts ...grpc.CallOption) (*UpdateReviewResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateReviewResponse) + err := c.cc.Invoke(ctx, Reviews_UpdateReview_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reviewsClient) DeleteReview(ctx context.Context, in *DeleteReviewRequest, opts ...grpc.CallOption) (*DeleteReviewResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeleteReviewResponse) + err := c.cc.Invoke(ctx, Reviews_DeleteReview_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reviewsClient) GetReviewsByPlaceID(ctx context.Context, in *GetReviewsByPlaceIDRequest, opts ...grpc.CallOption) (*GetReviewsByPlaceIDResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetReviewsByPlaceIDResponse) + err := c.cc.Invoke(ctx, Reviews_GetReviewsByPlaceID_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reviewsClient) GetReviewsByUserID(ctx context.Context, in *GetReviewsByUserIDRequest, opts ...grpc.CallOption) (*GetReviewsByUserIDResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetReviewsByUserIDResponse) + err := c.cc.Invoke(ctx, Reviews_GetReviewsByUserID_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *reviewsClient) GetReview(ctx context.Context, in *GetReviewRequest, opts ...grpc.CallOption) (*GetReviewResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetReviewResponse) + err := c.cc.Invoke(ctx, Reviews_GetReview_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ReviewsServer is the server API for Reviews service. +// All implementations must embed UnimplementedReviewsServer +// for forward compatibility. +type ReviewsServer interface { + CreateReview(context.Context, *CreateReviewRequest) (*CreateReviewResponse, error) + UpdateReview(context.Context, *UpdateReviewRequest) (*UpdateReviewResponse, error) + DeleteReview(context.Context, *DeleteReviewRequest) (*DeleteReviewResponse, error) + GetReviewsByPlaceID(context.Context, *GetReviewsByPlaceIDRequest) (*GetReviewsByPlaceIDResponse, error) + GetReviewsByUserID(context.Context, *GetReviewsByUserIDRequest) (*GetReviewsByUserIDResponse, error) + GetReview(context.Context, *GetReviewRequest) (*GetReviewResponse, error) + mustEmbedUnimplementedReviewsServer() +} + +// UnimplementedReviewsServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedReviewsServer struct{} + +func (UnimplementedReviewsServer) CreateReview(context.Context, *CreateReviewRequest) (*CreateReviewResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateReview not implemented") +} +func (UnimplementedReviewsServer) UpdateReview(context.Context, *UpdateReviewRequest) (*UpdateReviewResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateReview not implemented") +} +func (UnimplementedReviewsServer) DeleteReview(context.Context, *DeleteReviewRequest) (*DeleteReviewResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteReview not implemented") +} +func (UnimplementedReviewsServer) GetReviewsByPlaceID(context.Context, *GetReviewsByPlaceIDRequest) (*GetReviewsByPlaceIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetReviewsByPlaceID not implemented") +} +func (UnimplementedReviewsServer) GetReviewsByUserID(context.Context, *GetReviewsByUserIDRequest) (*GetReviewsByUserIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetReviewsByUserID not implemented") +} +func (UnimplementedReviewsServer) GetReview(context.Context, *GetReviewRequest) (*GetReviewResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetReview not implemented") +} +func (UnimplementedReviewsServer) mustEmbedUnimplementedReviewsServer() {} +func (UnimplementedReviewsServer) testEmbeddedByValue() {} + +// UnsafeReviewsServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ReviewsServer will +// result in compilation errors. +type UnsafeReviewsServer interface { + mustEmbedUnimplementedReviewsServer() +} + +func RegisterReviewsServer(s grpc.ServiceRegistrar, srv ReviewsServer) { + // If the following call pancis, it indicates UnimplementedReviewsServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Reviews_ServiceDesc, srv) +} + +func _Reviews_CreateReview_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateReviewRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReviewsServer).CreateReview(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Reviews_CreateReview_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReviewsServer).CreateReview(ctx, req.(*CreateReviewRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Reviews_UpdateReview_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateReviewRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReviewsServer).UpdateReview(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Reviews_UpdateReview_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReviewsServer).UpdateReview(ctx, req.(*UpdateReviewRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Reviews_DeleteReview_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteReviewRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReviewsServer).DeleteReview(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Reviews_DeleteReview_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReviewsServer).DeleteReview(ctx, req.(*DeleteReviewRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Reviews_GetReviewsByPlaceID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetReviewsByPlaceIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReviewsServer).GetReviewsByPlaceID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Reviews_GetReviewsByPlaceID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReviewsServer).GetReviewsByPlaceID(ctx, req.(*GetReviewsByPlaceIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Reviews_GetReviewsByUserID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetReviewsByUserIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReviewsServer).GetReviewsByUserID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Reviews_GetReviewsByUserID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReviewsServer).GetReviewsByUserID(ctx, req.(*GetReviewsByUserIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Reviews_GetReview_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetReviewRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReviewsServer).GetReview(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Reviews_GetReview_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReviewsServer).GetReview(ctx, req.(*GetReviewRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Reviews_ServiceDesc is the grpc.ServiceDesc for Reviews service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Reviews_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "reviews.Reviews", + HandlerType: (*ReviewsServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateReview", + Handler: _Reviews_CreateReview_Handler, + }, + { + MethodName: "UpdateReview", + Handler: _Reviews_UpdateReview_Handler, + }, + { + MethodName: "DeleteReview", + Handler: _Reviews_DeleteReview_Handler, + }, + { + MethodName: "GetReviewsByPlaceID", + Handler: _Reviews_GetReviewsByPlaceID_Handler, + }, + { + MethodName: "GetReviewsByUserID", + Handler: _Reviews_GetReviewsByUserID_Handler, + }, + { + MethodName: "GetReview", + Handler: _Reviews_GetReview_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/reviews.proto", +} diff --git a/internal/pkg/reviews/delivery/grpc/grpc_server.go b/internal/pkg/reviews/delivery/grpc/grpc_server.go new file mode 100644 index 0000000..ff05f5f --- /dev/null +++ b/internal/pkg/reviews/delivery/grpc/grpc_server.go @@ -0,0 +1,119 @@ +package grpc + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/reviews" + "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/grpc/gen" + "context" +) + +//go:generate protoc -I . proto/reviews.proto --go_out=./gen --go-grpc_out=./gen + +type GrpcReviewsHandler struct { + gen.UnimplementedReviewsServer + uc reviews.ReviewsUsecase +} + +func NewGrpcReviewsHandler(uc reviews.ReviewsUsecase) *GrpcReviewsHandler { + return &GrpcReviewsHandler{uc: uc} +} + +func (s *GrpcReviewsHandler) CreateReview(ctx context.Context, req *gen.CreateReviewRequest) (*gen.CreateReviewResponse, error) { + review := models.Review{ + ID: uint(req.Review.Id), + UserID: uint(req.Review.UserId), + PlaceID: uint(req.Review.PlaceId), + Rating: int(req.Review.Rating), + ReviewText: req.Review.ReviewText, + } + + res, err := s.uc.CreateReview(ctx, review) + if err != nil { + return nil, err + } + reviewResponse := &gen.GetReview{ + Id: uint32(res.ID), + UserLogin: res.UserLogin, + AvatarPath: res.AvatarPath, + Rating: int32(res.Rating), + ReviewText: res.ReviewText, + } + return &gen.CreateReviewResponse{Review: reviewResponse}, nil +} + +func (s *GrpcReviewsHandler) UpdateReview(ctx context.Context, req *gen.UpdateReviewRequest) (*gen.UpdateReviewResponse, error) { + review := models.Review{ + ID: uint(req.Review.Id), + UserID: uint(req.Review.UserId), + PlaceID: uint(req.Review.PlaceId), + Rating: int(req.Review.Rating), + ReviewText: req.Review.ReviewText, + } + + err := s.uc.UpdateReview(ctx, review) + if err != nil { + return &gen.UpdateReviewResponse{Success: false}, err + } + return &gen.UpdateReviewResponse{Success: true}, nil +} + +func (s *GrpcReviewsHandler) DeleteReview(ctx context.Context, req *gen.DeleteReviewRequest) (*gen.DeleteReviewResponse, error) { + err := s.uc.DeleteReview(ctx, uint(req.Id)) + if err != nil { + return &gen.DeleteReviewResponse{Success: false}, err + } + return &gen.DeleteReviewResponse{Success: true}, nil +} + +func (s *GrpcReviewsHandler) GetReviewsByPlaceID(ctx context.Context, req *gen.GetReviewsByPlaceIDRequest) (*gen.GetReviewsByPlaceIDResponse, error) { + reviews, err := s.uc.GetReviewsByPlaceID(ctx, uint(req.PlaceId), int(req.Limit), int(req.Offset)) + if err != nil { + return nil, err + } + + reviewsResponse := make([]*gen.GetReview, len(reviews)) + for i, review := range reviews { + reviewsResponse[i] = &gen.GetReview{ + Id: uint32(review.ID), + UserLogin: review.UserLogin, + AvatarPath: review.AvatarPath, + Rating: int32(review.Rating), + ReviewText: review.ReviewText, + } + } + return &gen.GetReviewsByPlaceIDResponse{Reviews: reviewsResponse}, nil +} + +func (s *GrpcReviewsHandler) GetReviewsByUserID(ctx context.Context, req *gen.GetReviewsByUserIDRequest) (*gen.GetReviewsByUserIDResponse, error) { + reviews, err := s.uc.GetReviewsByUserID(ctx, uint(req.UserId), int(req.Limit), int(req.Offset)) + if err != nil { + return nil, err + } + + reviewsResponse := make([]*gen.GetReviewByUserID, len(reviews)) + for i, review := range reviews { + reviewsResponse[i] = &gen.GetReviewByUserID{ + Id: uint32(review.ID), + PlaceName: review.PlaceName, + Rating: int32(review.Rating), + ReviewText: review.ReviewText, + } + } + return &gen.GetReviewsByUserIDResponse{Reviews: reviewsResponse}, nil +} + +func (s *GrpcReviewsHandler) GetReview(ctx context.Context, req *gen.GetReviewRequest) (*gen.GetReviewResponse, error) { + review, err := s.uc.GetReview(ctx, uint(req.Id)) + if err != nil { + return nil, err + } + + reviewResponse := &gen.GetReview{ + Id: uint32(review.ID), + UserLogin: review.UserLogin, + AvatarPath: review.AvatarPath, + Rating: int32(review.Rating), + ReviewText: review.ReviewText, + } + return &gen.GetReviewResponse{Review: reviewResponse}, nil +} diff --git a/internal/pkg/reviews/delivery/grpc/proto/reviews.proto b/internal/pkg/reviews/delivery/grpc/proto/reviews.proto new file mode 100644 index 0000000..7286039 --- /dev/null +++ b/internal/pkg/reviews/delivery/grpc/proto/reviews.proto @@ -0,0 +1,88 @@ +syntax = "proto3"; + +package reviews; +option go_package = ".;gen"; + +service Reviews { + rpc CreateReview(CreateReviewRequest) returns (CreateReviewResponse) {} + rpc UpdateReview(UpdateReviewRequest) returns (UpdateReviewResponse) {} + rpc DeleteReview(DeleteReviewRequest) returns (DeleteReviewResponse) {} + rpc GetReviewsByPlaceID(GetReviewsByPlaceIDRequest) returns (GetReviewsByPlaceIDResponse) {} + rpc GetReviewsByUserID(GetReviewsByUserIDRequest) returns (GetReviewsByUserIDResponse) {} + rpc GetReview(GetReviewRequest) returns (GetReviewResponse) {} +} + +message Review { + uint32 id = 1; + uint32 user_id = 2; + uint32 place_id = 3; + int32 rating = 4; + string review_text = 5; +} + +message GetReview { + uint32 id = 1; + string user_login = 2; + string avatar_path = 3; + int32 rating = 4; + string review_text = 5; +} + +message GetReviewByUserID { + uint32 id = 1; + string place_name = 2; + int32 rating = 3; + string review_text = 4; +} + +message CreateReviewRequest { + Review review = 1; +} + +message CreateReviewResponse { + GetReview review = 1; +} + +message UpdateReviewRequest { + Review review = 1; +} + +message UpdateReviewResponse { + bool success = 1; +} + +message DeleteReviewRequest { + uint32 id = 1; +} + +message DeleteReviewResponse { + bool success = 1; +} + +message GetReviewsByPlaceIDRequest { + uint32 place_id = 1; + int32 limit = 2; + int32 offset = 3; +} + +message GetReviewsByPlaceIDResponse { + repeated GetReview reviews = 1; +} + +message GetReviewsByUserIDRequest { + uint32 user_id = 1; + int32 limit = 2; + int32 offset = 3; +} + +message GetReviewsByUserIDResponse { + repeated GetReviewByUserID reviews = 1; +} + +message GetReviewRequest { + uint32 id = 1; +} + +message GetReviewResponse { + GetReview review = 1; +} diff --git a/internal/pkg/reviews/delivery/http/handler.go b/internal/pkg/reviews/delivery/http/handler.go index 2cf536d..0289e52 100644 --- a/internal/pkg/reviews/delivery/http/handler.go +++ b/internal/pkg/reviews/delivery/http/handler.go @@ -5,7 +5,7 @@ import ( httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" log "2024_2_ThereWillBeName/internal/pkg/logger" "2024_2_ThereWillBeName/internal/pkg/middleware" - "2024_2_ThereWillBeName/internal/pkg/reviews" + "2024_2_ThereWillBeName/internal/pkg/reviews/delivery/grpc/gen" "2024_2_ThereWillBeName/internal/validator" "context" "html/template" @@ -21,12 +21,12 @@ import ( ) type ReviewHandler struct { - uc reviews.ReviewsUsecase + client gen.ReviewsClient logger *slog.Logger } -func NewReviewHandler(uc reviews.ReviewsUsecase, logger *slog.Logger) *ReviewHandler { - return &ReviewHandler{uc, logger} +func NewReviewHandler(client gen.ReviewsClient, logger *slog.Logger) *ReviewHandler { + return &ReviewHandler{client, logger} } func ErrorCheck(err error, action string, logger *slog.Logger, ctx context.Context) (httpresponse.ErrorResponse, int) { @@ -108,9 +108,17 @@ func (h *ReviewHandler) CreateReviewHandler(w http.ResponseWriter, r *http.Reque return } - review.UserID = uint(userID) + review.UserID = userID - createdReview, err := h.uc.CreateReview(context.Background(), review) + reviewRequest := &gen.Review{ + Id: uint32(review.ID), + UserId: uint32(review.UserID), + PlaceId: uint32(review.PlaceID), + Rating: int32(review.Rating), + ReviewText: review.ReviewText, + } + + createdReview, err := h.client.CreateReview(context.Background(), &gen.CreateReviewRequest{Review: reviewRequest}) if err != nil { response, status := ErrorCheck(err, "create", h.logger, context.Background()) httpresponse.SendJSONResponse(w, response, status, h.logger) @@ -119,7 +127,7 @@ func (h *ReviewHandler) CreateReviewHandler(w http.ResponseWriter, r *http.Reque h.logger.DebugContext(logCtx, "Successfully created a review") - httpresponse.SendJSONResponse(w, createdReview, http.StatusCreated, h.logger) + httpresponse.SendJSONResponse(w, createdReview.Review, http.StatusCreated, h.logger) } // UpdateReviewHandler godoc @@ -154,7 +162,7 @@ func (h *ReviewHandler) UpdateReviewHandler(w http.ResponseWriter, r *http.Reque var review models.Review vars := mux.Vars(r) - reviewID, err := strconv.Atoi(vars["id"]) + reviewID, err := strconv.Atoi(vars["reviewID"]) if err != nil || reviewID < 0 { response := httpresponse.ErrorResponse{ Message: "Invalid review ID", @@ -183,7 +191,15 @@ func (h *ReviewHandler) UpdateReviewHandler(w http.ResponseWriter, r *http.Reque review.ReviewText = template.HTMLEscapeString(review.ReviewText) review.ID = uint(reviewID) - err = h.uc.UpdateReview(context.Background(), review) + + reviewRequest := &gen.Review{ + ReviewText: review.ReviewText, + UserId: uint32(review.UserID), + PlaceId: uint32(review.PlaceID), + Rating: int32(review.Rating), + Id: uint32(review.ID), + } + res, err := h.client.UpdateReview(r.Context(), &gen.UpdateReviewRequest{Review: reviewRequest}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.Int("reviewID", reviewID)) response, status := ErrorCheck(err, "update", h.logger, logCtx) @@ -192,7 +208,7 @@ func (h *ReviewHandler) UpdateReviewHandler(w http.ResponseWriter, r *http.Reque } h.logger.DebugContext(logCtx, "Successfully updated a review") - w.WriteHeader(http.StatusOK) + httpresponse.SendJSONResponse(w, res.Success, http.StatusOK, h.logger) } // DeleteReviewHandler godoc @@ -209,7 +225,7 @@ func (h *ReviewHandler) UpdateReviewHandler(w http.ResponseWriter, r *http.Reque // @Router /reviews/{id} [delete] func (h *ReviewHandler) DeleteReviewHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) - idStr := vars["id"] + idStr := vars["reviewID"] _, ok := r.Context().Value(middleware.IdKey).(uint) if !ok { @@ -236,7 +252,7 @@ func (h *ReviewHandler) DeleteReviewHandler(w http.ResponseWriter, r *http.Reque return } - err = h.uc.DeleteReview(context.Background(), uint(id)) + _, err = h.client.DeleteReview(r.Context(), &gen.DeleteReviewRequest{Id: uint32(id)}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.String("reviewID", idStr)) response, status := ErrorCheck(err, "delete", h.logger, logCtx) @@ -245,7 +261,11 @@ func (h *ReviewHandler) DeleteReviewHandler(w http.ResponseWriter, r *http.Reque } h.logger.DebugContext(logCtx, "Successfully deleted a review") - w.WriteHeader(http.StatusNoContent) + response := map[string]string{ + "message": "Review deleted successfully", + } + + httpresponse.SendJSONResponse(w, response, http.StatusOK, h.logger) } // GetReviewsByPlaceIDHandler godoc @@ -257,7 +277,7 @@ func (h *ReviewHandler) DeleteReviewHandler(w http.ResponseWriter, r *http.Reque // @Failure 400 {object} httpresponses.ErrorResponse "Invalid place ID" // @Failure 404 {object} httpresponses.ErrorResponse "No reviews found for the place" // @Failure 500 {object} httpresponses.ErrorResponse "Failed to retrieve reviews" -// @Router /places/{placeID}/reviews [get] +// @Router /attractions/{placeID}/reviews [get] func (h *ReviewHandler) GetReviewsByPlaceIDHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) placeIDStr := vars["placeID"] @@ -288,7 +308,7 @@ func (h *ReviewHandler) GetReviewsByPlaceIDHandler(w http.ResponseWriter, r *htt } limit := 10 offset := limit * (page - 1) - reviews, err := h.uc.GetReviewsByPlaceID(context.Background(), uint(placeID), limit, offset) + reviews, err := h.client.GetReviewsByPlaceID(r.Context(), &gen.GetReviewsByPlaceIDRequest{PlaceId: uint32(placeID), Limit: int32(limit), Offset: int32(offset)}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.String("placeID", placeIDStr)) response, status := ErrorCheck(err, "retrieve", h.logger, logCtx) @@ -297,7 +317,7 @@ func (h *ReviewHandler) GetReviewsByPlaceIDHandler(w http.ResponseWriter, r *htt } h.logger.DebugContext(logCtx, "Successfully got reviews by place ID") - httpresponse.SendJSONResponse(w, reviews, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, reviews.Reviews, http.StatusOK, h.logger) } // GetReviewsByUserIDHandler godoc @@ -342,7 +362,7 @@ func (h *ReviewHandler) GetReviewsByUserIDHandler(w http.ResponseWriter, r *http } limit := 10 offset := limit * (page - 1) - reviews, err := h.uc.GetReviewsByUserID(context.Background(), uint(userID), limit, offset) + reviews, err := h.client.GetReviewsByUserID(r.Context(), &gen.GetReviewsByUserIDRequest{UserId: uint32(userID), Limit: int32(limit), Offset: int32(offset)}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.Int("userID", int(userID))) response, status := ErrorCheck(err, "retrieve", h.logger, logCtx) @@ -351,7 +371,7 @@ func (h *ReviewHandler) GetReviewsByUserIDHandler(w http.ResponseWriter, r *http } h.logger.DebugContext(logCtx, "Successfully got reviews by user ID") - httpresponse.SendJSONResponse(w, reviews, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, reviews.Reviews, http.StatusOK, h.logger) } // GetReviewHandler godoc @@ -381,7 +401,7 @@ func (h *ReviewHandler) GetReviewHandler(w http.ResponseWriter, r *http.Request) return } - review, err := h.uc.GetReview(context.Background(), uint(reviewID)) + review, err := h.client.GetReview(r.Context(), &gen.GetReviewRequest{Id: uint32(reviewID)}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.String("reviewID", reviewIDStr)) response, status := ErrorCheck(err, "retrieve", h.logger, logCtx) @@ -391,5 +411,5 @@ func (h *ReviewHandler) GetReviewHandler(w http.ResponseWriter, r *http.Request) h.logger.DebugContext(logCtx, "Successfully got review by ID") - httpresponse.SendJSONResponse(w, review, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, review.Review, http.StatusOK, h.logger) } diff --git a/internal/pkg/reviews/mocks/mock.go b/internal/pkg/reviews/mocks/mock.go new file mode 100644 index 0000000..0e339fc --- /dev/null +++ b/internal/pkg/reviews/mocks/mock.go @@ -0,0 +1,235 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: interfaces.go + +// Package mock_reviews is a generated GoMock package. +package mock_reviews + +import ( + models "2024_2_ThereWillBeName/internal/models" + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockReviewsUsecase is a mock of ReviewsUsecase interface. +type MockReviewsUsecase struct { + ctrl *gomock.Controller + recorder *MockReviewsUsecaseMockRecorder +} + +// MockReviewsUsecaseMockRecorder is the mock recorder for MockReviewsUsecase. +type MockReviewsUsecaseMockRecorder struct { + mock *MockReviewsUsecase +} + +// NewMockReviewsUsecase creates a new mock instance. +func NewMockReviewsUsecase(ctrl *gomock.Controller) *MockReviewsUsecase { + mock := &MockReviewsUsecase{ctrl: ctrl} + mock.recorder = &MockReviewsUsecaseMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockReviewsUsecase) EXPECT() *MockReviewsUsecaseMockRecorder { + return m.recorder +} + +// CreateReview mocks base method. +func (m *MockReviewsUsecase) CreateReview(ctx context.Context, review models.Review) (models.GetReview, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateReview", ctx, review) + ret0, _ := ret[0].(models.GetReview) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateReview indicates an expected call of CreateReview. +func (mr *MockReviewsUsecaseMockRecorder) CreateReview(ctx, review interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateReview", reflect.TypeOf((*MockReviewsUsecase)(nil).CreateReview), ctx, review) +} + +// DeleteReview mocks base method. +func (m *MockReviewsUsecase) DeleteReview(ctx context.Context, reviewID uint) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteReview", ctx, reviewID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteReview indicates an expected call of DeleteReview. +func (mr *MockReviewsUsecaseMockRecorder) DeleteReview(ctx, reviewID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReview", reflect.TypeOf((*MockReviewsUsecase)(nil).DeleteReview), ctx, reviewID) +} + +// GetReview mocks base method. +func (m *MockReviewsUsecase) GetReview(ctx context.Context, reviewID uint) (models.GetReview, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReview", ctx, reviewID) + ret0, _ := ret[0].(models.GetReview) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReview indicates an expected call of GetReview. +func (mr *MockReviewsUsecaseMockRecorder) GetReview(ctx, reviewID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReview", reflect.TypeOf((*MockReviewsUsecase)(nil).GetReview), ctx, reviewID) +} + +// GetReviewsByPlaceID mocks base method. +func (m *MockReviewsUsecase) GetReviewsByPlaceID(ctx context.Context, placeID uint, limit, offset int) ([]models.GetReview, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReviewsByPlaceID", ctx, placeID, limit, offset) + ret0, _ := ret[0].([]models.GetReview) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReviewsByPlaceID indicates an expected call of GetReviewsByPlaceID. +func (mr *MockReviewsUsecaseMockRecorder) GetReviewsByPlaceID(ctx, placeID, limit, offset interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReviewsByPlaceID", reflect.TypeOf((*MockReviewsUsecase)(nil).GetReviewsByPlaceID), ctx, placeID, limit, offset) +} + +// GetReviewsByUserID mocks base method. +func (m *MockReviewsUsecase) GetReviewsByUserID(ctx context.Context, userID uint, limit, offset int) ([]models.GetReviewByUserID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReviewsByUserID", ctx, userID, limit, offset) + ret0, _ := ret[0].([]models.GetReviewByUserID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReviewsByUserID indicates an expected call of GetReviewsByUserID. +func (mr *MockReviewsUsecaseMockRecorder) GetReviewsByUserID(ctx, userID, limit, offset interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReviewsByUserID", reflect.TypeOf((*MockReviewsUsecase)(nil).GetReviewsByUserID), ctx, userID, limit, offset) +} + +// UpdateReview mocks base method. +func (m *MockReviewsUsecase) UpdateReview(ctx context.Context, review models.Review) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateReview", ctx, review) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateReview indicates an expected call of UpdateReview. +func (mr *MockReviewsUsecaseMockRecorder) UpdateReview(ctx, review interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateReview", reflect.TypeOf((*MockReviewsUsecase)(nil).UpdateReview), ctx, review) +} + +// MockReviewsRepo is a mock of ReviewsRepo interface. +type MockReviewsRepo struct { + ctrl *gomock.Controller + recorder *MockReviewsRepoMockRecorder +} + +// MockReviewsRepoMockRecorder is the mock recorder for MockReviewsRepo. +type MockReviewsRepoMockRecorder struct { + mock *MockReviewsRepo +} + +// NewMockReviewsRepo creates a new mock instance. +func NewMockReviewsRepo(ctrl *gomock.Controller) *MockReviewsRepo { + mock := &MockReviewsRepo{ctrl: ctrl} + mock.recorder = &MockReviewsRepoMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockReviewsRepo) EXPECT() *MockReviewsRepoMockRecorder { + return m.recorder +} + +// CreateReview mocks base method. +func (m *MockReviewsRepo) CreateReview(ctx context.Context, review models.Review) (models.GetReview, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateReview", ctx, review) + ret0, _ := ret[0].(models.GetReview) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateReview indicates an expected call of CreateReview. +func (mr *MockReviewsRepoMockRecorder) CreateReview(ctx, review interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateReview", reflect.TypeOf((*MockReviewsRepo)(nil).CreateReview), ctx, review) +} + +// DeleteReview mocks base method. +func (m *MockReviewsRepo) DeleteReview(ctx context.Context, reviewID uint) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteReview", ctx, reviewID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteReview indicates an expected call of DeleteReview. +func (mr *MockReviewsRepoMockRecorder) DeleteReview(ctx, reviewID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteReview", reflect.TypeOf((*MockReviewsRepo)(nil).DeleteReview), ctx, reviewID) +} + +// GetReview mocks base method. +func (m *MockReviewsRepo) GetReview(ctx context.Context, reviewID uint) (models.GetReview, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReview", ctx, reviewID) + ret0, _ := ret[0].(models.GetReview) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReview indicates an expected call of GetReview. +func (mr *MockReviewsRepoMockRecorder) GetReview(ctx, reviewID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReview", reflect.TypeOf((*MockReviewsRepo)(nil).GetReview), ctx, reviewID) +} + +// GetReviewsByPlaceID mocks base method. +func (m *MockReviewsRepo) GetReviewsByPlaceID(ctx context.Context, placeID uint, limit, offset int) ([]models.GetReview, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReviewsByPlaceID", ctx, placeID, limit, offset) + ret0, _ := ret[0].([]models.GetReview) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReviewsByPlaceID indicates an expected call of GetReviewsByPlaceID. +func (mr *MockReviewsRepoMockRecorder) GetReviewsByPlaceID(ctx, placeID, limit, offset interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReviewsByPlaceID", reflect.TypeOf((*MockReviewsRepo)(nil).GetReviewsByPlaceID), ctx, placeID, limit, offset) +} + +// GetReviewsByUserID mocks base method. +func (m *MockReviewsRepo) GetReviewsByUserID(ctx context.Context, userID uint, limit, offset int) ([]models.GetReviewByUserID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReviewsByUserID", ctx, userID, limit, offset) + ret0, _ := ret[0].([]models.GetReviewByUserID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetReviewsByUserID indicates an expected call of GetReviewsByUserID. +func (mr *MockReviewsRepoMockRecorder) GetReviewsByUserID(ctx, userID, limit, offset interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReviewsByUserID", reflect.TypeOf((*MockReviewsRepo)(nil).GetReviewsByUserID), ctx, userID, limit, offset) +} + +// UpdateReview mocks base method. +func (m *MockReviewsRepo) UpdateReview(ctx context.Context, review models.Review) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateReview", ctx, review) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateReview indicates an expected call of UpdateReview. +func (mr *MockReviewsRepoMockRecorder) UpdateReview(ctx, review interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateReview", reflect.TypeOf((*MockReviewsRepo)(nil).UpdateReview), ctx, review) +} diff --git a/internal/pkg/search/delivery/grpc/gen/search.pb.go b/internal/pkg/search/delivery/grpc/gen/search.pb.go new file mode 100644 index 0000000..c0f3b1f --- /dev/null +++ b/internal/pkg/search/delivery/grpc/gen/search.pb.go @@ -0,0 +1,253 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.2 +// protoc v3.12.4 +// source: search.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type SearchRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DecodedQuery string `protobuf:"bytes,1,opt,name=decoded_query,json=decodedQuery,proto3" json:"decoded_query,omitempty"` +} + +func (x *SearchRequest) Reset() { + *x = SearchRequest{} + mi := &file_search_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchRequest) ProtoMessage() {} + +func (x *SearchRequest) ProtoReflect() protoreflect.Message { + mi := &file_search_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchRequest.ProtoReflect.Descriptor instead. +func (*SearchRequest) Descriptor() ([]byte, []int) { + return file_search_proto_rawDescGZIP(), []int{0} +} + +func (x *SearchRequest) GetDecodedQuery() string { + if x != nil { + return x.DecodedQuery + } + return "" +} + +type SearchResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SearchResult []*SearchResult `protobuf:"bytes,1,rep,name=search_result,json=searchResult,proto3" json:"search_result,omitempty"` +} + +func (x *SearchResponse) Reset() { + *x = SearchResponse{} + mi := &file_search_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchResponse) ProtoMessage() {} + +func (x *SearchResponse) ProtoReflect() protoreflect.Message { + mi := &file_search_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchResponse.ProtoReflect.Descriptor instead. +func (*SearchResponse) Descriptor() ([]byte, []int) { + return file_search_proto_rawDescGZIP(), []int{1} +} + +func (x *SearchResponse) GetSearchResult() []*SearchResult { + if x != nil { + return x.SearchResult + } + return nil +} + +type SearchResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Id uint32 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` +} + +func (x *SearchResult) Reset() { + *x = SearchResult{} + mi := &file_search_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SearchResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SearchResult) ProtoMessage() {} + +func (x *SearchResult) ProtoReflect() protoreflect.Message { + mi := &file_search_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SearchResult.ProtoReflect.Descriptor instead. +func (*SearchResult) Descriptor() ([]byte, []int) { + return file_search_proto_rawDescGZIP(), []int{2} +} + +func (x *SearchResult) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *SearchResult) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *SearchResult) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +var File_search_proto protoreflect.FileDescriptor + +var file_search_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, + 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x22, 0x34, 0x0a, 0x0d, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x65, 0x63, 0x6f, 0x64, + 0x65, 0x64, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x51, 0x75, 0x65, 0x72, 0x79, 0x22, 0x4b, 0x0a, 0x0e, + 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, + 0x0a, 0x0d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x0c, 0x73, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 0x46, 0x0a, 0x0c, 0x53, 0x65, 0x61, + 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x32, 0x41, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x37, 0x0a, 0x06, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x12, 0x15, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x53, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x73, + 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2e, 0x5a, 0x2c, 0x2e, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x64, 0x65, + 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x2f, + 0x3b, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_search_proto_rawDescOnce sync.Once + file_search_proto_rawDescData = file_search_proto_rawDesc +) + +func file_search_proto_rawDescGZIP() []byte { + file_search_proto_rawDescOnce.Do(func() { + file_search_proto_rawDescData = protoimpl.X.CompressGZIP(file_search_proto_rawDescData) + }) + return file_search_proto_rawDescData +} + +var file_search_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_search_proto_goTypes = []any{ + (*SearchRequest)(nil), // 0: search.SearchRequest + (*SearchResponse)(nil), // 1: search.SearchResponse + (*SearchResult)(nil), // 2: search.SearchResult +} +var file_search_proto_depIdxs = []int32{ + 2, // 0: search.SearchResponse.search_result:type_name -> search.SearchResult + 0, // 1: search.Search.Search:input_type -> search.SearchRequest + 1, // 2: search.Search.Search:output_type -> search.SearchResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_search_proto_init() } +func file_search_proto_init() { + if File_search_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_search_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_search_proto_goTypes, + DependencyIndexes: file_search_proto_depIdxs, + MessageInfos: file_search_proto_msgTypes, + }.Build() + File_search_proto = out.File + file_search_proto_rawDesc = nil + file_search_proto_goTypes = nil + file_search_proto_depIdxs = nil +} diff --git a/internal/pkg/search/delivery/grpc/gen/search_grpc.pb.go b/internal/pkg/search/delivery/grpc/gen/search_grpc.pb.go new file mode 100644 index 0000000..e684226 --- /dev/null +++ b/internal/pkg/search/delivery/grpc/gen/search_grpc.pb.go @@ -0,0 +1,121 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.12.4 +// source: search.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Search_Search_FullMethodName = "/search.Search/Search" +) + +// SearchClient is the client API for Search service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type SearchClient interface { + Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error) +} + +type searchClient struct { + cc grpc.ClientConnInterface +} + +func NewSearchClient(cc grpc.ClientConnInterface) SearchClient { + return &searchClient{cc} +} + +func (c *searchClient) Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SearchResponse) + err := c.cc.Invoke(ctx, Search_Search_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SearchServer is the server API for Search service. +// All implementations must embed UnimplementedSearchServer +// for forward compatibility. +type SearchServer interface { + Search(context.Context, *SearchRequest) (*SearchResponse, error) + mustEmbedUnimplementedSearchServer() +} + +// UnimplementedSearchServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSearchServer struct{} + +func (UnimplementedSearchServer) Search(context.Context, *SearchRequest) (*SearchResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Search not implemented") +} +func (UnimplementedSearchServer) mustEmbedUnimplementedSearchServer() {} +func (UnimplementedSearchServer) testEmbeddedByValue() {} + +// UnsafeSearchServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SearchServer will +// result in compilation errors. +type UnsafeSearchServer interface { + mustEmbedUnimplementedSearchServer() +} + +func RegisterSearchServer(s grpc.ServiceRegistrar, srv SearchServer) { + // If the following call pancis, it indicates UnimplementedSearchServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Search_ServiceDesc, srv) +} + +func _Search_Search_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SearchRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SearchServer).Search(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Search_Search_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SearchServer).Search(ctx, req.(*SearchRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Search_ServiceDesc is the grpc.ServiceDesc for Search service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Search_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "search.Search", + HandlerType: (*SearchServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Search", + Handler: _Search_Search_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "search.proto", +} diff --git a/internal/pkg/search/delivery/grpc/grpc.go b/internal/pkg/search/delivery/grpc/grpc.go new file mode 100644 index 0000000..f0f2599 --- /dev/null +++ b/internal/pkg/search/delivery/grpc/grpc.go @@ -0,0 +1,36 @@ +package grpc + +import ( + "2024_2_ThereWillBeName/internal/pkg/search" + searchGen "2024_2_ThereWillBeName/internal/pkg/search/delivery/grpc/gen" + "context" + "log/slog" +) + +type GrpcSearchHandler struct { + searchGen.SearchServer + uc search.SearchUsecase + logger *slog.Logger +} + +func NewGrpcSearchHandler(uc search.SearchUsecase, logger *slog.Logger) *GrpcSearchHandler { + return &GrpcSearchHandler{uc: uc, logger: logger} +} + +func (h *GrpcSearchHandler) Search(ctx context.Context, in *searchGen.SearchRequest) (*searchGen.SearchResponse, error) { + results, err := h.uc.Search(context.Background(), in.DecodedQuery) + if err != nil { + return nil, err + } + + grpcResults := make([]*searchGen.SearchResult, len(results)) + for i, searchResult := range results { + grpcResults[i] = &searchGen.SearchResult{ + Name: searchResult.Name, + Id: uint32(searchResult.Id), + Type: searchResult.Type, + } + } + + return &searchGen.SearchResponse{SearchResult: grpcResults}, nil +} diff --git a/internal/pkg/search/delivery/http/handler.go b/internal/pkg/search/delivery/http/handler.go new file mode 100644 index 0000000..e4e65d7 --- /dev/null +++ b/internal/pkg/search/delivery/http/handler.go @@ -0,0 +1,58 @@ +package http + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/httpresponses" + log "2024_2_ThereWillBeName/internal/pkg/logger" + searchGen "2024_2_ThereWillBeName/internal/pkg/search/delivery/grpc/gen" + "errors" + "log/slog" + "net/http" + "net/url" +) + +type SearchHandler struct { + client searchGen.SearchClient + logger *slog.Logger +} + +func NewSearchHandler(client searchGen.SearchClient, logger *slog.Logger) *SearchHandler { + return &SearchHandler{client, logger} + +} + +func (h *SearchHandler) Search(w http.ResponseWriter, r *http.Request) { + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request for global searching places and cities") + + query := r.URL.Query().Get("query") + if query == "" { + h.logger.Warn("Query parameter can't be empty") + httpresponses.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + return + } + + decodedQuery, err := url.QueryUnescape(query) + if err != nil { + h.logger.Warn("Error decoding query", slog.String("error", err.Error())) + httpresponses.SendJSONResponse(w, nil, http.StatusBadRequest, h.logger) + return + } + + results, err := h.client.Search(r.Context(), &searchGen.SearchRequest{DecodedQuery: decodedQuery}) + if err != nil { + if errors.Is(err, models.ErrNotFound) { + h.logger.WarnContext(logCtx, "No results found for query", slog.String("decodedQuery", decodedQuery)) + + httpresponses.SendJSONResponse(w, nil, http.StatusNotFound, h.logger) + } + + h.logger.ErrorContext(logCtx, "Failed to search", slog.String("decodedQuery", decodedQuery), slog.String("error", err.Error())) + + httpresponses.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger) + return + } + h.logger.InfoContext(logCtx, "Search completed successfully", slog.Int("resultCount", len(results.SearchResult))) + + httpresponses.SendJSONResponse(w, results.SearchResult, http.StatusOK, h.logger) +} diff --git a/internal/pkg/search/interfaces.go b/internal/pkg/search/interfaces.go new file mode 100644 index 0000000..51574e7 --- /dev/null +++ b/internal/pkg/search/interfaces.go @@ -0,0 +1,14 @@ +package search + +import ( + "2024_2_ThereWillBeName/internal/models" + "context" +) + +type SearchUsecase interface { + Search(ctx context.Context, query string) ([]models.SearchResult, error) +} + +type SearchRepo interface { + SearchCitiesAndPlacesBySubString(ctx context.Context, query string) ([]models.SearchResult, error) +} diff --git a/internal/pkg/search/repo/search_repository.go b/internal/pkg/search/repo/search_repository.go new file mode 100644 index 0000000..c53a6b1 --- /dev/null +++ b/internal/pkg/search/repo/search_repository.go @@ -0,0 +1,49 @@ +package search + +import ( + "2024_2_ThereWillBeName/internal/models" + "context" + "database/sql" + "fmt" +) + +type SearchRepository struct { + db *sql.DB +} + +func NewSearchRepository(db *sql.DB) *SearchRepository { + return &SearchRepository{db} +} + +func (r *SearchRepository) SearchCitiesAndPlacesBySubString(ctx context.Context, query string) ([]models.SearchResult, error) { + queryStr := ` + SELECT id, name, 'city' AS type + FROM city + WHERE name ILIKE '%' || $1 || '%' + UNION ALL + SELECT id, name, 'place' AS type + FROM place + WHERE name ILIKE '%' || $1 || '%' + ` + + rows, err := r.db.QueryContext(ctx, queryStr, query) + if err != nil { + return nil, fmt.Errorf("failed to execute search query: %w", models.ErrInternal) + } + defer rows.Close() + + var searchResults []models.SearchResult + for rows.Next() { + var item models.SearchResult + if err := rows.Scan(&item.Id, &item.Name, &item.Type); err != nil { + return nil, fmt.Errorf("failed to scan search row: %w", models.ErrInternal) + } + searchResults = append(searchResults, item) + } + + if len(searchResults) == 0 { + return nil, fmt.Errorf("no results found matching query %q: %w", query, models.ErrNotFound) + } + + return searchResults, nil +} diff --git a/internal/pkg/search/usecase/search_usecase.go b/internal/pkg/search/usecase/search_usecase.go new file mode 100644 index 0000000..183f878 --- /dev/null +++ b/internal/pkg/search/usecase/search_usecase.go @@ -0,0 +1,26 @@ +package usecase + +import ( + "2024_2_ThereWillBeName/internal/models" + search "2024_2_ThereWillBeName/internal/pkg/search" + "context" +) + +type SearchUsecaseImpl struct { + repo search.SearchRepo +} + +func NewSearchUsecase(repo search.SearchRepo) *SearchUsecaseImpl { + return &SearchUsecaseImpl{repo: repo} +} + +func (uc *SearchUsecaseImpl) Search(ctx context.Context, query string) ([]models.SearchResult, error) { + var results []models.SearchResult + + results, err := uc.repo.SearchCitiesAndPlacesBySubString(ctx, query) + if err != nil { + return nil, err + } + + return results, nil +} diff --git a/internal/pkg/survey/delivery/grpc/gen/survey.pb.go b/internal/pkg/survey/delivery/grpc/gen/survey.pb.go new file mode 100644 index 0000000..558cf8f --- /dev/null +++ b/internal/pkg/survey/delivery/grpc/gen/survey.pb.go @@ -0,0 +1,821 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.2 +// protoc v3.12.4 +// source: proto/survey.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetSurveyByIdRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetSurveyByIdRequest) Reset() { + *x = GetSurveyByIdRequest{} + mi := &file_proto_survey_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSurveyByIdRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSurveyByIdRequest) ProtoMessage() {} + +func (x *GetSurveyByIdRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSurveyByIdRequest.ProtoReflect.Descriptor instead. +func (*GetSurveyByIdRequest) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{0} +} + +func (x *GetSurveyByIdRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +type Survey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + SurveyText string `protobuf:"bytes,2,opt,name=survey_text,json=surveyText,proto3" json:"survey_text,omitempty"` + MaxRating uint32 `protobuf:"varint,3,opt,name=max_rating,json=maxRating,proto3" json:"max_rating,omitempty"` +} + +func (x *Survey) Reset() { + *x = Survey{} + mi := &file_proto_survey_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Survey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Survey) ProtoMessage() {} + +func (x *Survey) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Survey.ProtoReflect.Descriptor instead. +func (*Survey) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{1} +} + +func (x *Survey) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Survey) GetSurveyText() string { + if x != nil { + return x.SurveyText + } + return "" +} + +func (x *Survey) GetMaxRating() uint32 { + if x != nil { + return x.MaxRating + } + return 0 +} + +type SurveyResponce struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SurveyId uint32 `protobuf:"varint,1,opt,name=survey_id,json=surveyId,proto3" json:"survey_id,omitempty"` + UserId uint32 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Rating uint32 `protobuf:"varint,4,opt,name=rating,proto3" json:"rating,omitempty"` +} + +func (x *SurveyResponce) Reset() { + *x = SurveyResponce{} + mi := &file_proto_survey_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SurveyResponce) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SurveyResponce) ProtoMessage() {} + +func (x *SurveyResponce) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SurveyResponce.ProtoReflect.Descriptor instead. +func (*SurveyResponce) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{2} +} + +func (x *SurveyResponce) GetSurveyId() uint32 { + if x != nil { + return x.SurveyId + } + return 0 +} + +func (x *SurveyResponce) GetUserId() uint32 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *SurveyResponce) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *SurveyResponce) GetRating() uint32 { + if x != nil { + return x.Rating + } + return 0 +} + +type GetSurveyByIdResponce struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Survey *Survey `protobuf:"bytes,1,opt,name=survey,proto3" json:"survey,omitempty"` +} + +func (x *GetSurveyByIdResponce) Reset() { + *x = GetSurveyByIdResponce{} + mi := &file_proto_survey_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSurveyByIdResponce) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSurveyByIdResponce) ProtoMessage() {} + +func (x *GetSurveyByIdResponce) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSurveyByIdResponce.ProtoReflect.Descriptor instead. +func (*GetSurveyByIdResponce) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{3} +} + +func (x *GetSurveyByIdResponce) GetSurvey() *Survey { + if x != nil { + return x.Survey + } + return nil +} + +type CreateSurveyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ServeyResponce *SurveyResponce `protobuf:"bytes,1,opt,name=serveyResponce,proto3" json:"serveyResponce,omitempty"` +} + +func (x *CreateSurveyRequest) Reset() { + *x = CreateSurveyRequest{} + mi := &file_proto_survey_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateSurveyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSurveyRequest) ProtoMessage() {} + +func (x *CreateSurveyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateSurveyRequest.ProtoReflect.Descriptor instead. +func (*CreateSurveyRequest) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateSurveyRequest) GetServeyResponce() *SurveyResponce { + if x != nil { + return x.ServeyResponce + } + return nil +} + +type CreateSurveyResponce struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` +} + +func (x *CreateSurveyResponce) Reset() { + *x = CreateSurveyResponce{} + mi := &file_proto_survey_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateSurveyResponce) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSurveyResponce) ProtoMessage() {} + +func (x *CreateSurveyResponce) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateSurveyResponce.ProtoReflect.Descriptor instead. +func (*CreateSurveyResponce) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateSurveyResponce) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +type SurveyStatsBySurvey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ServeyId uint32 `protobuf:"varint,1,opt,name=servey_id,json=serveyId,proto3" json:"servey_id,omitempty"` + ServeyText string `protobuf:"bytes,2,opt,name=servey_text,json=serveyText,proto3" json:"servey_text,omitempty"` + AvgRating float32 `protobuf:"fixed32,3,opt,name=avg_rating,json=avgRating,proto3" json:"avg_rating,omitempty"` + RatingsCount map[int32]int32 `protobuf:"bytes,4,rep,name=ratings_count,json=ratingsCount,proto3" json:"ratings_count,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` +} + +func (x *SurveyStatsBySurvey) Reset() { + *x = SurveyStatsBySurvey{} + mi := &file_proto_survey_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SurveyStatsBySurvey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SurveyStatsBySurvey) ProtoMessage() {} + +func (x *SurveyStatsBySurvey) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SurveyStatsBySurvey.ProtoReflect.Descriptor instead. +func (*SurveyStatsBySurvey) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{6} +} + +func (x *SurveyStatsBySurvey) GetServeyId() uint32 { + if x != nil { + return x.ServeyId + } + return 0 +} + +func (x *SurveyStatsBySurvey) GetServeyText() string { + if x != nil { + return x.ServeyText + } + return "" +} + +func (x *SurveyStatsBySurvey) GetAvgRating() float32 { + if x != nil { + return x.AvgRating + } + return 0 +} + +func (x *SurveyStatsBySurvey) GetRatingsCount() map[int32]int32 { + if x != nil { + return x.RatingsCount + } + return nil +} + +type GetSurveyStatsBySurveyIdRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *GetSurveyStatsBySurveyIdRequest) Reset() { + *x = GetSurveyStatsBySurveyIdRequest{} + mi := &file_proto_survey_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSurveyStatsBySurveyIdRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSurveyStatsBySurveyIdRequest) ProtoMessage() {} + +func (x *GetSurveyStatsBySurveyIdRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSurveyStatsBySurveyIdRequest.ProtoReflect.Descriptor instead. +func (*GetSurveyStatsBySurveyIdRequest) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{7} +} + +func (x *GetSurveyStatsBySurveyIdRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +type GetSurveyStatsBySurveyIdResponce struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SurveyStatsBySurvey *SurveyStatsBySurvey `protobuf:"bytes,1,opt,name=surveyStatsBySurvey,proto3" json:"surveyStatsBySurvey,omitempty"` +} + +func (x *GetSurveyStatsBySurveyIdResponce) Reset() { + *x = GetSurveyStatsBySurveyIdResponce{} + mi := &file_proto_survey_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSurveyStatsBySurveyIdResponce) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSurveyStatsBySurveyIdResponce) ProtoMessage() {} + +func (x *GetSurveyStatsBySurveyIdResponce) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSurveyStatsBySurveyIdResponce.ProtoReflect.Descriptor instead. +func (*GetSurveyStatsBySurveyIdResponce) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{8} +} + +func (x *GetSurveyStatsBySurveyIdResponce) GetSurveyStatsBySurvey() *SurveyStatsBySurvey { + if x != nil { + return x.SurveyStatsBySurvey + } + return nil +} + +type UserSurveyStats struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ServeyId uint32 `protobuf:"varint,1,opt,name=servey_id,json=serveyId,proto3" json:"servey_id,omitempty"` + ServeyText string `protobuf:"bytes,2,opt,name=servey_text,json=serveyText,proto3" json:"servey_text,omitempty"` + Answered bool `protobuf:"varint,3,opt,name=answered,proto3" json:"answered,omitempty"` +} + +func (x *UserSurveyStats) Reset() { + *x = UserSurveyStats{} + mi := &file_proto_survey_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UserSurveyStats) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UserSurveyStats) ProtoMessage() {} + +func (x *UserSurveyStats) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UserSurveyStats.ProtoReflect.Descriptor instead. +func (*UserSurveyStats) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{9} +} + +func (x *UserSurveyStats) GetServeyId() uint32 { + if x != nil { + return x.ServeyId + } + return 0 +} + +func (x *UserSurveyStats) GetServeyText() string { + if x != nil { + return x.ServeyText + } + return "" +} + +func (x *UserSurveyStats) GetAnswered() bool { + if x != nil { + return x.Answered + } + return false +} + +type GetSurveyStatsByUserIdRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId uint32 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"` +} + +func (x *GetSurveyStatsByUserIdRequest) Reset() { + *x = GetSurveyStatsByUserIdRequest{} + mi := &file_proto_survey_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSurveyStatsByUserIdRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSurveyStatsByUserIdRequest) ProtoMessage() {} + +func (x *GetSurveyStatsByUserIdRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSurveyStatsByUserIdRequest.ProtoReflect.Descriptor instead. +func (*GetSurveyStatsByUserIdRequest) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{10} +} + +func (x *GetSurveyStatsByUserIdRequest) GetUserId() uint32 { + if x != nil { + return x.UserId + } + return 0 +} + +type GetSurveyStatsByUserIdResponce struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserServeyStats []*UserSurveyStats `protobuf:"bytes,1,rep,name=userServeyStats,proto3" json:"userServeyStats,omitempty"` +} + +func (x *GetSurveyStatsByUserIdResponce) Reset() { + *x = GetSurveyStatsByUserIdResponce{} + mi := &file_proto_survey_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSurveyStatsByUserIdResponce) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSurveyStatsByUserIdResponce) ProtoMessage() {} + +func (x *GetSurveyStatsByUserIdResponce) ProtoReflect() protoreflect.Message { + mi := &file_proto_survey_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSurveyStatsByUserIdResponce.ProtoReflect.Descriptor instead. +func (*GetSurveyStatsByUserIdResponce) Descriptor() ([]byte, []int) { + return file_proto_survey_proto_rawDescGZIP(), []int{11} +} + +func (x *GetSurveyStatsByUserIdResponce) GetUserServeyStats() []*UserSurveyStats { + if x != nil { + return x.UserServeyStats + } + return nil +} + +var File_proto_survey_proto protoreflect.FileDescriptor + +var file_proto_survey_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x22, 0x26, 0x0a, 0x14, + 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x02, 0x69, 0x64, 0x22, 0x58, 0x0a, 0x06, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, + 0x0a, 0x0b, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x54, 0x65, 0x78, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6d, 0x61, 0x78, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x80, + 0x01, 0x0a, 0x0e, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x63, + 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x49, 0x64, 0x12, 0x17, + 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x61, 0x74, + 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x72, 0x61, 0x74, 0x69, 0x6e, + 0x67, 0x22, 0x3f, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x42, 0x79, + 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x73, 0x75, + 0x72, 0x76, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x75, 0x72, + 0x76, 0x65, 0x79, 0x2e, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x52, 0x06, 0x73, 0x75, 0x72, 0x76, + 0x65, 0x79, 0x22, 0x55, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x72, 0x76, + 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3e, 0x0a, 0x0e, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x16, 0x2e, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x2e, 0x53, 0x75, 0x72, 0x76, 0x65, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x30, 0x0a, 0x14, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x63, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x87, 0x02, 0x0a, 0x13, + 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, 0x72, + 0x76, 0x65, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x79, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x79, 0x49, 0x64, + 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x79, 0x54, 0x65, 0x78, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x76, 0x67, 0x5f, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x09, 0x61, 0x76, 0x67, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, + 0x12, 0x52, 0x0a, 0x0d, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, + 0x2e, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, + 0x72, 0x76, 0x65, 0x79, 0x2e, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, + 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x3f, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x31, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, + 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x49, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x71, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, 0x72, 0x76, + 0x65, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x4d, 0x0a, 0x13, + 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, 0x72, + 0x76, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x75, 0x72, 0x76, + 0x65, 0x79, 0x2e, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, + 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x52, 0x13, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x22, 0x6b, 0x0a, 0x0f, 0x55, + 0x73, 0x65, 0x72, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x1b, + 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x79, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x79, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x79, 0x54, 0x65, 0x78, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x65, 0x64, 0x22, 0x37, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, + 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, + 0x64, 0x22, 0x63, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, + 0x61, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x63, 0x65, 0x12, 0x41, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, + 0x75, 0x72, 0x76, 0x65, 0x79, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, + 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x32, 0x88, 0x03, 0x0a, 0x0d, 0x53, 0x75, 0x72, 0x76, 0x65, + 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x72, 0x76, 0x65, 0x79, 0x42, 0x79, 0x49, 0x64, 0x12, 0x1c, 0x2e, 0x73, 0x75, 0x72, 0x76, + 0x65, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x42, 0x79, 0x49, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x42, 0x79, 0x49, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x12, 0x1b, 0x2e, 0x73, 0x75, 0x72, 0x76, 0x65, + 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, + 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x49, + 0x64, 0x12, 0x27, 0x2e, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, + 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, 0x72, 0x76, 0x65, + 0x79, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x73, 0x75, 0x72, + 0x76, 0x65, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, + 0x74, 0x73, 0x42, 0x79, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, + 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x25, 0x2e, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, + 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x73, 0x75, 0x72, 0x76, 0x65, 0x79, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x72, 0x76, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x73, 0x42, + 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x63, 0x65, 0x22, + 0x00, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x3b, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_proto_survey_proto_rawDescOnce sync.Once + file_proto_survey_proto_rawDescData = file_proto_survey_proto_rawDesc +) + +func file_proto_survey_proto_rawDescGZIP() []byte { + file_proto_survey_proto_rawDescOnce.Do(func() { + file_proto_survey_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_survey_proto_rawDescData) + }) + return file_proto_survey_proto_rawDescData +} + +var file_proto_survey_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_proto_survey_proto_goTypes = []any{ + (*GetSurveyByIdRequest)(nil), // 0: survey.GetSurveyByIdRequest + (*Survey)(nil), // 1: survey.Survey + (*SurveyResponce)(nil), // 2: survey.SurveyResponce + (*GetSurveyByIdResponce)(nil), // 3: survey.GetSurveyByIdResponce + (*CreateSurveyRequest)(nil), // 4: survey.CreateSurveyRequest + (*CreateSurveyResponce)(nil), // 5: survey.CreateSurveyResponce + (*SurveyStatsBySurvey)(nil), // 6: survey.SurveyStatsBySurvey + (*GetSurveyStatsBySurveyIdRequest)(nil), // 7: survey.GetSurveyStatsBySurveyIdRequest + (*GetSurveyStatsBySurveyIdResponce)(nil), // 8: survey.GetSurveyStatsBySurveyIdResponce + (*UserSurveyStats)(nil), // 9: survey.UserSurveyStats + (*GetSurveyStatsByUserIdRequest)(nil), // 10: survey.GetSurveyStatsByUserIdRequest + (*GetSurveyStatsByUserIdResponce)(nil), // 11: survey.GetSurveyStatsByUserIdResponce + nil, // 12: survey.SurveyStatsBySurvey.RatingsCountEntry +} +var file_proto_survey_proto_depIdxs = []int32{ + 1, // 0: survey.GetSurveyByIdResponce.survey:type_name -> survey.Survey + 2, // 1: survey.CreateSurveyRequest.serveyResponce:type_name -> survey.SurveyResponce + 12, // 2: survey.SurveyStatsBySurvey.ratings_count:type_name -> survey.SurveyStatsBySurvey.RatingsCountEntry + 6, // 3: survey.GetSurveyStatsBySurveyIdResponce.surveyStatsBySurvey:type_name -> survey.SurveyStatsBySurvey + 9, // 4: survey.GetSurveyStatsByUserIdResponce.userServeyStats:type_name -> survey.UserSurveyStats + 0, // 5: survey.SurveyService.GetSurveyById:input_type -> survey.GetSurveyByIdRequest + 4, // 6: survey.SurveyService.CreateSurvey:input_type -> survey.CreateSurveyRequest + 7, // 7: survey.SurveyService.GetSurveyStatsBySurveyId:input_type -> survey.GetSurveyStatsBySurveyIdRequest + 10, // 8: survey.SurveyService.GetSurveyStatsByUserId:input_type -> survey.GetSurveyStatsByUserIdRequest + 3, // 9: survey.SurveyService.GetSurveyById:output_type -> survey.GetSurveyByIdResponce + 5, // 10: survey.SurveyService.CreateSurvey:output_type -> survey.CreateSurveyResponce + 8, // 11: survey.SurveyService.GetSurveyStatsBySurveyId:output_type -> survey.GetSurveyStatsBySurveyIdResponce + 11, // 12: survey.SurveyService.GetSurveyStatsByUserId:output_type -> survey.GetSurveyStatsByUserIdResponce + 9, // [9:13] is the sub-list for method output_type + 5, // [5:9] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_proto_survey_proto_init() } +func file_proto_survey_proto_init() { + if File_proto_survey_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_proto_survey_proto_rawDesc, + NumEnums: 0, + NumMessages: 13, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_survey_proto_goTypes, + DependencyIndexes: file_proto_survey_proto_depIdxs, + MessageInfos: file_proto_survey_proto_msgTypes, + }.Build() + File_proto_survey_proto = out.File + file_proto_survey_proto_rawDesc = nil + file_proto_survey_proto_goTypes = nil + file_proto_survey_proto_depIdxs = nil +} diff --git a/internal/pkg/survey/delivery/grpc/gen/survey_grpc.pb.go b/internal/pkg/survey/delivery/grpc/gen/survey_grpc.pb.go new file mode 100644 index 0000000..056db78 --- /dev/null +++ b/internal/pkg/survey/delivery/grpc/gen/survey_grpc.pb.go @@ -0,0 +1,235 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.12.4 +// source: proto/survey.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + SurveyService_GetSurveyById_FullMethodName = "/survey.SurveyService/GetSurveyById" + SurveyService_CreateSurvey_FullMethodName = "/survey.SurveyService/CreateSurvey" + SurveyService_GetSurveyStatsBySurveyId_FullMethodName = "/survey.SurveyService/GetSurveyStatsBySurveyId" + SurveyService_GetSurveyStatsByUserId_FullMethodName = "/survey.SurveyService/GetSurveyStatsByUserId" +) + +// SurveyServiceClient is the client API for SurveyService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type SurveyServiceClient interface { + GetSurveyById(ctx context.Context, in *GetSurveyByIdRequest, opts ...grpc.CallOption) (*GetSurveyByIdResponce, error) + CreateSurvey(ctx context.Context, in *CreateSurveyRequest, opts ...grpc.CallOption) (*CreateSurveyResponce, error) + GetSurveyStatsBySurveyId(ctx context.Context, in *GetSurveyStatsBySurveyIdRequest, opts ...grpc.CallOption) (*GetSurveyStatsBySurveyIdResponce, error) + GetSurveyStatsByUserId(ctx context.Context, in *GetSurveyStatsByUserIdRequest, opts ...grpc.CallOption) (*GetSurveyStatsByUserIdResponce, error) +} + +type surveyServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewSurveyServiceClient(cc grpc.ClientConnInterface) SurveyServiceClient { + return &surveyServiceClient{cc} +} + +func (c *surveyServiceClient) GetSurveyById(ctx context.Context, in *GetSurveyByIdRequest, opts ...grpc.CallOption) (*GetSurveyByIdResponce, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetSurveyByIdResponce) + err := c.cc.Invoke(ctx, SurveyService_GetSurveyById_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *surveyServiceClient) CreateSurvey(ctx context.Context, in *CreateSurveyRequest, opts ...grpc.CallOption) (*CreateSurveyResponce, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateSurveyResponce) + err := c.cc.Invoke(ctx, SurveyService_CreateSurvey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *surveyServiceClient) GetSurveyStatsBySurveyId(ctx context.Context, in *GetSurveyStatsBySurveyIdRequest, opts ...grpc.CallOption) (*GetSurveyStatsBySurveyIdResponce, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetSurveyStatsBySurveyIdResponce) + err := c.cc.Invoke(ctx, SurveyService_GetSurveyStatsBySurveyId_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *surveyServiceClient) GetSurveyStatsByUserId(ctx context.Context, in *GetSurveyStatsByUserIdRequest, opts ...grpc.CallOption) (*GetSurveyStatsByUserIdResponce, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetSurveyStatsByUserIdResponce) + err := c.cc.Invoke(ctx, SurveyService_GetSurveyStatsByUserId_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SurveyServiceServer is the server API for SurveyService service. +// All implementations must embed UnimplementedSurveyServiceServer +// for forward compatibility. +type SurveyServiceServer interface { + GetSurveyById(context.Context, *GetSurveyByIdRequest) (*GetSurveyByIdResponce, error) + CreateSurvey(context.Context, *CreateSurveyRequest) (*CreateSurveyResponce, error) + GetSurveyStatsBySurveyId(context.Context, *GetSurveyStatsBySurveyIdRequest) (*GetSurveyStatsBySurveyIdResponce, error) + GetSurveyStatsByUserId(context.Context, *GetSurveyStatsByUserIdRequest) (*GetSurveyStatsByUserIdResponce, error) + mustEmbedUnimplementedSurveyServiceServer() +} + +// UnimplementedSurveyServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedSurveyServiceServer struct{} + +func (UnimplementedSurveyServiceServer) GetSurveyById(context.Context, *GetSurveyByIdRequest) (*GetSurveyByIdResponce, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSurveyById not implemented") +} +func (UnimplementedSurveyServiceServer) CreateSurvey(context.Context, *CreateSurveyRequest) (*CreateSurveyResponce, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateSurvey not implemented") +} +func (UnimplementedSurveyServiceServer) GetSurveyStatsBySurveyId(context.Context, *GetSurveyStatsBySurveyIdRequest) (*GetSurveyStatsBySurveyIdResponce, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSurveyStatsBySurveyId not implemented") +} +func (UnimplementedSurveyServiceServer) GetSurveyStatsByUserId(context.Context, *GetSurveyStatsByUserIdRequest) (*GetSurveyStatsByUserIdResponce, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSurveyStatsByUserId not implemented") +} +func (UnimplementedSurveyServiceServer) mustEmbedUnimplementedSurveyServiceServer() {} +func (UnimplementedSurveyServiceServer) testEmbeddedByValue() {} + +// UnsafeSurveyServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SurveyServiceServer will +// result in compilation errors. +type UnsafeSurveyServiceServer interface { + mustEmbedUnimplementedSurveyServiceServer() +} + +func RegisterSurveyServiceServer(s grpc.ServiceRegistrar, srv SurveyServiceServer) { + // If the following call pancis, it indicates UnimplementedSurveyServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&SurveyService_ServiceDesc, srv) +} + +func _SurveyService_GetSurveyById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSurveyByIdRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SurveyServiceServer).GetSurveyById(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SurveyService_GetSurveyById_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SurveyServiceServer).GetSurveyById(ctx, req.(*GetSurveyByIdRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SurveyService_CreateSurvey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateSurveyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SurveyServiceServer).CreateSurvey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SurveyService_CreateSurvey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SurveyServiceServer).CreateSurvey(ctx, req.(*CreateSurveyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SurveyService_GetSurveyStatsBySurveyId_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSurveyStatsBySurveyIdRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SurveyServiceServer).GetSurveyStatsBySurveyId(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SurveyService_GetSurveyStatsBySurveyId_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SurveyServiceServer).GetSurveyStatsBySurveyId(ctx, req.(*GetSurveyStatsBySurveyIdRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _SurveyService_GetSurveyStatsByUserId_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSurveyStatsByUserIdRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SurveyServiceServer).GetSurveyStatsByUserId(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: SurveyService_GetSurveyStatsByUserId_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SurveyServiceServer).GetSurveyStatsByUserId(ctx, req.(*GetSurveyStatsByUserIdRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// SurveyService_ServiceDesc is the grpc.ServiceDesc for SurveyService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var SurveyService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "survey.SurveyService", + HandlerType: (*SurveyServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetSurveyById", + Handler: _SurveyService_GetSurveyById_Handler, + }, + { + MethodName: "CreateSurvey", + Handler: _SurveyService_CreateSurvey_Handler, + }, + { + MethodName: "GetSurveyStatsBySurveyId", + Handler: _SurveyService_GetSurveyStatsBySurveyId_Handler, + }, + { + MethodName: "GetSurveyStatsByUserId", + Handler: _SurveyService_GetSurveyStatsByUserId_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/survey.proto", +} diff --git a/internal/pkg/survey/delivery/grpc/grpc_server.go b/internal/pkg/survey/delivery/grpc/grpc_server.go new file mode 100644 index 0000000..361027e --- /dev/null +++ b/internal/pkg/survey/delivery/grpc/grpc_server.go @@ -0,0 +1,81 @@ +package grpc + +import ( + "2024_2_ThereWillBeName/internal/models" + surveyPkg "2024_2_ThereWillBeName/internal/pkg/survey" + "2024_2_ThereWillBeName/internal/pkg/survey/delivery/grpc/gen" + "context" + "log/slog" +) + +//go:generate protoc -I . proto/survey.proto --go_out=./gen --go-grpc_out=./gen + +type GrpcSurveyHandler struct { + gen.UnimplementedSurveyServiceServer + uc surveyPkg.SurveysUsecase + logger *slog.Logger +} + +func NewGrpcSurveyHandler(uc surveyPkg.SurveysUsecase, logger *slog.Logger) *GrpcSurveyHandler { + return &GrpcSurveyHandler{uc: uc, logger: logger} +} + +func (s *GrpcSurveyHandler) GetSurveyById(ctx context.Context, req *gen.GetSurveyByIdRequest) (*gen.GetSurveyByIdResponce, error) { + survey, err := s.uc.GetSurveyById(ctx, uint(req.Id)) + if err != nil { + return nil, err + } + surveyResponce := &gen.Survey{ + Id: uint32(survey.Id), + SurveyText: survey.SurveyText, + MaxRating: uint32(survey.MaxRating), + } + return &gen.GetSurveyByIdResponce{Survey: surveyResponce}, nil +} + +func (s *GrpcSurveyHandler) CreateSurvey(ctx context.Context, req *gen.CreateSurveyRequest) (*gen.CreateSurveyResponce, error) { + survey := models.SurveyResponse{ + SurveyId: uint(req.ServeyResponce.SurveyId), + UserId: uint(req.ServeyResponce.UserId), + Rating: int(req.ServeyResponce.Rating), + } + err := s.uc.CreateSurveyResponse(ctx, survey) + if err != nil { + return nil, err + } + return &gen.CreateSurveyResponce{Success: true}, nil +} + +func (s *GrpcSurveyHandler) GetSurveyStatsBySurveyId(ctx context.Context, req *gen.GetSurveyStatsBySurveyIdRequest) (*gen.GetSurveyStatsBySurveyIdResponce, error) { + res, err := s.uc.GetSurveyStatsBySurveyId(ctx, uint(req.Id)) + if err != nil { + return nil, err + } + ratingsCount := make(map[int32]int32) + for k, v := range res.RatingsCount { + ratingsCount[int32(k)] = int32(v) + } + statsResponce := &gen.SurveyStatsBySurvey{ + ServeyId: uint32(res.SurveyId), + ServeyText: res.SurveyText, + AvgRating: float32(res.AvgRating), + RatingsCount: ratingsCount, + } + return &gen.GetSurveyStatsBySurveyIdResponce{SurveyStatsBySurvey: statsResponce}, nil +} + +func (s *GrpcSurveyHandler) GetSurveyStatsByUserId(ctx context.Context, req *gen.GetSurveyStatsByUserIdRequest) (*gen.GetSurveyStatsByUserIdResponce, error) { + res, err := s.uc.GetSurveyStatsByUserId(ctx, uint(req.UserId)) + if err != nil { + return nil, err + } + serveyStatsResponce := make([]*gen.UserSurveyStats, len(res)) + for k, v := range res { + serveyStatsResponce[k] = &gen.UserSurveyStats{ + ServeyId: uint32(v.SurveyId), + ServeyText: v.SurveyText, + Answered: v.Answered, + } + } + return &gen.GetSurveyStatsByUserIdResponce{UserServeyStats: serveyStatsResponce}, nil +} diff --git a/internal/pkg/survey/delivery/grpc/proto/survey.proto b/internal/pkg/survey/delivery/grpc/proto/survey.proto new file mode 100644 index 0000000..868eaff --- /dev/null +++ b/internal/pkg/survey/delivery/grpc/proto/survey.proto @@ -0,0 +1,68 @@ +syntax = "proto3"; + +package survey; +option go_package = ".;gen"; + +service SurveyService { + rpc GetSurveyById(GetSurveyByIdRequest) returns (GetSurveyByIdResponce) {} + rpc CreateSurvey(CreateSurveyRequest) returns (CreateSurveyResponce) {} + rpc GetSurveyStatsBySurveyId(GetSurveyStatsBySurveyIdRequest) returns (GetSurveyStatsBySurveyIdResponce) {} + rpc GetSurveyStatsByUserId(GetSurveyStatsByUserIdRequest) returns (GetSurveyStatsByUserIdResponce) {} +} + +message GetSurveyByIdRequest { + uint32 id = 1; +} + +message Survey { + uint32 id = 1; + string survey_text = 2; + uint32 max_rating = 3; +} + +message SurveyResponce { + uint32 survey_id = 1; + uint32 user_id = 2; + string description = 3; + uint32 rating = 4; +} +message GetSurveyByIdResponce { + Survey survey = 1; +} + +message CreateSurveyRequest { + SurveyResponce serveyResponce = 1; +} + +message CreateSurveyResponce { + bool success = 1; +} + +message SurveyStatsBySurvey { + uint32 servey_id = 1; + string servey_text = 2; + float avg_rating = 3; + map ratings_count = 4; +} + +message GetSurveyStatsBySurveyIdRequest { + uint32 id = 1; +} + +message GetSurveyStatsBySurveyIdResponce { + SurveyStatsBySurvey surveyStatsBySurvey = 1; +} + +message UserSurveyStats { + uint32 servey_id = 1; + string servey_text = 2; + bool answered = 3; +} + +message GetSurveyStatsByUserIdRequest { + uint32 userId = 1; +} + +message GetSurveyStatsByUserIdResponce { + repeated UserSurveyStats userServeyStats = 1; +} diff --git a/internal/pkg/survey/delivery/http/handler.go b/internal/pkg/survey/delivery/http/handler.go new file mode 100644 index 0000000..5937134 --- /dev/null +++ b/internal/pkg/survey/delivery/http/handler.go @@ -0,0 +1,213 @@ +package http + +import ( + "2024_2_ThereWillBeName/internal/models" + httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" + log "2024_2_ThereWillBeName/internal/pkg/logger" + "2024_2_ThereWillBeName/internal/pkg/middleware" + surveysGen "2024_2_ThereWillBeName/internal/pkg/survey/delivery/grpc/gen" + "context" + "encoding/json" + "errors" + "fmt" + "log/slog" + "net/http" + "strconv" + + "github.com/gorilla/mux" +) + +type SurveyHandler struct { + client surveysGen.SurveyServiceClient + logger *slog.Logger +} + +func NewSurveyHandler(client surveysGen.SurveyServiceClient, logger *slog.Logger) *SurveyHandler { + return &SurveyHandler{ + client: client, + logger: logger, + } +} + +func ErrorCheck(err error, action string, logger *slog.Logger, ctx context.Context) (httpresponse.ErrorResponse, int) { + if errors.Is(err, models.ErrNotFound) { + + logContext := log.AppendCtx(ctx, slog.String("action", action)) + logger.ErrorContext(logContext, fmt.Sprintf("Error during %s operation", action), slog.Any("error", err.Error())) + + response := httpresponse.ErrorResponse{ + Message: "Invalid request", + } + return response, http.StatusNotFound + } + logContext := log.AppendCtx(ctx, slog.String("action", action)) + logger.ErrorContext(logContext, fmt.Sprintf("Failed to %s survey", action), slog.Any("error", err.Error())) + response := httpresponse.ErrorResponse{ + Message: fmt.Sprintf("Failed to %s survey", action), + } + return response, http.StatusInternalServerError +} + +func (h *SurveyHandler) GetSurveyById(w http.ResponseWriter, r *http.Request) { + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request for getting a survey") + + _, ok := r.Context().Value(middleware.IdKey).(uint) + + if !ok { + response := httpresponse.ErrorResponse{ + Message: "User is not authorized", + } + httpresponse.SendJSONResponse(w, response, http.StatusUnauthorized, h.logger) + return + } + + vars := mux.Vars(r) + surveyIdStr := vars["id"] + + surveyId, err := strconv.ParseUint(surveyIdStr, 10, 64) + if err != nil { + h.logger.Warn("Failed to parse survey ID", slog.String("surveyID", surveyIdStr), slog.String("error", err.Error())) + response := httpresponse.ErrorResponse{ + Message: "Invalid survey ID", + } + httpresponse.SendJSONResponse(w, response, http.StatusBadRequest, h.logger) + return + } + + survey, err := h.client.GetSurveyById(r.Context(), &surveysGen.GetSurveyByIdRequest{Id: uint32(surveyId)}) + if err != nil { + logCtx := log.AppendCtx(r.Context(), slog.String("surveyId", surveyIdStr)) + response, status := ErrorCheck(err, "retrieve", h.logger, logCtx) + httpresponse.SendJSONResponse(w, response, status, h.logger) + return + } + + h.logger.DebugContext(logCtx, "Successfully got survey by ID") + + httpresponse.SendJSONResponse(w, survey, http.StatusOK, h.logger) +} + +func (h *SurveyHandler) CreateSurveyResponse(w http.ResponseWriter, r *http.Request) { + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request to create survey response") + + _, ok := r.Context().Value(middleware.IdKey).(uint) + if !ok { + response := httpresponse.ErrorResponse{ + Message: "User is not authorized", + } + httpresponse.SendJSONResponse(w, response, http.StatusUnauthorized, h.logger) + return + } + + var surveyResponse models.SurveyResponse + err := json.NewDecoder(r.Body).Decode(&surveyResponse) + if err != nil { + h.logger.Warn("Failed to decode survey response", slog.String("error", err.Error())) + response := httpresponse.ErrorResponse{ + Message: "Invalid request body", + } + httpresponse.SendJSONResponse(w, response, http.StatusBadRequest, h.logger) + return + } + + _, err = h.client.CreateSurvey(r.Context(), &surveysGen.CreateSurveyRequest{ServeyResponce: &surveysGen.SurveyResponce{ + SurveyId: uint32(surveyResponse.SurveyId), + UserId: uint32(surveyResponse.UserId), + Rating: uint32(surveyResponse.Rating), + }}) + if err != nil { + logCtx := log.AppendCtx(r.Context(), slog.String("surveyID", fmt.Sprint(surveyResponse.SurveyId))) + response, status := ErrorCheck(err, "submit", h.logger, logCtx) + httpresponse.SendJSONResponse(w, response, status, h.logger) + return + } + + h.logger.DebugContext(logCtx, "Successfully submitted survey response") + + httpresponse.SendJSONResponse(w, nil, http.StatusOK, h.logger) +} + +func (h *SurveyHandler) GetSurveyStatsBySurveyId(w http.ResponseWriter, r *http.Request) { + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request for survey statistics by survey ID") + + _, ok := r.Context().Value(middleware.IdKey).(uint) + if !ok { + response := httpresponse.ErrorResponse{ + Message: "User is not authorized", + } + httpresponse.SendJSONResponse(w, response, http.StatusUnauthorized, h.logger) + return + } + + vars := mux.Vars(r) + surveyIdStr := vars["id"] + + surveyId, err := strconv.ParseUint(surveyIdStr, 10, 64) + if err != nil { + h.logger.Warn("Failed to parse survey ID", slog.String("surveyID", surveyIdStr), slog.String("error", err.Error())) + response := httpresponse.ErrorResponse{ + Message: "Invalid survey ID", + } + httpresponse.SendJSONResponse(w, response, http.StatusBadRequest, h.logger) + return + } + + stats, err := h.client.GetSurveyStatsBySurveyId(r.Context(), &surveysGen.GetSurveyStatsBySurveyIdRequest{ + Id: uint32(surveyId), + }) + if err != nil { + logCtx := log.AppendCtx(r.Context(), slog.String("surveyId", surveyIdStr)) + response, status := ErrorCheck(err, "retrieve survey statistics", h.logger, logCtx) + httpresponse.SendJSONResponse(w, response, status, h.logger) + return + } + + h.logger.DebugContext(logCtx, "Successfully retrieved survey statistics by ID") + + httpresponse.SendJSONResponse(w, stats, http.StatusOK, h.logger) +} + +func (h *SurveyHandler) GetSurveyStatsByUserId(w http.ResponseWriter, r *http.Request) { + logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI) + h.logger.DebugContext(logCtx, "Handling request for survey statistics by survey ID") + + _, ok := r.Context().Value(middleware.IdKey).(uint) + if !ok { + response := httpresponse.ErrorResponse{ + Message: "User is not authorized", + } + httpresponse.SendJSONResponse(w, response, http.StatusUnauthorized, h.logger) + return + } + + vars := mux.Vars(r) + userIdStr := vars["id"] + + userId, err := strconv.ParseUint(userIdStr, 10, 64) + if err != nil { + h.logger.Warn("Failed to parse user ID", slog.String("userID", userIdStr), slog.String("error", err.Error())) + response := httpresponse.ErrorResponse{ + Message: "Invalid user ID", + } + httpresponse.SendJSONResponse(w, response, http.StatusBadRequest, h.logger) + return + } + + stats, err := h.client.GetSurveyStatsByUserId(r.Context(), &surveysGen.GetSurveyStatsByUserIdRequest{ + UserId: uint32(userId), + }) + if err != nil { + logCtx := log.AppendCtx(r.Context(), slog.String("userId", userIdStr)) + response, status := ErrorCheck(err, "retrieve survey statistics by user", h.logger, logCtx) + httpresponse.SendJSONResponse(w, response, status, h.logger) + return + } + + h.logger.DebugContext(logCtx, "Successfully retrieved survey statistics by user ID") + + httpresponse.SendJSONResponse(w, stats, http.StatusOK, h.logger) + +} diff --git a/internal/pkg/survey/interfaces.go b/internal/pkg/survey/interfaces.go new file mode 100644 index 0000000..0e2d766 --- /dev/null +++ b/internal/pkg/survey/interfaces.go @@ -0,0 +1,20 @@ +package survey + +import ( + "2024_2_ThereWillBeName/internal/models" + "context" +) + +type SurveysUsecase interface { + GetSurveyById(ctx context.Context, surveyId uint) (models.Survey, error) + CreateSurveyResponse(ctx context.Context, response models.SurveyResponse) error + GetSurveyStatsBySurveyId(ctx context.Context, surveyId uint) (models.SurveyStatsBySurvey, error) + GetSurveyStatsByUserId(ctx context.Context, userId uint) ([]models.UserSurveyStats, error) +} + +type SurveysRepo interface { + GetSurveyById(ctx context.Context, surveyId uint) (models.Survey, error) + CreateSurveyResponse(ctx context.Context, response models.SurveyResponse) error + GetSurveyStatsBySurveyId(ctx context.Context, surveyId uint) (models.SurveyStatsBySurvey, error) + GetSurveyStatsByUserId(ctx context.Context, userId uint) ([]models.UserSurveyStats, error) +} diff --git a/internal/pkg/survey/repo/survey_repository.go b/internal/pkg/survey/repo/survey_repository.go new file mode 100644 index 0000000..9e9ca0b --- /dev/null +++ b/internal/pkg/survey/repo/survey_repository.go @@ -0,0 +1,127 @@ +package repo + +import ( + "2024_2_ThereWillBeName/internal/models" + "context" + "database/sql" + _ "embed" + "fmt" + "log" +) + +type SurveyRepository struct { + db *sql.DB +} + +func NewPLaceRepository(db *sql.DB) *SurveyRepository { + return &SurveyRepository{db: db} +} + +func (r *SurveyRepository) GetSurveyById(ctx context.Context, surveyId uint) (models.Survey, error) { + query := `SELECT id, survey_text, max_rating FROM survey WHERE id=$1` + row := r.db.QueryRowContext(ctx, query, surveyId) + + var survey models.Survey + err := row.Scan(&survey.Id, &survey.SurveyText, &survey.MaxRating) + if err != nil { + return models.Survey{}, fmt.Errorf("could not retrieve survey: %w", err) + } + return survey, nil +} + +func (r *SurveyRepository) CreateSurveyResponse(ctx context.Context, response models.SurveyResponse) error { + query := `INSERT INTO user_survey (survey_id, user_id, rating) VALUES ($1, $2, $3)` + log.Println(response.SurveyId, response.UserId, response.Rating) + _, err := r.db.ExecContext(ctx, query, response.SurveyId, response.UserId, response.Rating) + if err != nil { + return fmt.Errorf("could not create survey: %w", err) + } + return nil +} + +func (r *SurveyRepository) GetSurveyStatsBySurveyId(ctx context.Context, surveyId uint) (models.SurveyStatsBySurvey, error) { + query := `SELECT + s.id AS survey_id, + s.survey_text, + us.rating, + COUNT(us.user_id) AS count_of_users + FROM + survey s + LEFT JOIN + user_survey us ON s.id = us.survey_id + WHERE + s.id = $1 + GROUP BY + s.id, s.survey_text, us.rating + ORDER BY + s.id, us.rating;` + + rows, err := r.db.QueryContext(ctx, query, surveyId) + if err != nil { + return models.SurveyStatsBySurvey{}, fmt.Errorf("failed to retrieve survey stats: %w", models.ErrInternal) + } + defer rows.Close() + + var surveyStats models.SurveyStatsBySurvey + surveyStats.RatingsCount = make(map[int]int) + for rows.Next() { + var rating int + var count int + err := rows.Scan(&surveyStats.SurveyId, &surveyStats.SurveyText, &rating, &count) + if err != nil { + return models.SurveyStatsBySurvey{}, fmt.Errorf("failed to scan survey stats row: %w", models.ErrInternal) + } + surveyStats.RatingsCount[rating] = count + } + + if len(surveyStats.RatingsCount) == 0 { + return models.SurveyStatsBySurvey{}, fmt.Errorf("no survey stats found: %w", models.ErrNotFound) + } + + // Вычисление среднего рейтинга + totalRatings := 0 + totalCount := 0 + for rating, count := range surveyStats.RatingsCount { + totalRatings += rating * count + totalCount += count + } + if totalCount > 0 { + surveyStats.AvgRating = float64(totalRatings) / float64(totalCount) + } + + return surveyStats, nil +} + +func (r *SurveyRepository) GetSurveyStatsByUserId(ctx context.Context, userId uint) ([]models.UserSurveyStats, error) { + query := `SELECT + s.id AS survey_id, + s.survey_text, + CASE + WHEN us.user_id IS NOT NULL THEN TRUE + ELSE FALSE + END AS participated +FROM + survey s +LEFT JOIN + user_survey us ON s.id = us.survey_id AND us.user_id = $1 +ORDER BY + s.id;` + rows, err := r.db.QueryContext(ctx, query, userId) + if err != nil { + return nil, fmt.Errorf("failed to retrieve user stats: %w", models.ErrInternal) + } + defer rows.Close() + var userStats []models.UserSurveyStats + for rows.Next() { + var userStat models.UserSurveyStats + if err := rows.Scan(&userStat.SurveyId, &userStat.SurveyText, &userStat.Answered); err != nil { + return nil, fmt.Errorf("failed to scan user stat row: %w", models.ErrInternal) + } + userStats = append(userStats, userStat) + } + if len(userStats) == 0 { + return nil, fmt.Errorf("no user stats found: %w", models.ErrNotFound) + } + + return userStats, nil +} diff --git a/internal/pkg/survey/usecase/survey_usecase.go b/internal/pkg/survey/usecase/survey_usecase.go new file mode 100644 index 0000000..0d1455c --- /dev/null +++ b/internal/pkg/survey/usecase/survey_usecase.go @@ -0,0 +1,84 @@ +package usecase + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/survey" + "context" + "errors" + "fmt" + "log" +) + +type SurveysUseCaseImpl struct { + surveyRepo survey.SurveysRepo +} + +func NewSurveysUsecase(repo survey.SurveysRepo) *SurveysUseCaseImpl { + return &SurveysUseCaseImpl{ + surveyRepo: repo, + } +} + +func (u *SurveysUseCaseImpl) GetSurveyById(ctx context.Context, surveyId uint) (models.Survey, error) { + survey, err := u.surveyRepo.GetSurveyById(ctx, surveyId) + if err != nil { + if errors.Is(err, models.ErrNotFound) { + return models.Survey{}, fmt.Errorf("invalid request: %w", models.ErrNotFound) + } + return models.Survey{}, fmt.Errorf("internal error: %w", models.ErrInternal) + } + return survey, nil +} + +func (u *SurveysUseCaseImpl) CreateSurveyResponse(ctx context.Context, response models.SurveyResponse) error { + survey, err := u.surveyRepo.GetSurveyById(ctx, response.SurveyId) + if err != nil { + if errors.Is(err, models.ErrNotFound) { + log.Println(err) + return fmt.Errorf("invalid request: %w", models.ErrNotFound) + } + log.Println(err) + return fmt.Errorf("internal error: %w", models.ErrInternal) + } + + if response.Rating > survey.MaxRating { + log.Println(err) + return fmt.Errorf("invalid rating: cannot be higher than the maximum rating of %d", survey.MaxRating) + } + + err = u.surveyRepo.CreateSurveyResponse(ctx, response) + if err != nil { + if errors.Is(err, models.ErrNotFound) { + log.Println(err) + return fmt.Errorf("invalid request: %w", models.ErrNotFound) + } + log.Println(err) + return fmt.Errorf("internal error: %w", models.ErrInternal) + } + return nil +} + +func (u *SurveysUseCaseImpl) GetSurveyStatsBySurveyId(ctx context.Context, surveyId uint) (models.SurveyStatsBySurvey, error) { + survey, err := u.surveyRepo.GetSurveyStatsBySurveyId(ctx, surveyId) + if err != nil { + if errors.Is(err, models.ErrNotFound) { + log.Println(err) + return models.SurveyStatsBySurvey{}, fmt.Errorf("invalid request: %w", models.ErrNotFound) + } + log.Println(err) + return models.SurveyStatsBySurvey{}, fmt.Errorf("internal error: %w", models.ErrInternal) + } + return survey, nil +} + +func (u *SurveysUseCaseImpl) GetSurveyStatsByUserId(ctx context.Context, userId uint) ([]models.UserSurveyStats, error) { + stats, err := u.surveyRepo.GetSurveyStatsByUserId(ctx, userId) + if err != nil { + log.Println(err) + if errors.Is(err, models.ErrNotFound) { + return nil, fmt.Errorf("invalid request: %w", models.ErrNotFound) + } + return nil, fmt.Errorf("internal error: %w", models.ErrInternal) + } + return stats, nil +} diff --git a/internal/pkg/trips/delivery/grpc/gen/trips.pb.go b/internal/pkg/trips/delivery/grpc/gen/trips.pb.go new file mode 100644 index 0000000..b683796 --- /dev/null +++ b/internal/pkg/trips/delivery/grpc/gen/trips.pb.go @@ -0,0 +1,713 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.2 +// protoc v3.12.4 +// source: trips.proto + +package gen + +import ( + timestamp "github.com/golang/protobuf/ptypes/timestamp" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type CreateTripRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Trip *Trip `protobuf:"bytes,1,opt,name=trip,proto3" json:"trip,omitempty"` +} + +func (x *CreateTripRequest) Reset() { + *x = CreateTripRequest{} + mi := &file_trips_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateTripRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateTripRequest) ProtoMessage() {} + +func (x *CreateTripRequest) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateTripRequest.ProtoReflect.Descriptor instead. +func (*CreateTripRequest) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{0} +} + +func (x *CreateTripRequest) GetTrip() *Trip { + if x != nil { + return x.Trip + } + return nil +} + +type UpdateTripRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Trip *Trip `protobuf:"bytes,1,opt,name=trip,proto3" json:"trip,omitempty"` +} + +func (x *UpdateTripRequest) Reset() { + *x = UpdateTripRequest{} + mi := &file_trips_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateTripRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateTripRequest) ProtoMessage() {} + +func (x *UpdateTripRequest) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateTripRequest.ProtoReflect.Descriptor instead. +func (*UpdateTripRequest) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{1} +} + +func (x *UpdateTripRequest) GetTrip() *Trip { + if x != nil { + return x.Trip + } + return nil +} + +type DeleteTripRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *DeleteTripRequest) Reset() { + *x = DeleteTripRequest{} + mi := &file_trips_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteTripRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteTripRequest) ProtoMessage() {} + +func (x *DeleteTripRequest) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteTripRequest.ProtoReflect.Descriptor instead. +func (*DeleteTripRequest) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{2} +} + +func (x *DeleteTripRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +type GetTripsByUserIDRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId uint32 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Limit int32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` + Offset int32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` +} + +func (x *GetTripsByUserIDRequest) Reset() { + *x = GetTripsByUserIDRequest{} + mi := &file_trips_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTripsByUserIDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTripsByUserIDRequest) ProtoMessage() {} + +func (x *GetTripsByUserIDRequest) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTripsByUserIDRequest.ProtoReflect.Descriptor instead. +func (*GetTripsByUserIDRequest) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{3} +} + +func (x *GetTripsByUserIDRequest) GetUserId() uint32 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *GetTripsByUserIDRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *GetTripsByUserIDRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +type GetTripsByUserIDResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Trips []*Trip `protobuf:"bytes,1,rep,name=trips,proto3" json:"trips,omitempty"` +} + +func (x *GetTripsByUserIDResponse) Reset() { + *x = GetTripsByUserIDResponse{} + mi := &file_trips_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTripsByUserIDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTripsByUserIDResponse) ProtoMessage() {} + +func (x *GetTripsByUserIDResponse) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTripsByUserIDResponse.ProtoReflect.Descriptor instead. +func (*GetTripsByUserIDResponse) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{4} +} + +func (x *GetTripsByUserIDResponse) GetTrips() []*Trip { + if x != nil { + return x.Trips + } + return nil +} + +type GetTripRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TripId uint32 `protobuf:"varint,1,opt,name=trip_id,json=tripId,proto3" json:"trip_id,omitempty"` +} + +func (x *GetTripRequest) Reset() { + *x = GetTripRequest{} + mi := &file_trips_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTripRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTripRequest) ProtoMessage() {} + +func (x *GetTripRequest) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTripRequest.ProtoReflect.Descriptor instead. +func (*GetTripRequest) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{5} +} + +func (x *GetTripRequest) GetTripId() uint32 { + if x != nil { + return x.TripId + } + return 0 +} + +type GetTripResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Trip *Trip `protobuf:"bytes,1,opt,name=trip,proto3" json:"trip,omitempty"` +} + +func (x *GetTripResponse) Reset() { + *x = GetTripResponse{} + mi := &file_trips_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTripResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTripResponse) ProtoMessage() {} + +func (x *GetTripResponse) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTripResponse.ProtoReflect.Descriptor instead. +func (*GetTripResponse) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{6} +} + +func (x *GetTripResponse) GetTrip() *Trip { + if x != nil { + return x.Trip + } + return nil +} + +type AddPlaceToTripRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TripId uint32 `protobuf:"varint,1,opt,name=trip_id,json=tripId,proto3" json:"trip_id,omitempty"` + PlaceId uint32 `protobuf:"varint,2,opt,name=place_id,json=placeId,proto3" json:"place_id,omitempty"` +} + +func (x *AddPlaceToTripRequest) Reset() { + *x = AddPlaceToTripRequest{} + mi := &file_trips_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AddPlaceToTripRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AddPlaceToTripRequest) ProtoMessage() {} + +func (x *AddPlaceToTripRequest) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AddPlaceToTripRequest.ProtoReflect.Descriptor instead. +func (*AddPlaceToTripRequest) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{7} +} + +func (x *AddPlaceToTripRequest) GetTripId() uint32 { + if x != nil { + return x.TripId + } + return 0 +} + +func (x *AddPlaceToTripRequest) GetPlaceId() uint32 { + if x != nil { + return x.PlaceId + } + return 0 +} + +type EmptyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *EmptyResponse) Reset() { + *x = EmptyResponse{} + mi := &file_trips_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EmptyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmptyResponse) ProtoMessage() {} + +func (x *EmptyResponse) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EmptyResponse.ProtoReflect.Descriptor instead. +func (*EmptyResponse) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{8} +} + +type Trip struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + UserId uint32 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + CityId uint32 `protobuf:"varint,5,opt,name=city_id,json=cityId,proto3" json:"city_id,omitempty"` + StartDate string `protobuf:"bytes,6,opt,name=start_date,json=startDate,proto3" json:"start_date,omitempty"` + EndDate string `protobuf:"bytes,7,opt,name=end_date,json=endDate,proto3" json:"end_date,omitempty"` + Private bool `protobuf:"varint,8,opt,name=private,proto3" json:"private,omitempty"` + CreatedAt *timestamp.Timestamp `protobuf:"bytes,9,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` +} + +func (x *Trip) Reset() { + *x = Trip{} + mi := &file_trips_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Trip) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Trip) ProtoMessage() {} + +func (x *Trip) ProtoReflect() protoreflect.Message { + mi := &file_trips_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Trip.ProtoReflect.Descriptor instead. +func (*Trip) Descriptor() ([]byte, []int) { + return file_trips_proto_rawDescGZIP(), []int{9} +} + +func (x *Trip) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Trip) GetUserId() uint32 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *Trip) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Trip) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Trip) GetCityId() uint32 { + if x != nil { + return x.CityId + } + return 0 +} + +func (x *Trip) GetStartDate() string { + if x != nil { + return x.StartDate + } + return "" +} + +func (x *Trip) GetEndDate() string { + if x != nil { + return x.EndDate + } + return "" +} + +func (x *Trip) GetPrivate() bool { + if x != nil { + return x.Private + } + return false +} + +func (x *Trip) GetCreatedAt() *timestamp.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +var File_trips_proto protoreflect.FileDescriptor + +var file_trips_proto_rawDesc = []byte{ + 0x0a, 0x0b, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, + 0x72, 0x69, 0x70, 0x73, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x34, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, + 0x72, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x04, 0x74, 0x72, + 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, + 0x2e, 0x54, 0x72, 0x69, 0x70, 0x52, 0x04, 0x74, 0x72, 0x69, 0x70, 0x22, 0x34, 0x0a, 0x11, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1f, 0x0a, 0x04, 0x74, 0x72, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, + 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, 0x54, 0x72, 0x69, 0x70, 0x52, 0x04, 0x74, 0x72, 0x69, + 0x70, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x69, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x60, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x54, 0x72, 0x69, + 0x70, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, + 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3d, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, + 0x72, 0x69, 0x70, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x05, 0x74, 0x72, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, 0x54, 0x72, 0x69, 0x70, + 0x52, 0x05, 0x74, 0x72, 0x69, 0x70, 0x73, 0x22, 0x29, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x54, 0x72, + 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x69, + 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x74, 0x72, 0x69, 0x70, + 0x49, 0x64, 0x22, 0x32, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x54, 0x72, 0x69, 0x70, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x04, 0x74, 0x72, 0x69, 0x70, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, 0x54, 0x72, 0x69, 0x70, + 0x52, 0x04, 0x74, 0x72, 0x69, 0x70, 0x22, 0x4b, 0x0a, 0x15, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, + 0x63, 0x65, 0x54, 0x6f, 0x54, 0x72, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x74, 0x72, 0x69, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x74, 0x72, 0x69, 0x70, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x70, 0x6c, 0x61, 0x63, + 0x65, 0x49, 0x64, 0x22, 0x0f, 0x0a, 0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x8d, 0x02, 0x0a, 0x04, 0x54, 0x72, 0x69, 0x70, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, + 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, + 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, + 0x63, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x63, + 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x44, 0x61, 0x74, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x41, 0x74, 0x32, 0x96, 0x03, 0x0a, 0x05, 0x54, 0x72, 0x69, 0x70, 0x73, 0x12, 0x3c, + 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x72, 0x69, 0x70, 0x12, 0x18, 0x2e, 0x74, + 0x72, 0x69, 0x70, 0x73, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x72, 0x69, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0a, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x69, 0x70, 0x12, 0x18, 0x2e, 0x74, 0x72, 0x69, + 0x70, 0x73, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x72, 0x69, 0x70, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0a, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x69, 0x70, 0x12, 0x18, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x54, 0x72, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, + 0x72, 0x69, 0x70, 0x73, 0x42, 0x79, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1e, 0x2e, 0x74, + 0x72, 0x69, 0x70, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x69, 0x70, 0x73, 0x42, 0x79, 0x55, + 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, + 0x72, 0x69, 0x70, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x69, 0x70, 0x73, 0x42, 0x79, 0x55, + 0x73, 0x65, 0x72, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, + 0x07, 0x47, 0x65, 0x74, 0x54, 0x72, 0x69, 0x70, 0x12, 0x15, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x72, 0x69, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x50, 0x6c, + 0x61, 0x63, 0x65, 0x54, 0x6f, 0x54, 0x72, 0x69, 0x70, 0x12, 0x1c, 0x2e, 0x74, 0x72, 0x69, 0x70, + 0x73, 0x2e, 0x41, 0x64, 0x64, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x54, 0x6f, 0x54, 0x72, 0x69, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x72, 0x69, 0x70, 0x73, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x2d, 0x5a, + 0x2b, 0x2e, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x74, 0x72, 0x69, 0x70, 0x73, 0x2f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x67, + 0x72, 0x70, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x3b, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_trips_proto_rawDescOnce sync.Once + file_trips_proto_rawDescData = file_trips_proto_rawDesc +) + +func file_trips_proto_rawDescGZIP() []byte { + file_trips_proto_rawDescOnce.Do(func() { + file_trips_proto_rawDescData = protoimpl.X.CompressGZIP(file_trips_proto_rawDescData) + }) + return file_trips_proto_rawDescData +} + +var file_trips_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_trips_proto_goTypes = []any{ + (*CreateTripRequest)(nil), // 0: trips.CreateTripRequest + (*UpdateTripRequest)(nil), // 1: trips.UpdateTripRequest + (*DeleteTripRequest)(nil), // 2: trips.DeleteTripRequest + (*GetTripsByUserIDRequest)(nil), // 3: trips.GetTripsByUserIDRequest + (*GetTripsByUserIDResponse)(nil), // 4: trips.GetTripsByUserIDResponse + (*GetTripRequest)(nil), // 5: trips.GetTripRequest + (*GetTripResponse)(nil), // 6: trips.GetTripResponse + (*AddPlaceToTripRequest)(nil), // 7: trips.AddPlaceToTripRequest + (*EmptyResponse)(nil), // 8: trips.EmptyResponse + (*Trip)(nil), // 9: trips.Trip + (*timestamp.Timestamp)(nil), // 10: google.protobuf.Timestamp +} +var file_trips_proto_depIdxs = []int32{ + 9, // 0: trips.CreateTripRequest.trip:type_name -> trips.Trip + 9, // 1: trips.UpdateTripRequest.trip:type_name -> trips.Trip + 9, // 2: trips.GetTripsByUserIDResponse.trips:type_name -> trips.Trip + 9, // 3: trips.GetTripResponse.trip:type_name -> trips.Trip + 10, // 4: trips.Trip.created_at:type_name -> google.protobuf.Timestamp + 0, // 5: trips.Trips.CreateTrip:input_type -> trips.CreateTripRequest + 1, // 6: trips.Trips.UpdateTrip:input_type -> trips.UpdateTripRequest + 2, // 7: trips.Trips.DeleteTrip:input_type -> trips.DeleteTripRequest + 3, // 8: trips.Trips.GetTripsByUserID:input_type -> trips.GetTripsByUserIDRequest + 5, // 9: trips.Trips.GetTrip:input_type -> trips.GetTripRequest + 7, // 10: trips.Trips.AddPlaceToTrip:input_type -> trips.AddPlaceToTripRequest + 8, // 11: trips.Trips.CreateTrip:output_type -> trips.EmptyResponse + 8, // 12: trips.Trips.UpdateTrip:output_type -> trips.EmptyResponse + 8, // 13: trips.Trips.DeleteTrip:output_type -> trips.EmptyResponse + 4, // 14: trips.Trips.GetTripsByUserID:output_type -> trips.GetTripsByUserIDResponse + 6, // 15: trips.Trips.GetTrip:output_type -> trips.GetTripResponse + 8, // 16: trips.Trips.AddPlaceToTrip:output_type -> trips.EmptyResponse + 11, // [11:17] is the sub-list for method output_type + 5, // [5:11] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_trips_proto_init() } +func file_trips_proto_init() { + if File_trips_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_trips_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_trips_proto_goTypes, + DependencyIndexes: file_trips_proto_depIdxs, + MessageInfos: file_trips_proto_msgTypes, + }.Build() + File_trips_proto = out.File + file_trips_proto_rawDesc = nil + file_trips_proto_goTypes = nil + file_trips_proto_depIdxs = nil +} diff --git a/internal/pkg/trips/delivery/grpc/gen/trips_grpc.pb.go b/internal/pkg/trips/delivery/grpc/gen/trips_grpc.pb.go new file mode 100644 index 0000000..05be95f --- /dev/null +++ b/internal/pkg/trips/delivery/grpc/gen/trips_grpc.pb.go @@ -0,0 +1,311 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.12.4 +// source: trips.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Trips_CreateTrip_FullMethodName = "/trips.Trips/CreateTrip" + Trips_UpdateTrip_FullMethodName = "/trips.Trips/UpdateTrip" + Trips_DeleteTrip_FullMethodName = "/trips.Trips/DeleteTrip" + Trips_GetTripsByUserID_FullMethodName = "/trips.Trips/GetTripsByUserID" + Trips_GetTrip_FullMethodName = "/trips.Trips/GetTrip" + Trips_AddPlaceToTrip_FullMethodName = "/trips.Trips/AddPlaceToTrip" +) + +// TripsClient is the client API for Trips service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type TripsClient interface { + CreateTrip(ctx context.Context, in *CreateTripRequest, opts ...grpc.CallOption) (*EmptyResponse, error) + UpdateTrip(ctx context.Context, in *UpdateTripRequest, opts ...grpc.CallOption) (*EmptyResponse, error) + DeleteTrip(ctx context.Context, in *DeleteTripRequest, opts ...grpc.CallOption) (*EmptyResponse, error) + GetTripsByUserID(ctx context.Context, in *GetTripsByUserIDRequest, opts ...grpc.CallOption) (*GetTripsByUserIDResponse, error) + GetTrip(ctx context.Context, in *GetTripRequest, opts ...grpc.CallOption) (*GetTripResponse, error) + AddPlaceToTrip(ctx context.Context, in *AddPlaceToTripRequest, opts ...grpc.CallOption) (*EmptyResponse, error) +} + +type tripsClient struct { + cc grpc.ClientConnInterface +} + +func NewTripsClient(cc grpc.ClientConnInterface) TripsClient { + return &tripsClient{cc} +} + +func (c *tripsClient) CreateTrip(ctx context.Context, in *CreateTripRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, Trips_CreateTrip_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *tripsClient) UpdateTrip(ctx context.Context, in *UpdateTripRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, Trips_UpdateTrip_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *tripsClient) DeleteTrip(ctx context.Context, in *DeleteTripRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, Trips_DeleteTrip_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *tripsClient) GetTripsByUserID(ctx context.Context, in *GetTripsByUserIDRequest, opts ...grpc.CallOption) (*GetTripsByUserIDResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetTripsByUserIDResponse) + err := c.cc.Invoke(ctx, Trips_GetTripsByUserID_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *tripsClient) GetTrip(ctx context.Context, in *GetTripRequest, opts ...grpc.CallOption) (*GetTripResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetTripResponse) + err := c.cc.Invoke(ctx, Trips_GetTrip_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *tripsClient) AddPlaceToTrip(ctx context.Context, in *AddPlaceToTripRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, Trips_AddPlaceToTrip_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TripsServer is the server API for Trips service. +// All implementations must embed UnimplementedTripsServer +// for forward compatibility. +type TripsServer interface { + CreateTrip(context.Context, *CreateTripRequest) (*EmptyResponse, error) + UpdateTrip(context.Context, *UpdateTripRequest) (*EmptyResponse, error) + DeleteTrip(context.Context, *DeleteTripRequest) (*EmptyResponse, error) + GetTripsByUserID(context.Context, *GetTripsByUserIDRequest) (*GetTripsByUserIDResponse, error) + GetTrip(context.Context, *GetTripRequest) (*GetTripResponse, error) + AddPlaceToTrip(context.Context, *AddPlaceToTripRequest) (*EmptyResponse, error) + mustEmbedUnimplementedTripsServer() +} + +// UnimplementedTripsServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTripsServer struct{} + +func (UnimplementedTripsServer) CreateTrip(context.Context, *CreateTripRequest) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateTrip not implemented") +} +func (UnimplementedTripsServer) UpdateTrip(context.Context, *UpdateTripRequest) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateTrip not implemented") +} +func (UnimplementedTripsServer) DeleteTrip(context.Context, *DeleteTripRequest) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteTrip not implemented") +} +func (UnimplementedTripsServer) GetTripsByUserID(context.Context, *GetTripsByUserIDRequest) (*GetTripsByUserIDResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTripsByUserID not implemented") +} +func (UnimplementedTripsServer) GetTrip(context.Context, *GetTripRequest) (*GetTripResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTrip not implemented") +} +func (UnimplementedTripsServer) AddPlaceToTrip(context.Context, *AddPlaceToTripRequest) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddPlaceToTrip not implemented") +} +func (UnimplementedTripsServer) mustEmbedUnimplementedTripsServer() {} +func (UnimplementedTripsServer) testEmbeddedByValue() {} + +// UnsafeTripsServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TripsServer will +// result in compilation errors. +type UnsafeTripsServer interface { + mustEmbedUnimplementedTripsServer() +} + +func RegisterTripsServer(s grpc.ServiceRegistrar, srv TripsServer) { + // If the following call pancis, it indicates UnimplementedTripsServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Trips_ServiceDesc, srv) +} + +func _Trips_CreateTrip_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateTripRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TripsServer).CreateTrip(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Trips_CreateTrip_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TripsServer).CreateTrip(ctx, req.(*CreateTripRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Trips_UpdateTrip_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateTripRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TripsServer).UpdateTrip(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Trips_UpdateTrip_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TripsServer).UpdateTrip(ctx, req.(*UpdateTripRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Trips_DeleteTrip_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteTripRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TripsServer).DeleteTrip(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Trips_DeleteTrip_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TripsServer).DeleteTrip(ctx, req.(*DeleteTripRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Trips_GetTripsByUserID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTripsByUserIDRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TripsServer).GetTripsByUserID(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Trips_GetTripsByUserID_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TripsServer).GetTripsByUserID(ctx, req.(*GetTripsByUserIDRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Trips_GetTrip_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTripRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TripsServer).GetTrip(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Trips_GetTrip_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TripsServer).GetTrip(ctx, req.(*GetTripRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Trips_AddPlaceToTrip_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddPlaceToTripRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TripsServer).AddPlaceToTrip(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Trips_AddPlaceToTrip_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TripsServer).AddPlaceToTrip(ctx, req.(*AddPlaceToTripRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Trips_ServiceDesc is the grpc.ServiceDesc for Trips service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Trips_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "trips.Trips", + HandlerType: (*TripsServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateTrip", + Handler: _Trips_CreateTrip_Handler, + }, + { + MethodName: "UpdateTrip", + Handler: _Trips_UpdateTrip_Handler, + }, + { + MethodName: "DeleteTrip", + Handler: _Trips_DeleteTrip_Handler, + }, + { + MethodName: "GetTripsByUserID", + Handler: _Trips_GetTripsByUserID_Handler, + }, + { + MethodName: "GetTrip", + Handler: _Trips_GetTrip_Handler, + }, + { + MethodName: "AddPlaceToTrip", + Handler: _Trips_AddPlaceToTrip_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "trips.proto", +} diff --git a/internal/pkg/trips/delivery/grpc/grpc.go b/internal/pkg/trips/delivery/grpc/grpc.go new file mode 100644 index 0000000..8de8171 --- /dev/null +++ b/internal/pkg/trips/delivery/grpc/grpc.go @@ -0,0 +1,123 @@ +package grpc + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/trips" + tripsGen "2024_2_ThereWillBeName/internal/pkg/trips/delivery/grpc/gen" + "context" + "log/slog" +) + +type GrpcTripsHandler struct { + tripsGen.TripsServer + uc trips.TripsUsecase + logger *slog.Logger +} + +func NewGrpcTripHandler(uc trips.TripsUsecase, logger *slog.Logger) *GrpcTripsHandler { + return &GrpcTripsHandler{uc: uc, logger: logger} +} + +func (h *GrpcTripsHandler) CreateTrip(ctx context.Context, in *tripsGen.CreateTripRequest) (*tripsGen.EmptyResponse, error) { + + trip := models.Trip{ + UserID: uint(in.Trip.UserId), + Name: in.Trip.Name, + Description: in.Trip.Description, + CityID: uint(in.Trip.CityId), + StartDate: in.Trip.StartDate, + EndDate: in.Trip.EndDate, + Private: in.Trip.Private, + } + + err := h.uc.CreateTrip(context.Background(), trip) + + if err != nil { + return nil, err + } + + return &tripsGen.EmptyResponse{}, nil +} + +func (h *GrpcTripsHandler) UpdateTrip(ctx context.Context, in *tripsGen.UpdateTripRequest) (*tripsGen.EmptyResponse, error) { + + trip := models.Trip{ + UserID: uint(in.Trip.UserId), + Name: in.Trip.Name, + Description: in.Trip.Description, + CityID: uint(in.Trip.CityId), + StartDate: in.Trip.StartDate, + EndDate: in.Trip.EndDate, + Private: in.Trip.Private, + } + + err := h.uc.UpdateTrip(context.Background(), trip) + + if err != nil { + return nil, err + } + + return &tripsGen.EmptyResponse{}, nil +} + +func (h *GrpcTripsHandler) DeleteTrip(ctx context.Context, in *tripsGen.DeleteTripRequest) (*tripsGen.EmptyResponse, error) { + err := h.uc.DeleteTrip(context.Background(), uint(in.Id)) + + if err != nil { + return nil, err + } + + return &tripsGen.EmptyResponse{}, nil +} + +func (h *GrpcTripsHandler) GetTripsByUserID(ctx context.Context, in *tripsGen.GetTripsByUserIDRequest) (*tripsGen.GetTripsByUserIDResponse, error) { + trips, err := h.uc.GetTripsByUserID(context.Background(), uint(in.UserId), int(in.Limit), int(in.Offset)) + if err != nil { + return nil, err + } + grpcTrips := make([]*tripsGen.Trip, 0, len(trips)) + for _, trip := range trips { + grpcTrips = append(grpcTrips, &tripsGen.Trip{ + Id: uint32(trip.ID), + UserId: uint32(trip.UserID), + Name: trip.Name, + Description: trip.Description, + CityId: uint32(trip.CityID), + StartDate: trip.StartDate, + EndDate: trip.EndDate, + Private: trip.Private, + }) + } + return &tripsGen.GetTripsByUserIDResponse{Trips: grpcTrips}, nil +} + +func (h *GrpcTripsHandler) GetTrip(ctx context.Context, in *tripsGen.GetTripRequest) (*tripsGen.GetTripResponse, error) { + trip, err := h.uc.GetTrip(context.Background(), uint(in.TripId)) + if err != nil { + return nil, err + } + return &tripsGen.GetTripResponse{ + Trip: &tripsGen.Trip{ + Id: uint32(trip.ID), + UserId: uint32(trip.UserID), + Name: trip.Name, + Description: trip.Description, + CityId: uint32(trip.CityID), + StartDate: trip.StartDate, + EndDate: trip.EndDate, + Private: trip.Private, + }, + }, nil +} + +func (h *GrpcTripsHandler) AddPlaceToTrip(ctx context.Context, in *tripsGen.AddPlaceToTripRequest) (*tripsGen.EmptyResponse, error) { + tripID := uint(in.TripId) + placeID := uint(in.PlaceId) + err := h.uc.AddPlaceToTrip(ctx, tripID, placeID) + if err != nil { + h.logger.Error("Failed to add place to trip", slog.Any("error", err)) + return nil, err + } + + return &tripsGen.EmptyResponse{}, nil +} diff --git a/internal/pkg/trips/delivery/http/handler.go b/internal/pkg/trips/delivery/http/handler.go index 25ff175..e784c87 100644 --- a/internal/pkg/trips/delivery/http/handler.go +++ b/internal/pkg/trips/delivery/http/handler.go @@ -5,7 +5,7 @@ import ( httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" log "2024_2_ThereWillBeName/internal/pkg/logger" "2024_2_ThereWillBeName/internal/pkg/middleware" - "2024_2_ThereWillBeName/internal/pkg/trips" + tripsGen "2024_2_ThereWillBeName/internal/pkg/trips/delivery/grpc/gen" "2024_2_ThereWillBeName/internal/validator" "context" @@ -35,12 +35,12 @@ type TripData struct { } type TripHandler struct { - uc trips.TripsUsecase + client tripsGen.TripsClient logger *slog.Logger } -func NewTripHandler(uc trips.TripsUsecase, logger *slog.Logger) *TripHandler { - return &TripHandler{uc, logger} +func NewTripHandler(client tripsGen.TripsClient, logger *slog.Logger) *TripHandler { + return &TripHandler{client, logger} } func ErrorCheck(err error, action string, logger *slog.Logger, ctx context.Context) (httpresponse.ErrorResponse, int) { @@ -125,7 +125,17 @@ func (h *TripHandler) CreateTripHandler(w http.ResponseWriter, r *http.Request) trip.Name = template.HTMLEscapeString(trip.Name) trip.Description = template.HTMLEscapeString(trip.Description) - err = h.uc.CreateTrip(context.Background(), trip) + // err = h.uc.CreateTrip(context.Background(), trip) + _, err = h.client.CreateTrip(r.Context(), &tripsGen.CreateTripRequest{Trip: &tripsGen.Trip{ + Id: uint32(trip.ID), + UserId: uint32(trip.UserID), + Name: trip.Name, + Description: trip.Description, + CityId: uint32(trip.CityID), + StartDate: trip.StartDate, + EndDate: trip.EndDate, + Private: trip.Private, + }}) if err != nil { response, status := ErrorCheck(err, "create", h.logger, context.Background()) httpresponse.SendJSONResponse(w, response, status, h.logger) @@ -211,7 +221,17 @@ func (h *TripHandler) UpdateTripHandler(w http.ResponseWriter, r *http.Request) trip.Name = template.HTMLEscapeString(trip.Name) trip.Description = template.HTMLEscapeString(trip.Description) - err = h.uc.UpdateTrip(context.Background(), trip) + // err = h.uc.UpdateTrip(context.Background(), trip) + _, err = h.client.UpdateTrip(r.Context(), &tripsGen.UpdateTripRequest{Trip: &tripsGen.Trip{ + Id: uint32(trip.ID), + UserId: uint32(trip.UserID), + Name: trip.Name, + Description: trip.Description, + CityId: uint32(trip.CityID), + StartDate: trip.StartDate, + EndDate: trip.EndDate, + Private: trip.Private, + }}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.Int("tripID", tripID)) response, status := ErrorCheck(err, "update", h.logger, logCtx) @@ -264,7 +284,8 @@ func (h *TripHandler) DeleteTripHandler(w http.ResponseWriter, r *http.Request) return } - err = h.uc.DeleteTrip(context.Background(), uint(id)) + // err = h.uc.DeleteTrip(context.Background(), uint(id)) + _, err = h.client.DeleteTrip(r.Context(), &tripsGen.DeleteTripRequest{Id: uint32(id)}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.String("tripID", idStr)) response, status := ErrorCheck(err, "delete", h.logger, logCtx) @@ -322,7 +343,12 @@ func (h *TripHandler) GetTripsByUserIDHandler(w http.ResponseWriter, r *http.Req } limit := 10 offset := limit * (page - 1) - trip, err := h.uc.GetTripsByUserID(context.Background(), uint(userID), limit, offset) + // trip, err := h.uc.GetTripsByUserID(context.Background(), uint(userID), limit, offset) + trip, err := h.client.GetTripsByUserID(r.Context(), &tripsGen.GetTripsByUserIDRequest{ + UserId: uint32(userID), + Limit: int32(limit), + Offset: int32(offset), + }) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.Int("userID", int(userID))) response, status := ErrorCheck(err, "retrieve", h.logger, logCtx) @@ -331,8 +357,9 @@ func (h *TripHandler) GetTripsByUserIDHandler(w http.ResponseWriter, r *http.Req } h.logger.DebugContext(logCtx, "Successfully got trips by user ID") + tripArr := trip.Trips - httpresponse.SendJSONResponse(w, trip, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, tripArr, http.StatusOK, h.logger) } // GetTripHandler godoc @@ -374,7 +401,8 @@ func (h *TripHandler) GetTripHandler(w http.ResponseWriter, r *http.Request) { return } - trip, err := h.uc.GetTrip(context.Background(), uint(tripID)) + // trip, err := h.uc.GetTrip(context.Background(), uint(tripID)) + trip, err := h.client.GetTrip(r.Context(), &tripsGen.GetTripRequest{TripId: uint32(tripID)}) if err != nil { logCtx := log.AppendCtx(context.Background(), slog.String("tripID", tripIDStr)) response, status := ErrorCheck(err, "retrieve", h.logger, logCtx) @@ -383,8 +411,9 @@ func (h *TripHandler) GetTripHandler(w http.ResponseWriter, r *http.Request) { } h.logger.DebugContext(logCtx, "Successfully got trip by ID") + tripResponse := trip.Trip - httpresponse.SendJSONResponse(w, trip, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, tripResponse, http.StatusOK, h.logger) } // AddPlaceToTripHandler godoc @@ -442,7 +471,11 @@ func (h *TripHandler) AddPlaceToTripHandler(w http.ResponseWriter, r *http.Reque return } - err = h.uc.AddPlaceToTrip(context.Background(), uint(tripID), req.PlaceID) + // err = h.uc.AddPlaceToTrip(context.Background(), uint(tripID), req.PlaceID) + _, err = h.client.AddPlaceToTrip(r.Context(), &tripsGen.AddPlaceToTripRequest{ + TripId: uint32(tripID), + PlaceId: uint32(req.PlaceID), + }) if err != nil { response, status := ErrorCheck(err, "add place", h.logger, context.Background()) httpresponse.SendJSONResponse(w, response, status, h.logger) diff --git a/internal/pkg/trips/delivery/http/handler_test.go b/internal/pkg/trips/delivery/http/handler_test.go index 0fd76c4..fab2487 100644 --- a/internal/pkg/trips/delivery/http/handler_test.go +++ b/internal/pkg/trips/delivery/http/handler_test.go @@ -1,380 +1,30 @@ package http -import ( - "bytes" - "encoding/json" - "errors" - "log/slog" - "net/http" - "net/http/httptest" - "os" - - "strconv" - "testing" - - "2024_2_ThereWillBeName/internal/models" - httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" - mocks "2024_2_ThereWillBeName/internal/pkg/trips/mocks" - - "github.com/gorilla/mux" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" -) - -func TestCreateTripHandler(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - opts := &slog.HandlerOptions{ - Level: slog.LevelDebug, - } - - handl := slog.NewJSONHandler(os.Stdout, opts) - - logger := slog.New(handl) - - mockUsecase := mocks.NewMockTripsUsecase(ctrl) - handler := NewTripHandler(mockUsecase, logger) - - tests := []struct { - name string - inputTrip models.Trip - usecaseErr error - expectedStatus int - expectedBody httpresponse.ErrorResponse - }{ - { - name: "successful creation", - inputTrip: models.Trip{ - - ID: 0, - UserID: 100, - Name: "Test Trip", - Description: "A trip for testing", - CityID: 1, - StartDate: "2024-12-01", - EndDate: "2024-12-15", - Private: false, - }, - usecaseErr: nil, - expectedStatus: http.StatusCreated, - }, - { - name: "invalid input data", - inputTrip: models.Trip{ - - ID: 0, - UserID: 101, - StartDate: "invalid-date", - EndDate: "2024-12-15", - }, - usecaseErr: errors.New("validation error"), - expectedStatus: http.StatusInternalServerError, - expectedBody: httpresponse.ErrorResponse{Message: "Failed to create trip"}, - }, - { - name: "internal server error", - inputTrip: models.Trip{ - - ID: 0, - UserID: 102, - Name: "Error Trip", - Description: "This trip causes an error", - CityID: 1, - StartDate: "2024-12-01", - EndDate: "2024-12-15", - Private: true, - }, - usecaseErr: models.ErrNotFound, - expectedStatus: http.StatusNotFound, - expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockUsecase.EXPECT().CreateTrip(gomock.Any(), tt.inputTrip).Return(tt.usecaseErr) - - reqBody, _ := json.Marshal(tt.inputTrip) - req := httptest.NewRequest("POST", "/trips", bytes.NewReader(reqBody)) - rec := httptest.NewRecorder() - - handler.CreateTripHandler(rec, req) - - assert.Equal(t, tt.expectedStatus, rec.Code) - - if tt.expectedStatus != http.StatusCreated { - var response httpresponse.ErrorResponse - _ = json.NewDecoder(rec.Body).Decode(&response) - assert.Equal(t, tt.expectedBody.Message, response.Message) - } - }) - } -} - -func TestUpdateTripHandler(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - opts := &slog.HandlerOptions{ - Level: slog.LevelDebug, - } - - handl := slog.NewJSONHandler(os.Stdout, opts) - - logger := slog.New(handl) - mockUsecase := mocks.NewMockTripsUsecase(ctrl) - handler := NewTripHandler(mockUsecase, logger) - - tests := []struct { - name string - inputTrip models.Trip - usecaseErr error - expectedStatus int - expectedBody httpresponse.ErrorResponse - }{ - { - name: "successful update", - inputTrip: models.Trip{ - ID: 1, - Name: "Updated Trip", - UserID: 100, - Description: "Updated description", - CityID: 1, - StartDate: "2024-12-01", - EndDate: "2024-12-15"}, - usecaseErr: nil, - expectedStatus: http.StatusOK, - }, - { - name: "invalid request", - inputTrip: models.Trip{ID: 10000}, - usecaseErr: models.ErrNotFound, - expectedStatus: http.StatusNotFound, - expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockUsecase.EXPECT().UpdateTrip(gomock.Any(), tt.inputTrip).Return(tt.usecaseErr) - - reqBody, _ := json.Marshal(tt.inputTrip) - req := httptest.NewRequest("PUT", "/trips/"+strconv.FormatUint(uint64(tt.inputTrip.ID), 10), bytes.NewReader(reqBody)) - rec := httptest.NewRecorder() - - r := mux.NewRouter() - r.HandleFunc("/trips/{id}", handler.UpdateTripHandler).Methods("PUT") - r.ServeHTTP(rec, req) - - assert.Equal(t, tt.expectedStatus, rec.Code) - if tt.expectedStatus != http.StatusOK { - var response httpresponse.ErrorResponse - _ = json.NewDecoder(rec.Body).Decode(&response) - assert.Equal(t, tt.expectedBody.Message, response.Message) - } - }) - } -} - -func TestDeleteTripHandler(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - opts := &slog.HandlerOptions{ - Level: slog.LevelDebug, - } - - handl := slog.NewJSONHandler(os.Stdout, opts) - - logger := slog.New(handl) - - mockUsecase := mocks.NewMockTripsUsecase(ctrl) - handler := NewTripHandler(mockUsecase, logger) - - tests := []struct { - name string - tripID uint - usecaseErr error - expectedStatus int - expectedBody httpresponse.ErrorResponse - }{ - { - name: "successful deletion", - tripID: 1, - usecaseErr: nil, - expectedStatus: http.StatusNoContent, - }, - { - name: "invalid request", - tripID: 10000, - usecaseErr: models.ErrNotFound, - expectedStatus: http.StatusNotFound, - expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockUsecase.EXPECT().DeleteTrip(gomock.Any(), tt.tripID).Return(tt.usecaseErr) - - req := httptest.NewRequest("DELETE", "/trips/"+strconv.FormatUint(uint64(tt.tripID), 10), nil) - rec := httptest.NewRecorder() - - r := mux.NewRouter() - r.HandleFunc("/trips/{id}", handler.DeleteTripHandler).Methods("DELETE") - r.ServeHTTP(rec, req) - - assert.Equal(t, tt.expectedStatus, rec.Code) - if tt.expectedStatus != http.StatusNoContent { - var response httpresponse.ErrorResponse - _ = json.NewDecoder(rec.Body).Decode(&response) - assert.Equal(t, tt.expectedBody.Message, response.Message) - } - }) - } -} - -func TestGetTripsByUserIDHandler(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - opts := &slog.HandlerOptions{ - Level: slog.LevelDebug, - } - - handl := slog.NewJSONHandler(os.Stdout, opts) - - logger := slog.New(handl) - mockUsecase := mocks.NewMockTripsUsecase(ctrl) - handler := NewTripHandler(mockUsecase, logger) - - tests := []struct { - name string - userID uint - expectedTrips []models.Trip - usecaseErr error - expectedStatus int - expectedBody httpresponse.ErrorResponse - }{ - { - name: "successful retrieval", - userID: 100, - expectedTrips: []models.Trip{{ - ID: 1, - UserID: 100, - Name: "Test Trip", - Description: "A trip for testing"}}, - usecaseErr: nil, - expectedStatus: http.StatusOK, - }, - { - name: "invalid request", - userID: 100, - expectedTrips: nil, - usecaseErr: models.ErrNotFound, - expectedStatus: http.StatusNotFound, - expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockUsecase.EXPECT().GetTripsByUserID(gomock.Any(), tt.userID, gomock.Any(), gomock.Any()).Return(tt.expectedTrips, tt.usecaseErr) - - req := httptest.NewRequest("GET", "/users/"+strconv.FormatUint(uint64(tt.userID), 10)+"/trips", nil) - rec := httptest.NewRecorder() - - r := mux.NewRouter() - r.HandleFunc("/users/{userID}/trips", handler.GetTripsByUserIDHandler).Methods("GET") - r.ServeHTTP(rec, req) - - assert.Equal(t, tt.expectedStatus, rec.Code) - if tt.expectedStatus == http.StatusOK { - var trips []models.Trip - _ = json.NewDecoder(rec.Body).Decode(&trips) - assert.Equal(t, tt.expectedTrips, trips) - } else { - var response httpresponse.ErrorResponse - _ = json.NewDecoder(rec.Body).Decode(&response) - assert.Equal(t, tt.expectedBody.Message, response.Message) - } - }) - } -} - -func TestGetTripHandler(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - opts := &slog.HandlerOptions{ - Level: slog.LevelDebug, - } - - handl := slog.NewJSONHandler(os.Stdout, opts) - - logger := slog.New(handl) - mockUsecase := mocks.NewMockTripsUsecase(ctrl) - handler := NewTripHandler(mockUsecase, logger) - - tests := []struct { - name string - tripID uint - expectedTrip models.Trip - usecaseErr error - expectedStatus int - expectedBody httpresponse.ErrorResponse - }{ - { - name: "successful retrieval", - tripID: 1, - expectedTrip: models.Trip{ - ID: 1, - UserID: 100, - Name: "Test Trip", - Description: "A trip for testing"}, - usecaseErr: nil, - expectedStatus: http.StatusOK, - }, - { - name: "invalid request", - tripID: 10000, - usecaseErr: models.ErrNotFound, - expectedStatus: http.StatusNotFound, - expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - mockUsecase.EXPECT().GetTrip(gomock.Any(), tt.tripID).Return(tt.expectedTrip, tt.usecaseErr) - - req := httptest.NewRequest("GET", "/trips/"+strconv.Itoa(int(tt.tripID)), nil) - rec := httptest.NewRecorder() - - r := mux.NewRouter() - r.HandleFunc("/trips/{id}", handler.GetTripHandler).Methods("GET") - r.ServeHTTP(rec, req) - - assert.Equal(t, tt.expectedStatus, rec.Code) - if tt.expectedStatus == http.StatusOK { - var trip models.Trip - _ = json.NewDecoder(rec.Body).Decode(&trip) - assert.Equal(t, tt.expectedTrip, trip) - } else { - var response httpresponse.ErrorResponse - _ = json.NewDecoder(rec.Body).Decode(&response) - assert.Equal(t, tt.expectedBody.Message, response.Message) - } - }) - } -} - -// func TestAddPlaceToTripHandler(t *testing.T) { +// import ( +// "bytes" +// "encoding/json" +// "errors" +// "log/slog" +// "net/http" +// "net/http/httptest" +// "os" + +// "strconv" +// "testing" + +// "2024_2_ThereWillBeName/internal/models" +// httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses" +// mocks "2024_2_ThereWillBeName/internal/pkg/trips/mocks" + +// "github.com/gorilla/mux" + +// "github.com/golang/mock/gomock" +// "github.com/stretchr/testify/assert" +// ) + +// func TestCreateTripHandler(t *testing.T) { // ctrl := gomock.NewController(t) // defer ctrl.Finish() -// opts := &slog.HandlerOptions{ -// Level: slog.LevelDebug, -// } // opts := &slog.HandlerOptions{ // Level: slog.LevelDebug, @@ -389,53 +39,403 @@ func TestGetTripHandler(t *testing.T) { // tests := []struct { // name string -// ID uint -// requestBody string +// inputTrip models.Trip // usecaseErr error // expectedStatus int // expectedBody httpresponse.ErrorResponse // }{ // { -// name: "successful addition of place", -// ID: 1, -// requestBody: `{"place_id": 2}`, +// name: "successful creation", +// inputTrip: models.Trip{ + +// ID: 0, +// UserID: 100, +// Name: "Test Trip", +// Description: "A trip for testing", +// CityID: 1, +// StartDate: "2024-12-01", +// EndDate: "2024-12-15", +// Private: false, +// }, // usecaseErr: nil, // expectedStatus: http.StatusCreated, // }, // { -// name: "invalid request body", -// ID: 2, -// requestBody: `{"place_id": "invalid"}`, -// usecaseErr: nil, -// expectedStatus: http.StatusBadRequest, -// expectedBody: httpresponse.ErrorResponse{Message: "Invalid place ID"}, +// name: "invalid input data", +// inputTrip: models.Trip{ + +// ID: 0, +// UserID: 101, +// StartDate: "invalid-date", +// EndDate: "2024-12-15", +// }, +// usecaseErr: errors.New("validation error"), +// expectedStatus: http.StatusInternalServerError, +// expectedBody: httpresponse.ErrorResponse{Message: "Failed to create trip"}, // }, // { -// name: "error from usecase", -// ID: 3, -// requestBody: `{"place_id": 2}`, -// usecaseErr: errors.New("usecase error"), -// expectedStatus: http.StatusBadRequest, -// expectedBody: httpresponse.ErrorResponse{Message: "Invalid trip ID"}, +// name: "internal server error", +// inputTrip: models.Trip{ + +// ID: 0, +// UserID: 102, +// Name: "Error Trip", +// Description: "This trip causes an error", +// CityID: 1, +// StartDate: "2024-12-01", +// EndDate: "2024-12-15", +// Private: true, +// }, +// usecaseErr: models.ErrNotFound, +// expectedStatus: http.StatusNotFound, +// expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, // }, // } // for _, tt := range tests { // t.Run(tt.name, func(t *testing.T) { -// mockUsecase.EXPECT().AddPlaceToTrip(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.usecaseErr) -// req := httptest.NewRequest("POST", "/trips/"+strconv.Itoa(int(tt.ID)), bytes.NewReader([]byte(tt.requestBody))) +// mockUsecase.EXPECT().CreateTrip(gomock.Any(), tt.inputTrip).Return(tt.usecaseErr) + +// reqBody, _ := json.Marshal(tt.inputTrip) +// req := httptest.NewRequest("POST", "/trips", bytes.NewReader(reqBody)) // rec := httptest.NewRecorder() -// handler.AddPlaceToTripHandler(rec, req) +// handler.CreateTripHandler(rec, req) // assert.Equal(t, tt.expectedStatus, rec.Code) // if tt.expectedStatus != http.StatusCreated { // var response httpresponse.ErrorResponse // _ = json.NewDecoder(rec.Body).Decode(&response) -// fmt.Println(response) // assert.Equal(t, tt.expectedBody.Message, response.Message) // } // }) // } // } + +// func TestUpdateTripHandler(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// opts := &slog.HandlerOptions{ +// Level: slog.LevelDebug, +// } + +// handl := slog.NewJSONHandler(os.Stdout, opts) + +// logger := slog.New(handl) +// mockUsecase := mocks.NewMockTripsUsecase(ctrl) +// handler := NewTripHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// inputTrip models.Trip +// usecaseErr error +// expectedStatus int +// expectedBody httpresponse.ErrorResponse +// }{ +// { +// name: "successful update", +// inputTrip: models.Trip{ +// ID: 1, +// Name: "Updated Trip", +// UserID: 100, +// Description: "Updated description", +// CityID: 1, +// StartDate: "2024-12-01", +// EndDate: "2024-12-15"}, +// usecaseErr: nil, +// expectedStatus: http.StatusOK, +// }, +// { +// name: "invalid request", +// inputTrip: models.Trip{ID: 10000}, +// usecaseErr: models.ErrNotFound, +// expectedStatus: http.StatusNotFound, +// expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, +// }, +// } + +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// mockUsecase.EXPECT().UpdateTrip(gomock.Any(), tt.inputTrip).Return(tt.usecaseErr) + +// reqBody, _ := json.Marshal(tt.inputTrip) +// req := httptest.NewRequest("PUT", "/trips/"+strconv.FormatUint(uint64(tt.inputTrip.ID), 10), bytes.NewReader(reqBody)) +// rec := httptest.NewRecorder() + +// r := mux.NewRouter() +// r.HandleFunc("/trips/{id}", handler.UpdateTripHandler).Methods("PUT") +// r.ServeHTTP(rec, req) + +// assert.Equal(t, tt.expectedStatus, rec.Code) +// if tt.expectedStatus != http.StatusOK { +// var response httpresponse.ErrorResponse +// _ = json.NewDecoder(rec.Body).Decode(&response) +// assert.Equal(t, tt.expectedBody.Message, response.Message) +// } +// }) +// } +// } + +// func TestDeleteTripHandler(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// opts := &slog.HandlerOptions{ +// Level: slog.LevelDebug, +// } + +// handl := slog.NewJSONHandler(os.Stdout, opts) + +// logger := slog.New(handl) + +// mockUsecase := mocks.NewMockTripsUsecase(ctrl) +// handler := NewTripHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// tripID uint +// usecaseErr error +// expectedStatus int +// expectedBody httpresponse.ErrorResponse +// }{ +// { +// name: "successful deletion", +// tripID: 1, +// usecaseErr: nil, +// expectedStatus: http.StatusNoContent, +// }, +// { +// name: "invalid request", +// tripID: 10000, +// usecaseErr: models.ErrNotFound, +// expectedStatus: http.StatusNotFound, +// expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, +// }, +// } + +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// mockUsecase.EXPECT().DeleteTrip(gomock.Any(), tt.tripID).Return(tt.usecaseErr) + +// req := httptest.NewRequest("DELETE", "/trips/"+strconv.FormatUint(uint64(tt.tripID), 10), nil) +// rec := httptest.NewRecorder() + +// r := mux.NewRouter() +// r.HandleFunc("/trips/{id}", handler.DeleteTripHandler).Methods("DELETE") +// r.ServeHTTP(rec, req) + +// assert.Equal(t, tt.expectedStatus, rec.Code) +// if tt.expectedStatus != http.StatusNoContent { +// var response httpresponse.ErrorResponse +// _ = json.NewDecoder(rec.Body).Decode(&response) +// assert.Equal(t, tt.expectedBody.Message, response.Message) +// } +// }) +// } +// } + +// func TestGetTripsByUserIDHandler(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() + +// opts := &slog.HandlerOptions{ +// Level: slog.LevelDebug, +// } + +// handl := slog.NewJSONHandler(os.Stdout, opts) + +// logger := slog.New(handl) +// mockUsecase := mocks.NewMockTripsUsecase(ctrl) +// handler := NewTripHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// userID uint +// expectedTrips []models.Trip +// usecaseErr error +// expectedStatus int +// expectedBody httpresponse.ErrorResponse +// }{ +// { +// name: "successful retrieval", +// userID: 100, +// expectedTrips: []models.Trip{{ +// ID: 1, +// UserID: 100, +// Name: "Test Trip", +// Description: "A trip for testing"}}, +// usecaseErr: nil, +// expectedStatus: http.StatusOK, +// }, +// { +// name: "invalid request", +// userID: 100, +// expectedTrips: nil, +// usecaseErr: models.ErrNotFound, +// expectedStatus: http.StatusNotFound, +// expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, +// }, +// } + +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// mockUsecase.EXPECT().GetTripsByUserID(gomock.Any(), tt.userID, gomock.Any(), gomock.Any()).Return(tt.expectedTrips, tt.usecaseErr) + +// req := httptest.NewRequest("GET", "/users/"+strconv.FormatUint(uint64(tt.userID), 10)+"/trips", nil) +// rec := httptest.NewRecorder() + +// r := mux.NewRouter() +// r.HandleFunc("/users/{userID}/trips", handler.GetTripsByUserIDHandler).Methods("GET") +// r.ServeHTTP(rec, req) + +// assert.Equal(t, tt.expectedStatus, rec.Code) +// if tt.expectedStatus == http.StatusOK { +// var trips []models.Trip +// _ = json.NewDecoder(rec.Body).Decode(&trips) +// assert.Equal(t, tt.expectedTrips, trips) +// } else { +// var response httpresponse.ErrorResponse +// _ = json.NewDecoder(rec.Body).Decode(&response) +// assert.Equal(t, tt.expectedBody.Message, response.Message) +// } +// }) +// } +// } + +// func TestGetTripHandler(t *testing.T) { +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// opts := &slog.HandlerOptions{ +// Level: slog.LevelDebug, +// } + +// handl := slog.NewJSONHandler(os.Stdout, opts) + +// logger := slog.New(handl) +// mockUsecase := mocks.NewMockTripsUsecase(ctrl) +// handler := NewTripHandler(mockUsecase, logger) + +// tests := []struct { +// name string +// tripID uint +// expectedTrip models.Trip +// usecaseErr error +// expectedStatus int +// expectedBody httpresponse.ErrorResponse +// }{ +// { +// name: "successful retrieval", +// tripID: 1, +// expectedTrip: models.Trip{ +// ID: 1, +// UserID: 100, +// Name: "Test Trip", +// Description: "A trip for testing"}, +// usecaseErr: nil, +// expectedStatus: http.StatusOK, +// }, +// { +// name: "invalid request", +// tripID: 10000, +// usecaseErr: models.ErrNotFound, +// expectedStatus: http.StatusNotFound, +// expectedBody: httpresponse.ErrorResponse{Message: "Invalid request"}, +// }, +// } + +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// mockUsecase.EXPECT().GetTrip(gomock.Any(), tt.tripID).Return(tt.expectedTrip, tt.usecaseErr) + +// req := httptest.NewRequest("GET", "/trips/"+strconv.Itoa(int(tt.tripID)), nil) +// rec := httptest.NewRecorder() + +// r := mux.NewRouter() +// r.HandleFunc("/trips/{id}", handler.GetTripHandler).Methods("GET") +// r.ServeHTTP(rec, req) + +// assert.Equal(t, tt.expectedStatus, rec.Code) +// if tt.expectedStatus == http.StatusOK { +// var trip models.Trip +// _ = json.NewDecoder(rec.Body).Decode(&trip) +// assert.Equal(t, tt.expectedTrip, trip) +// } else { +// var response httpresponse.ErrorResponse +// _ = json.NewDecoder(rec.Body).Decode(&response) +// assert.Equal(t, tt.expectedBody.Message, response.Message) +// } +// }) +// } +// } + +// // func TestAddPlaceToTripHandler(t *testing.T) { +// // ctrl := gomock.NewController(t) +// // defer ctrl.Finish() +// // opts := &slog.HandlerOptions{ +// // Level: slog.LevelDebug, +// // } + +// // opts := &slog.HandlerOptions{ +// // Level: slog.LevelDebug, +// // } + +// // handl := slog.NewJSONHandler(os.Stdout, opts) + +// // logger := slog.New(handl) + +// // mockUsecase := mocks.NewMockTripsUsecase(ctrl) +// // handler := NewTripHandler(mockUsecase, logger) + +// // tests := []struct { +// // name string +// // ID uint +// // requestBody string +// // usecaseErr error +// // expectedStatus int +// // expectedBody httpresponse.ErrorResponse +// // }{ +// // { +// // name: "successful addition of place", +// // ID: 1, +// // requestBody: `{"place_id": 2}`, +// // usecaseErr: nil, +// // expectedStatus: http.StatusCreated, +// // }, +// // { +// // name: "invalid request body", +// // ID: 2, +// // requestBody: `{"place_id": "invalid"}`, +// // usecaseErr: nil, +// // expectedStatus: http.StatusBadRequest, +// // expectedBody: httpresponse.ErrorResponse{Message: "Invalid place ID"}, +// // }, +// // { +// // name: "error from usecase", +// // ID: 3, +// // requestBody: `{"place_id": 2}`, +// // usecaseErr: errors.New("usecase error"), +// // expectedStatus: http.StatusBadRequest, +// // expectedBody: httpresponse.ErrorResponse{Message: "Invalid trip ID"}, +// // }, +// // } + +// // for _, tt := range tests { +// // t.Run(tt.name, func(t *testing.T) { +// // mockUsecase.EXPECT().AddPlaceToTrip(gomock.Any(), gomock.Any(), gomock.Any()).Return(tt.usecaseErr) +// // req := httptest.NewRequest("POST", "/trips/"+strconv.Itoa(int(tt.ID)), bytes.NewReader([]byte(tt.requestBody))) +// // rec := httptest.NewRecorder() + +// // handler.AddPlaceToTripHandler(rec, req) + +// // assert.Equal(t, tt.expectedStatus, rec.Code) + +// // if tt.expectedStatus != http.StatusCreated { +// // var response httpresponse.ErrorResponse +// // _ = json.NewDecoder(rec.Body).Decode(&response) +// // fmt.Println(response) +// // assert.Equal(t, tt.expectedBody.Message, response.Message) +// // } +// // }) +// // } +// // } diff --git a/internal/pkg/user/delivery/grpc/gen/user.pb.go b/internal/pkg/user/delivery/grpc/gen/user.pb.go new file mode 100644 index 0000000..72de36e --- /dev/null +++ b/internal/pkg/user/delivery/grpc/gen/user.pb.go @@ -0,0 +1,804 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.35.2 +// protoc v3.12.4 +// source: user.proto + +package gen + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type SignUpRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Login string `protobuf:"bytes,1,opt,name=login,proto3" json:"login,omitempty"` + Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *SignUpRequest) Reset() { + *x = SignUpRequest{} + mi := &file_user_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SignUpRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignUpRequest) ProtoMessage() {} + +func (x *SignUpRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignUpRequest.ProtoReflect.Descriptor instead. +func (*SignUpRequest) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{0} +} + +func (x *SignUpRequest) GetLogin() string { + if x != nil { + return x.Login + } + return "" +} + +func (x *SignUpRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *SignUpRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type SignUpResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *SignUpResponse) Reset() { + *x = SignUpResponse{} + mi := &file_user_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SignUpResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignUpResponse) ProtoMessage() {} + +func (x *SignUpResponse) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignUpResponse.ProtoReflect.Descriptor instead. +func (*SignUpResponse) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{1} +} + +func (x *SignUpResponse) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +type LoginRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` +} + +func (x *LoginRequest) Reset() { + *x = LoginRequest{} + mi := &file_user_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LoginRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginRequest) ProtoMessage() {} + +func (x *LoginRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead. +func (*LoginRequest) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{2} +} + +func (x *LoginRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *LoginRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type LoginResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Login string `protobuf:"bytes,2,opt,name=login,proto3" json:"login,omitempty"` + Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` + AvatarPath string `protobuf:"bytes,4,opt,name=avatar_path,json=avatarPath,proto3" json:"avatar_path,omitempty"` +} + +func (x *LoginResponse) Reset() { + *x = LoginResponse{} + mi := &file_user_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LoginResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginResponse) ProtoMessage() {} + +func (x *LoginResponse) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead. +func (*LoginResponse) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{3} +} + +func (x *LoginResponse) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *LoginResponse) GetLogin() string { + if x != nil { + return x.Login + } + return "" +} + +func (x *LoginResponse) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *LoginResponse) GetAvatarPath() string { + if x != nil { + return x.AvatarPath + } + return "" +} + +type UploadAvatarRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + AvatarData []byte `protobuf:"bytes,2,opt,name=avatar_data,json=avatarData,proto3" json:"avatar_data,omitempty"` + AvatarFileName string `protobuf:"bytes,3,opt,name=avatar_file_name,json=avatarFileName,proto3" json:"avatar_file_name,omitempty"` +} + +func (x *UploadAvatarRequest) Reset() { + *x = UploadAvatarRequest{} + mi := &file_user_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UploadAvatarRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadAvatarRequest) ProtoMessage() {} + +func (x *UploadAvatarRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadAvatarRequest.ProtoReflect.Descriptor instead. +func (*UploadAvatarRequest) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{4} +} + +func (x *UploadAvatarRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *UploadAvatarRequest) GetAvatarData() []byte { + if x != nil { + return x.AvatarData + } + return nil +} + +func (x *UploadAvatarRequest) GetAvatarFileName() string { + if x != nil { + return x.AvatarFileName + } + return "" +} + +type UploadAvatarResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AvatarPath string `protobuf:"bytes,2,opt,name=avatar_path,json=avatarPath,proto3" json:"avatar_path,omitempty"` +} + +func (x *UploadAvatarResponse) Reset() { + *x = UploadAvatarResponse{} + mi := &file_user_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UploadAvatarResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadAvatarResponse) ProtoMessage() {} + +func (x *UploadAvatarResponse) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadAvatarResponse.ProtoReflect.Descriptor instead. +func (*UploadAvatarResponse) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{5} +} + +func (x *UploadAvatarResponse) GetAvatarPath() string { + if x != nil { + return x.AvatarPath + } + return "" +} + +type GetProfileRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + RequesterId uint32 `protobuf:"varint,2,opt,name=requester_id,json=requesterId,proto3" json:"requester_id,omitempty"` +} + +func (x *GetProfileRequest) Reset() { + *x = GetProfileRequest{} + mi := &file_user_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetProfileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProfileRequest) ProtoMessage() {} + +func (x *GetProfileRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProfileRequest.ProtoReflect.Descriptor instead. +func (*GetProfileRequest) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{6} +} + +func (x *GetProfileRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *GetProfileRequest) GetRequesterId() uint32 { + if x != nil { + return x.RequesterId + } + return 0 +} + +type GetProfileResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Login string `protobuf:"bytes,1,opt,name=login,proto3" json:"login,omitempty"` + Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"` + AvatarPath string `protobuf:"bytes,3,opt,name=avatar_path,json=avatarPath,proto3" json:"avatar_path,omitempty"` +} + +func (x *GetProfileResponse) Reset() { + *x = GetProfileResponse{} + mi := &file_user_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetProfileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetProfileResponse) ProtoMessage() {} + +func (x *GetProfileResponse) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetProfileResponse.ProtoReflect.Descriptor instead. +func (*GetProfileResponse) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{7} +} + +func (x *GetProfileResponse) GetLogin() string { + if x != nil { + return x.Login + } + return "" +} + +func (x *GetProfileResponse) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *GetProfileResponse) GetAvatarPath() string { + if x != nil { + return x.AvatarPath + } + return "" +} + +type UpdatePasswordRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Login string `protobuf:"bytes,2,opt,name=login,proto3" json:"login,omitempty"` + Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` + OldPassword string `protobuf:"bytes,4,opt,name=old_password,json=oldPassword,proto3" json:"old_password,omitempty"` + NewPassword string `protobuf:"bytes,5,opt,name=new_password,json=newPassword,proto3" json:"new_password,omitempty"` +} + +func (x *UpdatePasswordRequest) Reset() { + *x = UpdatePasswordRequest{} + mi := &file_user_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdatePasswordRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdatePasswordRequest) ProtoMessage() {} + +func (x *UpdatePasswordRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdatePasswordRequest.ProtoReflect.Descriptor instead. +func (*UpdatePasswordRequest) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{8} +} + +func (x *UpdatePasswordRequest) GetId() uint32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *UpdatePasswordRequest) GetLogin() string { + if x != nil { + return x.Login + } + return "" +} + +func (x *UpdatePasswordRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *UpdatePasswordRequest) GetOldPassword() string { + if x != nil { + return x.OldPassword + } + return "" +} + +func (x *UpdatePasswordRequest) GetNewPassword() string { + if x != nil { + return x.NewPassword + } + return "" +} + +type UpdateProfileRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId uint32 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"` +} + +func (x *UpdateProfileRequest) Reset() { + *x = UpdateProfileRequest{} + mi := &file_user_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateProfileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateProfileRequest) ProtoMessage() {} + +func (x *UpdateProfileRequest) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateProfileRequest.ProtoReflect.Descriptor instead. +func (*UpdateProfileRequest) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{9} +} + +func (x *UpdateProfileRequest) GetUserId() uint32 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *UpdateProfileRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *UpdateProfileRequest) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +type EmptyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *EmptyResponse) Reset() { + *x = EmptyResponse{} + mi := &file_user_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EmptyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmptyResponse) ProtoMessage() {} + +func (x *EmptyResponse) ProtoReflect() protoreflect.Message { + mi := &file_user_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EmptyResponse.ProtoReflect.Descriptor instead. +func (*EmptyResponse) Descriptor() ([]byte, []int) { + return file_user_proto_rawDescGZIP(), []int{10} +} + +var File_user_proto protoreflect.FileDescriptor + +var file_user_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x75, 0x73, + 0x65, 0x72, 0x22, 0x57, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, + 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x20, 0x0a, 0x0e, 0x53, + 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x40, 0x0a, + 0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, + 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, + 0x6c, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, + 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x50, 0x61, 0x74, 0x68, 0x22, 0x70, 0x0a, + 0x13, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x76, 0x61, 0x74, 0x61, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x61, 0x76, 0x61, 0x74, 0x61, + 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x28, 0x0a, 0x10, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f, + 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x22, + 0x37, 0x0a, 0x14, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x76, 0x61, 0x74, 0x61, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x76, 0x61, 0x74, 0x61, + 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x76, + 0x61, 0x74, 0x61, 0x72, 0x50, 0x61, 0x74, 0x68, 0x22, 0x46, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, + 0x0c, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, + 0x22, 0x61, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, + 0x69, 0x6c, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x50, + 0x61, 0x74, 0x68, 0x22, 0x99, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, + 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6c, 0x6f, + 0x67, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x6c, 0x64, + 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x6f, 0x6c, 0x64, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x0a, 0x0c, + 0x6e, 0x65, 0x77, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, + 0x61, 0x0a, 0x14, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, + 0x69, 0x6c, 0x22, 0x0f, 0x0a, 0x0d, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x32, 0x8e, 0x03, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x12, 0x13, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x55, 0x70, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x32, 0x0a, 0x05, 0x4c, 0x6f, + 0x67, 0x69, 0x6e, 0x12, 0x12, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x4c, + 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, + 0x0a, 0x0c, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x19, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x76, 0x61, 0x74, + 0x61, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x76, 0x61, 0x74, 0x61, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x17, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x0e, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1b, 0x2e, 0x75, + 0x73, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x42, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x12, 0x1a, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x00, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x3b, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_user_proto_rawDescOnce sync.Once + file_user_proto_rawDescData = file_user_proto_rawDesc +) + +func file_user_proto_rawDescGZIP() []byte { + file_user_proto_rawDescOnce.Do(func() { + file_user_proto_rawDescData = protoimpl.X.CompressGZIP(file_user_proto_rawDescData) + }) + return file_user_proto_rawDescData +} + +var file_user_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_user_proto_goTypes = []any{ + (*SignUpRequest)(nil), // 0: user.SignUpRequest + (*SignUpResponse)(nil), // 1: user.SignUpResponse + (*LoginRequest)(nil), // 2: user.LoginRequest + (*LoginResponse)(nil), // 3: user.LoginResponse + (*UploadAvatarRequest)(nil), // 4: user.UploadAvatarRequest + (*UploadAvatarResponse)(nil), // 5: user.UploadAvatarResponse + (*GetProfileRequest)(nil), // 6: user.GetProfileRequest + (*GetProfileResponse)(nil), // 7: user.GetProfileResponse + (*UpdatePasswordRequest)(nil), // 8: user.UpdatePasswordRequest + (*UpdateProfileRequest)(nil), // 9: user.UpdateProfileRequest + (*EmptyResponse)(nil), // 10: user.EmptyResponse +} +var file_user_proto_depIdxs = []int32{ + 0, // 0: user.UserService.SignUp:input_type -> user.SignUpRequest + 2, // 1: user.UserService.Login:input_type -> user.LoginRequest + 4, // 2: user.UserService.UploadAvatar:input_type -> user.UploadAvatarRequest + 6, // 3: user.UserService.GetProfile:input_type -> user.GetProfileRequest + 8, // 4: user.UserService.UpdatePassword:input_type -> user.UpdatePasswordRequest + 9, // 5: user.UserService.UpdateProfile:input_type -> user.UpdateProfileRequest + 1, // 6: user.UserService.SignUp:output_type -> user.SignUpResponse + 3, // 7: user.UserService.Login:output_type -> user.LoginResponse + 5, // 8: user.UserService.UploadAvatar:output_type -> user.UploadAvatarResponse + 7, // 9: user.UserService.GetProfile:output_type -> user.GetProfileResponse + 10, // 10: user.UserService.UpdatePassword:output_type -> user.EmptyResponse + 10, // 11: user.UserService.UpdateProfile:output_type -> user.EmptyResponse + 6, // [6:12] is the sub-list for method output_type + 0, // [0:6] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_user_proto_init() } +func file_user_proto_init() { + if File_user_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_user_proto_rawDesc, + NumEnums: 0, + NumMessages: 11, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_user_proto_goTypes, + DependencyIndexes: file_user_proto_depIdxs, + MessageInfos: file_user_proto_msgTypes, + }.Build() + File_user_proto = out.File + file_user_proto_rawDesc = nil + file_user_proto_goTypes = nil + file_user_proto_depIdxs = nil +} diff --git a/internal/pkg/user/delivery/grpc/gen/user_grpc.pb.go b/internal/pkg/user/delivery/grpc/gen/user_grpc.pb.go new file mode 100644 index 0000000..d1d7c83 --- /dev/null +++ b/internal/pkg/user/delivery/grpc/gen/user_grpc.pb.go @@ -0,0 +1,311 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v3.12.4 +// source: user.proto + +package gen + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + UserService_SignUp_FullMethodName = "/user.UserService/SignUp" + UserService_Login_FullMethodName = "/user.UserService/Login" + UserService_UploadAvatar_FullMethodName = "/user.UserService/UploadAvatar" + UserService_GetProfile_FullMethodName = "/user.UserService/GetProfile" + UserService_UpdatePassword_FullMethodName = "/user.UserService/UpdatePassword" + UserService_UpdateProfile_FullMethodName = "/user.UserService/UpdateProfile" +) + +// UserServiceClient is the client API for UserService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type UserServiceClient interface { + SignUp(ctx context.Context, in *SignUpRequest, opts ...grpc.CallOption) (*SignUpResponse, error) + Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) + UploadAvatar(ctx context.Context, in *UploadAvatarRequest, opts ...grpc.CallOption) (*UploadAvatarResponse, error) + GetProfile(ctx context.Context, in *GetProfileRequest, opts ...grpc.CallOption) (*GetProfileResponse, error) + UpdatePassword(ctx context.Context, in *UpdatePasswordRequest, opts ...grpc.CallOption) (*EmptyResponse, error) + UpdateProfile(ctx context.Context, in *UpdateProfileRequest, opts ...grpc.CallOption) (*EmptyResponse, error) +} + +type userServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient { + return &userServiceClient{cc} +} + +func (c *userServiceClient) SignUp(ctx context.Context, in *SignUpRequest, opts ...grpc.CallOption) (*SignUpResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SignUpResponse) + err := c.cc.Invoke(ctx, UserService_SignUp_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(LoginResponse) + err := c.cc.Invoke(ctx, UserService_Login_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) UploadAvatar(ctx context.Context, in *UploadAvatarRequest, opts ...grpc.CallOption) (*UploadAvatarResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UploadAvatarResponse) + err := c.cc.Invoke(ctx, UserService_UploadAvatar_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) GetProfile(ctx context.Context, in *GetProfileRequest, opts ...grpc.CallOption) (*GetProfileResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetProfileResponse) + err := c.cc.Invoke(ctx, UserService_GetProfile_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) UpdatePassword(ctx context.Context, in *UpdatePasswordRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, UserService_UpdatePassword_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *userServiceClient) UpdateProfile(ctx context.Context, in *UpdateProfileRequest, opts ...grpc.CallOption) (*EmptyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, UserService_UpdateProfile_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UserServiceServer is the server API for UserService service. +// All implementations must embed UnimplementedUserServiceServer +// for forward compatibility. +type UserServiceServer interface { + SignUp(context.Context, *SignUpRequest) (*SignUpResponse, error) + Login(context.Context, *LoginRequest) (*LoginResponse, error) + UploadAvatar(context.Context, *UploadAvatarRequest) (*UploadAvatarResponse, error) + GetProfile(context.Context, *GetProfileRequest) (*GetProfileResponse, error) + UpdatePassword(context.Context, *UpdatePasswordRequest) (*EmptyResponse, error) + UpdateProfile(context.Context, *UpdateProfileRequest) (*EmptyResponse, error) + mustEmbedUnimplementedUserServiceServer() +} + +// UnimplementedUserServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedUserServiceServer struct{} + +func (UnimplementedUserServiceServer) SignUp(context.Context, *SignUpRequest) (*SignUpResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignUp not implemented") +} +func (UnimplementedUserServiceServer) Login(context.Context, *LoginRequest) (*LoginResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Login not implemented") +} +func (UnimplementedUserServiceServer) UploadAvatar(context.Context, *UploadAvatarRequest) (*UploadAvatarResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UploadAvatar not implemented") +} +func (UnimplementedUserServiceServer) GetProfile(context.Context, *GetProfileRequest) (*GetProfileResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetProfile not implemented") +} +func (UnimplementedUserServiceServer) UpdatePassword(context.Context, *UpdatePasswordRequest) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdatePassword not implemented") +} +func (UnimplementedUserServiceServer) UpdateProfile(context.Context, *UpdateProfileRequest) (*EmptyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateProfile not implemented") +} +func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {} +func (UnimplementedUserServiceServer) testEmbeddedByValue() {} + +// UnsafeUserServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to UserServiceServer will +// result in compilation errors. +type UnsafeUserServiceServer interface { + mustEmbedUnimplementedUserServiceServer() +} + +func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) { + // If the following call pancis, it indicates UnimplementedUserServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&UserService_ServiceDesc, srv) +} + +func _UserService_SignUp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignUpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).SignUp(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_SignUp_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).SignUp(ctx, req.(*SignUpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoginRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).Login(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_Login_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).Login(ctx, req.(*LoginRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_UploadAvatar_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UploadAvatarRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).UploadAvatar(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_UploadAvatar_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).UploadAvatar(ctx, req.(*UploadAvatarRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_GetProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetProfileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).GetProfile(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_GetProfile_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).GetProfile(ctx, req.(*GetProfileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_UpdatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdatePasswordRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).UpdatePassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_UpdatePassword_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).UpdatePassword(ctx, req.(*UpdatePasswordRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _UserService_UpdateProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateProfileRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserServiceServer).UpdateProfile(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: UserService_UpdateProfile_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserServiceServer).UpdateProfile(ctx, req.(*UpdateProfileRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var UserService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "user.UserService", + HandlerType: (*UserServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SignUp", + Handler: _UserService_SignUp_Handler, + }, + { + MethodName: "Login", + Handler: _UserService_Login_Handler, + }, + { + MethodName: "UploadAvatar", + Handler: _UserService_UploadAvatar_Handler, + }, + { + MethodName: "GetProfile", + Handler: _UserService_GetProfile_Handler, + }, + { + MethodName: "UpdatePassword", + Handler: _UserService_UpdatePassword_Handler, + }, + { + MethodName: "UpdateProfile", + Handler: _UserService_UpdateProfile_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "user.proto", +} diff --git a/internal/pkg/user/delivery/grpc/grpc_server.go b/internal/pkg/user/delivery/grpc/grpc_server.go new file mode 100644 index 0000000..a9c0110 --- /dev/null +++ b/internal/pkg/user/delivery/grpc/grpc_server.go @@ -0,0 +1,102 @@ +package grpc + +import ( + "2024_2_ThereWillBeName/internal/models" + "2024_2_ThereWillBeName/internal/pkg/user" + "2024_2_ThereWillBeName/internal/pkg/user/delivery/grpc/gen" + "context" + "log/slog" +) + +type GrpcUserHandler struct { + gen.UserServiceServer + usecase user.UserUsecase + logger *slog.Logger +} + +func NewGrpcUserHandler(usecase user.UserUsecase, logger *slog.Logger) *GrpcUserHandler { + return &GrpcUserHandler{usecase: usecase, logger: logger} +} + +func (h *GrpcUserHandler) SignUp(ctx context.Context, in *gen.SignUpRequest) (*gen.SignUpResponse, error) { + user := models.User{ + Login: in.Login, + Email: in.Email, + Password: in.Password, + } + + userID, err := h.usecase.SignUp(context.Background(), user) + if err != nil { + return nil, err + } + + return &gen.SignUpResponse{ + Id: uint32(userID), + }, nil +} + +func (h *GrpcUserHandler) Login(ctx context.Context, in *gen.LoginRequest) (*gen.LoginResponse, error) { + user, err := h.usecase.Login(context.Background(), in.Email, in.Password) + + if err != nil { + return nil, err + } + + return &gen.LoginResponse{ + Id: uint32(user.ID), + Login: user.Login, + Email: user.Email, + AvatarPath: user.AvatarPath, + }, nil +} + +func (h *GrpcUserHandler) UploadAvatar(ctx context.Context, in *gen.UploadAvatarRequest) (*gen.UploadAvatarResponse, error) { + avatarPath, err := h.usecase.UploadAvatar(context.Background(), uint(in.Id), in.AvatarData, in.AvatarFileName) + + if err != nil { + return nil, err + } + + return &gen.UploadAvatarResponse{ + AvatarPath: avatarPath, + }, nil +} + +func (h *GrpcUserHandler) GetProfile(ctx context.Context, in *gen.GetProfileRequest) (*gen.GetProfileResponse, error) { + profile, err := h.usecase.GetProfile(ctx, uint(in.Id), uint(in.RequesterId)) + if err != nil { + return nil, err + } + + return &gen.GetProfileResponse{ + Login: profile.Login, + Email: profile.Email, + AvatarPath: profile.AvatarPath, + }, nil +} + +func (h *GrpcUserHandler) UpdatePassword(ctx context.Context, in *gen.UpdatePasswordRequest) (*gen.EmptyResponse, error) { + user := models.User{ + ID: uint(in.Id), + Login: in.Login, + Email: in.Email, + Password: in.OldPassword, + } + + err := h.usecase.UpdatePassword(ctx, user, in.NewPassword) + if err != nil { + return nil, err + } + + return &gen.EmptyResponse{}, nil +} + +func (h *GrpcUserHandler) UpdateProfile(ctx context.Context, in *gen.UpdateProfileRequest) (*gen.EmptyResponse, error) { + + err := h.usecase.UpdateProfile(ctx, uint(in.UserId), in.Username, in.Email) + if err != nil { + return nil, err + } + + return &gen.EmptyResponse{}, nil +} diff --git a/internal/pkg/user/delivery/http/handler.go b/internal/pkg/user/delivery/http/handler.go index ec54aaf..be67792 100644 --- a/internal/pkg/user/delivery/http/handler.go +++ b/internal/pkg/user/delivery/http/handler.go @@ -6,10 +6,9 @@ import ( "2024_2_ThereWillBeName/internal/pkg/jwt" log "2024_2_ThereWillBeName/internal/pkg/logger" "2024_2_ThereWillBeName/internal/pkg/middleware" - "2024_2_ThereWillBeName/internal/pkg/user" + "2024_2_ThereWillBeName/internal/pkg/user/delivery/grpc/gen" "2024_2_ThereWillBeName/internal/validator" - "context" "encoding/base64" "encoding/json" "errors" @@ -31,9 +30,9 @@ type Credentials struct { } type Handler struct { - usecase user.UserUsecase - jwt jwt.JWTInterface - logger *slog.Logger + client gen.UserServiceClient + jwt jwt.JWTInterface + logger *slog.Logger } type UserResponseWithToken struct { @@ -41,11 +40,11 @@ type UserResponseWithToken struct { Token string `json:"token"` } -func NewUserHandler(usecase user.UserUsecase, jwt jwt.JWTInterface, logger *slog.Logger) *Handler { +func NewUserHandler(client gen.UserServiceClient, jwt jwt.JWTInterface, logger *slog.Logger) *Handler { return &Handler{ - usecase: usecase, - jwt: jwt, - logger: logger, + client: client, + jwt: jwt, + logger: logger, } } @@ -91,8 +90,14 @@ func (h *Handler) SignUp(w http.ResponseWriter, r *http.Request) { return } + signUpRequest := &gen.SignUpRequest{ + Login: user.Login, + Email: user.Email, + Password: user.Password, + } + var err error - user.ID, err = h.usecase.SignUp(context.Background(), user) + signupResponse, err := h.client.SignUp(r.Context(), signUpRequest) if err != nil { if errors.Is(err, models.ErrAlreadyExists) { h.logger.Warn("User already exists", slog.String("login", user.Login), slog.String("email", user.Email)) @@ -112,6 +117,8 @@ func (h *Handler) SignUp(w http.ResponseWriter, r *http.Request) { return } + user.ID = uint(signupResponse.Id) + h.logger.Debug("User signed up successfully", slog.Int("userID", int(user.ID)), slog.String("login", user.Login)) token, err := h.jwt.GenerateToken(user.ID, user.Email, user.Login) @@ -124,7 +131,7 @@ func (h *Handler) SignUp(w http.ResponseWriter, r *http.Request) { return } - h.logger.Debug("Token generated and set as cookie", slog.String("userID", strconv.Itoa(int(user.ID))), slog.String("login", user.Login), slog.String("email", user.Email)) + h.logger.Debug("Token generated", slog.String("userID", strconv.Itoa(int(user.ID))), slog.String("login", user.Login), slog.String("email", user.Email)) response := UserResponseWithToken{ User: models.User{ @@ -170,7 +177,12 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { credentials.Email = template.HTMLEscapeString(credentials.Email) credentials.Password = template.HTMLEscapeString(credentials.Password) - user, err := h.usecase.Login(context.Background(), credentials.Email, credentials.Password) + loginRequest := &gen.LoginRequest{ + Email: credentials.Email, + Password: credentials.Password, + } + + loginResponse, err := h.client.Login(r.Context(), loginRequest) if err != nil { h.logger.Warn("Login failed: invalid email or password", slog.String("email", credentials.Email)) @@ -180,11 +192,19 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { httpresponse.SendJSONResponse(w, response, http.StatusUnauthorized, h.logger) return } - v := validator.New() - if models.ValidateUser(v, &user); !v.Valid() { - httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger) - return + + user := models.User{ + ID: uint(loginResponse.Id), + Login: loginResponse.Login, + Email: loginResponse.Email, + AvatarPath: loginResponse.AvatarPath, } + + // v := validator.New() + // if models.ValidateUser(v, &user); !v.Valid() { + // httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger) + // return + // } h.logger.Debug("User logged in successfully", slog.Int("userID", int(user.ID)), slog.String("email", user.Email)) token, err := h.jwt.GenerateToken(user.ID, user.Email, user.Login) @@ -196,18 +216,14 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { httpresponse.SendJSONResponse(w, response, http.StatusInternalServerError, h.logger) return } - h.logger.Debug("Token generated and set as cookie", slog.String("userID", strconv.Itoa(int(user.ID))), slog.String("login", user.Login), slog.String("email", user.Email)) + h.logger.Debug("Token generated", slog.String("userID", strconv.Itoa(int(user.ID))), slog.String("login", user.Login), slog.String("email", user.Email)) response := UserResponseWithToken{ - User: models.User{ - ID: user.ID, - Login: user.Login, - Email: user.Email, - }, + User: user, Token: token, } - h.logger.DebugContext(logCtx, "Sign-up request completed successfully") + h.logger.DebugContext(logCtx, "Login request completed successfully") httpresponse.SendJSONResponse(w, response, http.StatusOK, h.logger) @@ -235,15 +251,6 @@ func (h *Handler) Logout(w http.ResponseWriter, r *http.Request) { return } - http.SetCookie(w, &http.Cookie{ - Name: "token", - Value: "", - Path: "/", - HttpOnly: true, - Secure: false, - MaxAge: -1, - }) - h.logger.DebugContext(logCtx, "User logged out successfully") w.WriteHeader(http.StatusOK) @@ -406,8 +413,14 @@ func (h *Handler) UploadAvatar(w http.ResponseWriter, r *http.Request) { h.logger.Debug("Uploading avatar", "userID", userID, "avatarFileName", avatarFileName) - var avatarPath string - if avatarPath, err = h.usecase.UploadAvatar(context.Background(), uint(userID), avatarData, avatarFileName); err != nil { + uploadAvatarRequest := &gen.UploadAvatarRequest{ + Id: uint32(userID), + AvatarData: avatarData, + AvatarFileName: avatarFileName, + } + + uploadAvatarResponse, err := h.client.UploadAvatar(r.Context(), uploadAvatarRequest) + if err != nil { h.logger.Error("Failed to upload avatar", "userID", userID, "error", err) response := httpresponse.ErrorResponse{ Message: "Failed to upload avatar", @@ -418,10 +431,10 @@ func (h *Handler) UploadAvatar(w http.ResponseWriter, r *http.Request) { response := map[string]string{ "message": "Avatar uploaded successfully", - "avatarPath": avatarPath, + "avatarPath": uploadAvatarResponse.AvatarPath, } - h.logger.DebugContext(logCtx, "Avatar uploaded successfully", "userID", userID, "avatarPath", avatarPath) + h.logger.DebugContext(logCtx, "Avatar uploaded successfully", "userID", userID, "avatarPath", uploadAvatarResponse.AvatarPath) httpresponse.SendJSONResponse(w, response, http.StatusOK, h.logger) } @@ -465,7 +478,12 @@ func (h *Handler) GetProfile(w http.ResponseWriter, r *http.Request) { h.logger.Debug("Fetching profile", "userID", userID, "requesterID", requesterID) - profile, err := h.usecase.GetProfile(context.Background(), uint(userID), requesterID) + getProfileRequest := &gen.GetProfileRequest{ + Id: uint32(userID), + RequesterId: uint32(requesterID), + } + + GetProfileResponse, err := h.client.GetProfile(r.Context(), getProfileRequest) if err != nil { if errors.Is(err, models.ErrNotFound) { h.logger.Warn("User not found", "userID", userID) @@ -487,7 +505,7 @@ func (h *Handler) GetProfile(w http.ResponseWriter, r *http.Request) { } h.logger.Debug("User profile retrieved successfully", "userID", userID) - httpresponse.SendJSONResponse(w, profile, http.StatusOK, h.logger) + httpresponse.SendJSONResponse(w, GetProfileResponse, http.StatusOK, h.logger) } func (h *Handler) UpdatePassword(w http.ResponseWriter, r *http.Request) { @@ -532,16 +550,17 @@ func (h *Handler) UpdatePassword(w http.ResponseWriter, r *http.Request) { return } - user := models.User{ - ID: userID, - Login: login, - Email: email, - Password: credentials.OldPassword, - } - h.logger.Debug("updating password", "userID", userID, "oldPassword", credentials.OldPassword, "newPassword", credentials.NewPassword) - err := h.usecase.UpdatePassword(r.Context(), user, credentials.NewPassword) + updatePasswordRequest := &gen.UpdatePasswordRequest{ + Id: uint32(userID), + Login: login, + Email: email, + OldPassword: credentials.OldPassword, + NewPassword: credentials.NewPassword, + } + + _, err := h.client.UpdatePassword(r.Context(), updatePasswordRequest) if err != nil { if errors.Is(err, models.ErrNotFound) { response := httpresponse.ErrorResponse{ @@ -565,11 +584,11 @@ func (h *Handler) UpdatePassword(w http.ResponseWriter, r *http.Request) { } response := struct { - ID uint `json:"id"` - Password string `json:"password"` + ID uint `json:"id"` + Message string `json:"message"` }{ - ID: user.ID, - Password: credentials.NewPassword, + ID: userID, + Message: "User's password updated successfully", } h.logger.Debug("User password updated successfully") @@ -604,7 +623,13 @@ func (h *Handler) UpdateProfile(w http.ResponseWriter, r *http.Request) { h.logger.Debug("updating profile", "userID", userID, "username", userData.Login, "email", userData.Email) - err := h.usecase.UpdateProfile(r.Context(), userID, userData.Login, userData.Email) + updateProfileRequest := &gen.UpdateProfileRequest{ + UserId: uint32(userID), + Username: userData.Login, + Email: userData.Email, + } + + _, err := h.client.UpdateProfile(r.Context(), updateProfileRequest) if err != nil { if errors.Is(err, models.ErrNotFound) { response := httpresponse.ErrorResponse{ diff --git a/internal/pkg/user/mocks/mock.go b/internal/pkg/user/mocks/mock.go index 6c95aa5..1bcd30b 100644 --- a/internal/pkg/user/mocks/mock.go +++ b/internal/pkg/user/mocks/mock.go @@ -1,13 +1,12 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: internal/pkg/user/interfaces.go +// Source: interfaces.go -// Package user is a generated GoMock package. -package user +// Package mock_user is a generated GoMock package. +package mock_user import ( models "2024_2_ThereWillBeName/internal/models" context "context" - multipart "mime/multipart" reflect "reflect" gomock "github.com/golang/mock/gomock" @@ -81,19 +80,47 @@ func (mr *MockUserUsecaseMockRecorder) SignUp(ctx, user interface{}) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignUp", reflect.TypeOf((*MockUserUsecase)(nil).SignUp), ctx, user) } +// UpdatePassword mocks base method. +func (m *MockUserUsecase) UpdatePassword(ctx context.Context, userData models.User, newPassword string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdatePassword", ctx, userData, newPassword) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdatePassword indicates an expected call of UpdatePassword. +func (mr *MockUserUsecaseMockRecorder) UpdatePassword(ctx, userData, newPassword interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePassword", reflect.TypeOf((*MockUserUsecase)(nil).UpdatePassword), ctx, userData, newPassword) +} + +// UpdateProfile mocks base method. +func (m *MockUserUsecase) UpdateProfile(ctx context.Context, userID uint, login, email string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateProfile", ctx, userID, login, email) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateProfile indicates an expected call of UpdateProfile. +func (mr *MockUserUsecaseMockRecorder) UpdateProfile(ctx, userID, login, email interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateProfile", reflect.TypeOf((*MockUserUsecase)(nil).UpdateProfile), ctx, userID, login, email) +} + // UploadAvatar mocks base method. -func (m *MockUserUsecase) UploadAvatar(ctx context.Context, userID uint, avatarFile multipart.File, header *multipart.FileHeader) (string, error) { +func (m *MockUserUsecase) UploadAvatar(ctx context.Context, userID uint, avatarData []byte, avatarFileName string) (string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UploadAvatar", ctx, userID, avatarFile, header) + ret := m.ctrl.Call(m, "UploadAvatar", ctx, userID, avatarData, avatarFileName) ret0, _ := ret[0].(string) ret1, _ := ret[1].(error) return ret0, ret1 } // UploadAvatar indicates an expected call of UploadAvatar. -func (mr *MockUserUsecaseMockRecorder) UploadAvatar(ctx, userID, avatarFile, header interface{}) *gomock.Call { +func (mr *MockUserUsecaseMockRecorder) UploadAvatar(ctx, userID, avatarData, avatarFileName interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadAvatar", reflect.TypeOf((*MockUserUsecase)(nil).UploadAvatar), ctx, userID, avatarFile, header) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadAvatar", reflect.TypeOf((*MockUserUsecase)(nil).UploadAvatar), ctx, userID, avatarData, avatarFileName) } // MockUserRepo is a mock of UserRepo interface. @@ -222,6 +249,34 @@ func (mr *MockUserRepoMockRecorder) UpdateAvatarPathByUserId(ctx, userID, avatar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAvatarPathByUserId", reflect.TypeOf((*MockUserRepo)(nil).UpdateAvatarPathByUserId), ctx, userID, avatarPath) } +// UpdatePassword mocks base method. +func (m *MockUserRepo) UpdatePassword(ctx context.Context, userID uint, newPassword string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdatePassword", ctx, userID, newPassword) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdatePassword indicates an expected call of UpdatePassword. +func (mr *MockUserRepoMockRecorder) UpdatePassword(ctx, userID, newPassword interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePassword", reflect.TypeOf((*MockUserRepo)(nil).UpdatePassword), ctx, userID, newPassword) +} + +// UpdateProfile mocks base method. +func (m *MockUserRepo) UpdateProfile(ctx context.Context, userID uint, login, email string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateProfile", ctx, userID, login, email) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateProfile indicates an expected call of UpdateProfile. +func (mr *MockUserRepoMockRecorder) UpdateProfile(ctx, userID, login, email interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateProfile", reflect.TypeOf((*MockUserRepo)(nil).UpdateProfile), ctx, userID, login, email) +} + // UpdateUser mocks base method. func (m *MockUserRepo) UpdateUser(ctx context.Context, user models.User) error { m.ctrl.T.Helper() diff --git a/internal/pkg/user/repo/user_repository.go b/internal/pkg/user/repo/user_repository.go index 4fb18f9..9272907 100644 --- a/internal/pkg/user/repo/user_repository.go +++ b/internal/pkg/user/repo/user_repository.go @@ -37,9 +37,9 @@ func (r *UserRepositoryImpl) CreateUser(ctx context.Context, user models.User) ( func (r *UserRepositoryImpl) GetUserByEmail(ctx context.Context, email string) (models.User, error) { var user models.User - query := `SELECT id, login, email, password_hash, created_at FROM "user" WHERE email = $1` + query := `SELECT id, login, email, password_hash FROM "user" WHERE email = $1` row := r.db.QueryRowContext(ctx, query, email) - err := row.Scan(&user.ID, &user.Login, &user.Email, &user.Password, &user.CreatedAt) + err := row.Scan(&user.ID, &user.Login, &user.Email, &user.Password) if err != nil { if err == sql.ErrNoRows { return models.User{}, fmt.Errorf("user not found with email: %s, %s", email, models.ErrNotFound) @@ -143,7 +143,7 @@ func (r *UserRepositoryImpl) GetUserByID(ctx context.Context, userID uint) (mode } func (r *UserRepositoryImpl) UpdatePassword(ctx context.Context, userId uint, newPassword string) error { - query := "UPDATE users SET password = $1 WHERE id = $2" + query := "UPDATE user SET password = $1 WHERE id = $2" _, err := r.db.ExecContext(ctx, query, newPassword, userId) if err != nil { @@ -153,7 +153,7 @@ func (r *UserRepositoryImpl) UpdatePassword(ctx context.Context, userId uint, ne } func (r UserRepositoryImpl) UpdateProfile(ctx context.Context, userID uint, login, email string) error { - query := "UPDATE users SET email = $1, login = $2 WHERE id = $3" + query := "UPDATE user SET email = $1, login = $2 WHERE id = $3" _, err := r.db.ExecContext(ctx, query, email, login, userID) if err != nil { diff --git a/proto/search.proto b/proto/search.proto new file mode 100644 index 0000000..895cfdc --- /dev/null +++ b/proto/search.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package search; +option go_package = "./internal/pkg/search/delivery/grpc/gen/;gen"; +service Search { + rpc Search(SearchRequest) returns (SearchResponse); +} + +message SearchRequest { + string decoded_query = 1; +} + +message SearchResponse { + repeated SearchResult search_result = 1; +} + +message SearchResult { + string name = 1; + uint32 id = 2; + string type = 3; +} diff --git a/proto/trips.proto b/proto/trips.proto new file mode 100644 index 0000000..3e53eaa --- /dev/null +++ b/proto/trips.proto @@ -0,0 +1,62 @@ +syntax = "proto3"; +package trips; +import "google/protobuf/timestamp.proto"; +option go_package = "./internal/pkg/trips/delivery/grpc/gen/;gen"; +service Trips { + rpc CreateTrip(CreateTripRequest) returns (EmptyResponse); + rpc UpdateTrip(UpdateTripRequest) returns (EmptyResponse); + rpc DeleteTrip(DeleteTripRequest) returns (EmptyResponse); + rpc GetTripsByUserID(GetTripsByUserIDRequest) returns (GetTripsByUserIDResponse); + rpc GetTrip(GetTripRequest) returns (GetTripResponse); + rpc AddPlaceToTrip(AddPlaceToTripRequest) returns (EmptyResponse); +} + +message CreateTripRequest { + Trip trip = 1; +} + +message UpdateTripRequest { + Trip trip = 1; +} + +message DeleteTripRequest { + uint32 id = 1; +} + +message GetTripsByUserIDRequest { + uint32 user_id = 1; + int32 limit = 2; + int32 offset = 3; +} + +message GetTripsByUserIDResponse { + repeated Trip trips = 1; +} + +message GetTripRequest { + uint32 trip_id = 1; +} + +message GetTripResponse { + Trip trip = 1; +} + +message AddPlaceToTripRequest { + uint32 trip_id = 1; + uint32 place_id = 2; +} + +message EmptyResponse {} + +message Trip { + uint32 id = 1; + uint32 user_id = 2; + string name = 3; + string description = 4; + uint32 city_id = 5; + string start_date = 6; + string end_date = 7; + bool private = 8; + google.protobuf.Timestamp created_at = 9; +} + diff --git a/proto/user.proto b/proto/user.proto new file mode 100644 index 0000000..c352909 --- /dev/null +++ b/proto/user.proto @@ -0,0 +1,73 @@ +syntax = "proto3"; + +package user; +option go_package = ".;gen"; + +service UserService { + rpc SignUp(SignUpRequest) returns (SignUpResponse) {} + rpc Login(LoginRequest) returns (LoginResponse) {} + rpc UploadAvatar(UploadAvatarRequest) returns (UploadAvatarResponse) {} + rpc GetProfile(GetProfileRequest) returns (GetProfileResponse) {} + rpc UpdatePassword(UpdatePasswordRequest) returns (EmptyResponse) {} + rpc UpdateProfile(UpdateProfileRequest) returns (EmptyResponse) {} +} + + +message SignUpRequest { + string login = 1; + string email = 2; + string password = 3; +} + +message SignUpResponse { + uint32 id = 1; +} + +message LoginRequest { + string email = 1; + string password = 2; +} + +message LoginResponse { + uint32 id = 1; + string login = 2; + string email = 3; + string avatar_path = 4; +} + +message UploadAvatarRequest { + uint32 id = 1; + bytes avatar_data = 2; + string avatar_file_name = 3; +} + +message UploadAvatarResponse { + string avatar_path = 2; +} + +message GetProfileRequest { + uint32 id = 1; + uint32 requester_id = 2; +} + +message GetProfileResponse { + string login = 1; + string email = 2; + string avatar_path = 3; +} + +message UpdatePasswordRequest { + uint32 id = 1; + string login = 2; + string email = 3; + string old_password = 4; + string new_password = 5; +} + +message UpdateProfileRequest { + uint32 user_id = 1; + string username = 2; + string email = 3; +} + +message EmptyResponse {} \ No newline at end of file diff --git a/protoc-25.1-linux-x86_64.zip b/protoc-25.1-linux-x86_64.zip new file mode 100644 index 0000000..d3f6dfc Binary files /dev/null and b/protoc-25.1-linux-x86_64.zip differ