diff --git a/backend/app/api/docs/docs.go b/backend/app/api/docs/docs.go index a6d2d37..e62b065 100644 --- a/backend/app/api/docs/docs.go +++ b/backend/app/api/docs/docs.go @@ -261,6 +261,78 @@ const docTemplate = `{ } } }, + "/v1/locations": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Locations" + ], + "summary": "Get All Locations", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/server.Results" + }, + { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/types.LocationOut" + } + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Locations" + ], + "summary": "Create a new location", + "parameters": [ + { + "description": "Location Data", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.LocationCreate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.LocationOut" + } + } + } + } + }, "/v1/users/login": { "post": { "consumes": [ @@ -358,23 +430,8 @@ const docTemplate = `{ } ], "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/ent.User" - } - } - } - ] - } + "204": { + "description": "" } } } @@ -908,6 +965,14 @@ const docTemplate = `{ } } }, + "server.Results": { + "type": "object", + "properties": { + "items": { + "type": "any" + } + } + }, "types.ApiSummary": { "type": "object", "properties": { @@ -928,6 +993,40 @@ const docTemplate = `{ } } }, + "types.LocationCreate": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "types.LocationOut": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, "types.TokenResponse": { "type": "object", "properties": { diff --git a/backend/app/api/docs/swagger.json b/backend/app/api/docs/swagger.json index 3124ca5..4d5a114 100644 --- a/backend/app/api/docs/swagger.json +++ b/backend/app/api/docs/swagger.json @@ -253,6 +253,78 @@ } } }, + "/v1/locations": { + "get": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Locations" + ], + "summary": "Get All Locations", + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/server.Results" + }, + { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/definitions/types.LocationOut" + } + } + } + } + ] + } + } + } + }, + "post": { + "security": [ + { + "Bearer": [] + } + ], + "produces": [ + "application/json" + ], + "tags": [ + "Locations" + ], + "summary": "Create a new location", + "parameters": [ + { + "description": "Location Data", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/types.LocationCreate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/types.LocationOut" + } + } + } + } + }, "/v1/users/login": { "post": { "consumes": [ @@ -350,23 +422,8 @@ } ], "responses": { - "200": { - "description": "OK", - "schema": { - "allOf": [ - { - "$ref": "#/definitions/server.Result" - }, - { - "type": "object", - "properties": { - "item": { - "$ref": "#/definitions/ent.User" - } - } - } - ] - } + "204": { + "description": "" } } } @@ -900,6 +957,14 @@ } } }, + "server.Results": { + "type": "object", + "properties": { + "items": { + "type": "any" + } + } + }, "types.ApiSummary": { "type": "object", "properties": { @@ -920,6 +985,40 @@ } } }, + "types.LocationCreate": { + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "types.LocationOut": { + "type": "object", + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + } + }, "types.TokenResponse": { "type": "object", "properties": { diff --git a/backend/app/api/docs/swagger.yaml b/backend/app/api/docs/swagger.yaml index 5845631..fa6d3e2 100644 --- a/backend/app/api/docs/swagger.yaml +++ b/backend/app/api/docs/swagger.yaml @@ -320,6 +320,11 @@ definitions: message: type: string type: object + server.Results: + properties: + items: + type: any + type: object types.ApiSummary: properties: health: @@ -333,6 +338,28 @@ definitions: type: string type: array type: object + types.LocationCreate: + properties: + description: + type: string + name: + type: string + type: object + types.LocationOut: + properties: + createdAt: + type: string + description: + type: string + groupId: + type: string + id: + type: string + name: + type: string + updatedAt: + type: string + type: object types.TokenResponse: properties: expiresAt: @@ -522,6 +549,47 @@ paths: summary: Update a User tags: - 'Admin: Users' + /v1/locations: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/server.Results' + - properties: + items: + items: + $ref: '#/definitions/types.LocationOut' + type: array + type: object + security: + - Bearer: [] + summary: Get All Locations + tags: + - Locations + post: + parameters: + - description: Location Data + in: body + name: payload + required: true + schema: + $ref: '#/definitions/types.LocationCreate' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/types.LocationOut' + security: + - Bearer: [] + summary: Create a new location + tags: + - Locations /v1/users/login: post: consumes: @@ -583,15 +651,8 @@ paths: produces: - application/json responses: - "200": - description: OK - schema: - allOf: - - $ref: '#/definitions/server.Result' - - properties: - item: - $ref: '#/definitions/ent.User' - type: object + "204": + description: "" summary: Get the current user tags: - User diff --git a/backend/app/api/routes.go b/backend/app/api/routes.go index 0fed8b2..0041cde 100644 --- a/backend/app/api/routes.go +++ b/backend/app/api/routes.go @@ -49,6 +49,9 @@ func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux { r.Put(v1Base("/users/self/password"), v1Handlers.HandleUserUpdatePassword()) r.Post(v1Base("/users/logout"), v1Handlers.HandleAuthLogout()) r.Get(v1Base("/users/refresh"), v1Handlers.HandleAuthRefresh()) + + r.Get(v1Base("/locations"), v1Handlers.HandleLocationGetAll()) + r.Post(v1Base("/locations"), v1Handlers.HandleLocationCreate()) }) r.Group(func(r chi.Router) { diff --git a/backend/app/api/v1/v1_ctrl_locations.go b/backend/app/api/v1/v1_ctrl_locations.go new file mode 100644 index 0000000..0674e7a --- /dev/null +++ b/backend/app/api/v1/v1_ctrl_locations.go @@ -0,0 +1,59 @@ +package v1 + +import ( + "net/http" + + "github.com/hay-kot/content/backend/internal/services" + "github.com/hay-kot/content/backend/internal/types" + "github.com/hay-kot/content/backend/pkgs/server" +) + +// HandleUserSelf godoc +// @Summary Get All Locations +// @Tags Locations +// @Produce json +// @Success 200 {object} server.Results{items=[]types.LocationOut} +// @Router /v1/locations [GET] +// @Security Bearer +func (ctrl *V1Controller) HandleLocationGetAll() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + user := services.UseUserCtx(r.Context()) + locations, err := ctrl.svc.Location.GetAll(r.Context(), user.GroupID) + if err != nil { + ctrl.log.Error(err, nil) + server.RespondServerError(w) + return + } + + server.Respond(w, http.StatusOK, server.Results{Items: locations}) + } +} + +// HandleUserSelf godoc +// @Summary Create a new location +// @Tags Locations +// @Produce json +// @Param payload body types.LocationCreate true "Location Data" +// @Success 200 {object} types.LocationOut +// @Router /v1/locations [POST] +// @Security Bearer +func (ctrl *V1Controller) HandleLocationCreate() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + createData := types.LocationCreate{} + if err := server.Decode(r, &createData); err != nil { + ctrl.log.Error(err, nil) + server.RespondError(w, http.StatusInternalServerError, err) + return + } + + user := services.UseUserCtx(r.Context()) + location, err := ctrl.svc.Location.Create(r.Context(), user.GroupID, createData) + if err != nil { + ctrl.log.Error(err, nil) + server.RespondServerError(w) + return + } + + server.Respond(w, http.StatusCreated, location) + } +} diff --git a/backend/app/api/v1/v1_ctrl_user.go b/backend/app/api/v1/v1_ctrl_user.go index ca29378..9abf0ee 100644 --- a/backend/app/api/v1/v1_ctrl_user.go +++ b/backend/app/api/v1/v1_ctrl_user.go @@ -15,8 +15,8 @@ import ( // @Summary Get the current user // @Tags User // @Produce json -// @Param payload body types.UserRegistration true "User Data" -// @Success 200 {object} server.Result{item=ent.User} +// @Param payload body types.UserRegistration true "User Data" +// @Success 204 // @Router /v1/users/register [Post] func (ctrl *V1Controller) HandleUserRegistration() http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/backend/internal/repo/repo_locations.go b/backend/internal/repo/repo_locations.go index 85c8cfd..babc185 100644 --- a/backend/internal/repo/repo_locations.go +++ b/backend/internal/repo/repo_locations.go @@ -21,6 +21,7 @@ func (r *EntLocationRepository) Get(ctx context.Context, ID uuid.UUID) (*ent.Loc func (r *EntLocationRepository) GetAll(ctx context.Context, groupId uuid.UUID) ([]*ent.Location, error) { return r.db.Location.Query(). Where(location.HasGroupWith(group.ID(groupId))). + WithGroup(). All(ctx) } diff --git a/backend/internal/services/all.go b/backend/internal/services/all.go index 39bc690..cd4110a 100644 --- a/backend/internal/services/all.go +++ b/backend/internal/services/all.go @@ -3,13 +3,15 @@ package services import "github.com/hay-kot/content/backend/internal/repo" type AllServices struct { - User *UserService - Admin *AdminService + User *UserService + Admin *AdminService + Location *LocationService } func NewServices(repos *repo.AllRepos) *AllServices { return &AllServices{ - User: &UserService{repos}, - Admin: &AdminService{repos}, + User: &UserService{repos}, + Admin: &AdminService{repos}, + Location: &LocationService{repos}, } } diff --git a/backend/internal/services/service_locations.go b/backend/internal/services/service_locations.go index 5eae347..b809c28 100644 --- a/backend/internal/services/service_locations.go +++ b/backend/internal/services/service_locations.go @@ -24,6 +24,22 @@ func ToLocationOut(location *ent.Location, err error) (*types.LocationOut, error }, err } -func (svc *LocationService) GetAll(ctx context.Context, groupId uuid.UUID) ([]*types.LocationOut, error) { - panic("not implemented") +func (svc *LocationService) Create(ctx context.Context, groupId uuid.UUID, data types.LocationCreate) (*types.LocationOut, error) { + location, err := svc.repos.Locations.Create(ctx, groupId, data) + return ToLocationOut(location, err) +} + +func (svc *LocationService) GetAll(ctx context.Context, groupId uuid.UUID) ([]*types.LocationOut, error) { + locations, err := svc.repos.Locations.GetAll(ctx, groupId) + if err != nil { + return nil, err + } + + locationsOut := make([]*types.LocationOut, len(locations)) + for i, location := range locations { + locationOut, _ := ToLocationOut(location, nil) + locationsOut[i] = locationOut + } + + return locationsOut, nil } diff --git a/backend/pkgs/server/result.go b/backend/pkgs/server/result.go index c2340a5..656d17b 100644 --- a/backend/pkgs/server/result.go +++ b/backend/pkgs/server/result.go @@ -7,6 +7,10 @@ type Result struct { Item interface{} `json:"item,omitempty"` } +type Results struct { + Items any `json:"items"` +} + // Wrap creates a Wrapper instance and adds the initial namespace and data to be returned. func Wrap(data interface{}) Result { return Result{