diff --git a/.env.template b/.env.template index 9f41c01..d9027e5 100644 --- a/.env.template +++ b/.env.template @@ -23,3 +23,7 @@ FIREBASE_CREDENTIALS_TOKEN_URI= FIREBASE_CREDENTIALS_AUTH_PROVIDER_X509_CERT_URL= FIREBASE_CREDENTIALS_CLIENT_X509_CERT_URL= FIREBASE_CREDENTIALS_UNIVERSE_DOMAIN= + +## Scripts ## +GOOGLE_SHEETS_API_KEY= +BREEDS_GOOGLE_SHEETS_ID= diff --git a/api/commonviews/common.go b/api/commonviews/common.go index 89d0eb8..dfef320 100644 --- a/api/commonviews/common.go +++ b/api/commonviews/common.go @@ -5,6 +5,20 @@ import ( "net/http" ) +type PaginatedView[T interface{}] struct { + Page int `json:"page"` + Size int `json:"size"` + Items []T `json:"items"` +} + +func NewPaginatedView[T interface{}](page int, size int, items []T) *PaginatedView[T] { + return &PaginatedView[T]{ + Page: page, + Size: size, + Items: items, + } +} + func writePayload(w http.ResponseWriter, headers map[string]string, payload interface{}, statusCode int) error { setHeaders(w, headers) diff --git a/cmd/import_breeds/main.go b/cmd/import_breeds/main.go index 115924c..3481ebd 100644 --- a/cmd/import_breeds/main.go +++ b/cmd/import_breeds/main.go @@ -33,7 +33,7 @@ func main() { log.Fatalf("error initializing google sheets client: %v\n", err) } - spreadsheet, err := client.GetSpreadsheet(configs.BreedsSheetsID) + spreadsheet, err := client.GetSpreadsheet(configs.BreedsGoogleSheetsID) switch flags.petTypeToImport { case Cat: diff --git a/cmd/server/handler/breed_handler.go b/cmd/server/handler/breed_handler.go new file mode 100644 index 0000000..ca5b01d --- /dev/null +++ b/cmd/server/handler/breed_handler.go @@ -0,0 +1,68 @@ +package handler + +import ( + "github.com/pet-sitter/pets-next-door-api/api/commonviews" + "github.com/pet-sitter/pets-next-door-api/internal/domain/pet" + "net/http" + "strconv" +) + +type BreedHandler struct { + breedService *pet.BreedService +} + +func NewBreedHandler(breedService *pet.BreedService) *BreedHandler { + return &BreedHandler{breedService: breedService} +} + +// FindBreeds godoc +// @Summary 견/묘종을 조회합니다. +// @Description +// @Tags pets +// @Accept json +// @Produce json +// @Param page query int false "페이지 번호" default(1) +// @Param size query int false "페이지 사이즈" default(20) +// @Param pet_type query string false "펫 종류" Enums(dog, cat) +// @Success 200 {object} commonviews.PaginatedView[pet.BreedView] +// @Router /breeds [get] +func (h *BreedHandler) FindBreeds(w http.ResponseWriter, r *http.Request) { + pageQuery := r.URL.Query().Get("page") + sizeQuery := r.URL.Query().Get("size") + petTypeQuery := r.URL.Query().Get("pet_type") + + page := 1 + size := 20 + var err error + + if pageQuery != "" { + page, err = strconv.Atoi(pageQuery) + if err != nil { + commonviews.BadRequest(w, nil, err.Error()) + return + } + } + + if sizeQuery != "" { + size, err = strconv.Atoi(sizeQuery) + if err != nil { + commonviews.BadRequest(w, nil, err.Error()) + return + } + } + + var petType *string + if petTypeQuery == "" { + petType = nil + } else { + petType = &petTypeQuery + } + + res, err := h.breedService.FindBreeds(page, size, petType) + if err != nil { + commonviews.InternalServerError(w, nil, err.Error()) + return + } + + commonviews.OK(w, nil, commonviews.NewPaginatedView(page, size, res)) +} diff --git a/cmd/server/router.go b/cmd/server/router.go index ff115f0..d99d206 100644 --- a/cmd/server/router.go +++ b/cmd/server/router.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/json" + "github.com/pet-sitter/pets-next-door-api/internal/domain/pet" "log" "net/http" @@ -63,10 +64,14 @@ func addRoutes(r *chi.Mux, authService auth.AuthService) { postgres.NewPetPostgresStore(db), mediaService, ) + breedService := pet.NewBreedService( + postgres.NewBreedPostgresStore(db), + ) authHandler := handler.NewAuthHandler(authService, kakaoinfra.NewKakaoClient()) userHandler := handler.NewUserHandler(userService, authService) mediaHandler := handler.NewMediaHandler(mediaService) + breedHandler := handler.NewBreedHandler(breedService) r.Get("/health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -94,5 +99,8 @@ func addRoutes(r *chi.Mux, authService auth.AuthService) { r.Get("/me/pets", userHandler.FindMyPets) r.Put("/me/pets", userHandler.AddMyPets) }) + r.Route("/breeds", func(r chi.Router) { + r.Get("/", breedHandler.FindBreeds) + }) }) } diff --git a/internal/configs/server.go b/internal/configs/server.go index 6b1c363..bac901c 100644 --- a/internal/configs/server.go +++ b/internal/configs/server.go @@ -54,7 +54,7 @@ var B2Endpoint = os.Getenv("B2_ENDPOINT") var B2Region = os.Getenv("B2_REGION") var GoogleSheetsAPIKey = os.Getenv("GOOGLE_SHEETS_API_KEY") -var BreedsSheetsID = os.Getenv("BREEDS_SHEETS_ID") +var BreedsGoogleSheetsID = os.Getenv("BREEDS_GOOGLE_SHEETS_ID") func init() { if Port == "" { diff --git a/pkg/docs/docs.go b/pkg/docs/docs.go index 9b5d594..4a64b8f 100644 --- a/pkg/docs/docs.go +++ b/pkg/docs/docs.go @@ -1,5 +1,4 @@ -// Code generated by swaggo/swag. DO NOT EDIT. - +// Package docs Code generated by swaggo/swag. DO NOT EDIT package docs import "github.com/swaggo/swag" @@ -54,6 +53,54 @@ const docTemplate = `{ } } }, + "/breeds": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pets" + ], + "summary": "견/묘종을 조회합니다.", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "페이지 번호", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "페이지 사이즈", + "name": "size", + "in": "query" + }, + { + "enum": [ + "dog", + "cat" + ], + "type": "string", + "description": "펫 종류", + "name": "pet_type", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/commonviews.PaginatedView-pet_BreedView" + } + } + } + } + }, "/media/images": { "post": { "consumes": [ @@ -320,6 +367,23 @@ const docTemplate = `{ } } }, + "commonviews.PaginatedView-pet_BreedView": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/pet.BreedView" + } + }, + "page": { + "type": "integer" + }, + "size": { + "type": "integer" + } + } + }, "media.MediaType": { "type": "string", "enum": [ @@ -411,6 +475,20 @@ const docTemplate = `{ } } }, + "pet.BreedView": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "pet_type": { + "$ref": "#/definitions/pet.PetType" + } + } + }, "pet.FindMyPetsView": { "type": "object", "properties": { diff --git a/pkg/docs/swagger.json b/pkg/docs/swagger.json index d91f32d..f9d5d30 100644 --- a/pkg/docs/swagger.json +++ b/pkg/docs/swagger.json @@ -46,6 +46,54 @@ } } }, + "/breeds": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "pets" + ], + "summary": "견/묘종을 조회합니다.", + "parameters": [ + { + "type": "integer", + "default": 1, + "description": "페이지 번호", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "default": 20, + "description": "페이지 사이즈", + "name": "size", + "in": "query" + }, + { + "enum": [ + "dog", + "cat" + ], + "type": "string", + "description": "펫 종류", + "name": "pet_type", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/commonviews.PaginatedView-pet_BreedView" + } + } + } + } + }, "/media/images": { "post": { "consumes": [ @@ -312,6 +360,23 @@ } } }, + "commonviews.PaginatedView-pet_BreedView": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/pet.BreedView" + } + }, + "page": { + "type": "integer" + }, + "size": { + "type": "integer" + } + } + }, "media.MediaType": { "type": "string", "enum": [ @@ -403,6 +468,20 @@ } } }, + "pet.BreedView": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "pet_type": { + "$ref": "#/definitions/pet.PetType" + } + } + }, "pet.FindMyPetsView": { "type": "object", "properties": { diff --git a/pkg/docs/swagger.yaml b/pkg/docs/swagger.yaml index 80d5770..a5019c5 100644 --- a/pkg/docs/swagger.yaml +++ b/pkg/docs/swagger.yaml @@ -13,6 +13,17 @@ definitions: photoURL: type: string type: object + commonviews.PaginatedView-pet_BreedView: + properties: + items: + items: + $ref: '#/definitions/pet.BreedView' + type: array + page: + type: integer + size: + type: integer + type: object media.MediaType: enum: - image @@ -72,6 +83,15 @@ definitions: required: - pets type: object + pet.BreedView: + properties: + id: + type: integer + name: + type: string + pet_type: + $ref: '#/definitions/pet.PetType' + type: object pet.FindMyPetsView: properties: pets: @@ -259,6 +279,38 @@ paths: summary: Kakao 로그인 페이지로 redirect 합니다. tags: - auth + /breeds: + get: + consumes: + - application/json + parameters: + - default: 1 + description: 페이지 번호 + in: query + name: page + type: integer + - default: 20 + description: 페이지 사이즈 + in: query + name: size + type: integer + - description: 펫 종류 + enum: + - dog + - cat + in: query + name: pet_type + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/commonviews.PaginatedView-pet_BreedView' + summary: 견/묘종을 조회합니다. + tags: + - pets /media/{id}: get: parameters: