Add locations endpoint

main
DustyP 4 years ago
parent 4cf140e529
commit 35b2afba37

@ -4,12 +4,17 @@ import (
"fmt"
"log"
"net/http"
"os"
"strings"
"github.com/dustinpianalto/errors"
"github.com/dustinpianalto/quartermaster"
"github.com/golang-jwt/jwt"
"github.com/gorilla/mux"
)
var jwtKey = []byte(os.Getenv("JWT_KEY"))
func Mount(r *mux.Router, path string, handler http.Handler) {
r.PathPrefix(path).Handler(
http.StripPrefix(
@ -77,3 +82,32 @@ func (rh RootHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func getErrorBody(s string) []byte {
return []byte(fmt.Sprintf("{\"error\": \"%s\"}", s))
}
func IsAuthenticated(r *http.Request) (*jwt.Token, error) {
const method errors.Method = "utils/IsAuthenticated"
c, err := r.Cookie("token")
if err != nil {
if err == http.ErrNoCookie {
return nil, errors.E(method, errors.Incorrect, "cookie not found", err)
}
return nil, errors.E(method, errors.Malformed, "failed to get cookie data", err)
}
tknStr := c.Value
claims := &quartermaster.Claims{}
tkn, err := jwt.ParseWithClaims(tknStr, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
if err == jwt.ErrSignatureInvalid {
return nil, errors.E(method, errors.Incorrect, "cookie is invalid", err)
}
e, _ := err.(*jwt.ValidationError)
if e.Inner == jwt.ErrInvalidKeyType {
return nil, errors.E(method, errors.Internal, err)
} else if e.Inner == jwt.ErrHashUnavailable {
return nil, errors.E(method, errors.Internal, err)
}
return nil, errors.E(method, errors.Malformed, "failed to parse cookie", err)
}
return tkn, nil
}

@ -1,7 +1,7 @@
package quartermaster
type Location struct {
ID int `json:"id"`
ID int `json:"id,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Parent *Location `json:"parent,omitempty"`

@ -2,14 +2,15 @@ package api
import (
"github.com/dustinpianalto/quartermaster/internal/utils"
"github.com/dustinpianalto/quartermaster/pkg/api/locations"
"github.com/dustinpianalto/quartermaster/pkg/api/users"
"github.com/gorilla/mux"
)
func GetRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", healthcheck).Methods("GET")
router.HandleFunc("/healthcheck", healthcheck).Methods("GET")
utils.Mount(router, "/users", users.GetRouter())
utils.Mount(router, "/locations", locations.GetRouter())
return router
}

@ -1,9 +0,0 @@
package location
import "github.com/gorilla/mux"
func GetRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", location).Methods("GET")
return router
}

@ -1,13 +0,0 @@
package location
import "net/http"
func location(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
addLocation(w, r)
}
}
func addLocation(w http.ResponseWriter, r *http.Request) {
}

@ -0,0 +1,12 @@
package locations
import (
"github.com/dustinpianalto/quartermaster/internal/utils"
"github.com/gorilla/mux"
)
func GetRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
router.Handle("/", utils.RootHandler(locations))
return router
}

@ -0,0 +1,86 @@
package locations
import (
"encoding/json"
"net/http"
"github.com/dustinpianalto/errors"
"github.com/dustinpianalto/quartermaster"
"github.com/dustinpianalto/quartermaster/internal/utils"
"github.com/dustinpianalto/quartermaster/pkg/services"
)
func locations(w http.ResponseWriter, r *http.Request) error {
const method = "locations"
token, err := utils.IsAuthenticated(r)
if err != nil {
return errors.E(method, errors.Permission, err)
}
if !token.Valid {
return errors.E(method, errors.Permission, "user not authenticated")
}
user, err := services.UserService.User(token.Claims.(*quartermaster.Claims).Username)
if err != nil {
return errors.E(method, errors.Permission, err)
}
if r.Method == "POST" {
err = addLocation(w, r, user)
if err != nil {
return errors.E(method, "there was a problem adding the location", err)
}
} else if r.Method == "GET" {
err = getTopLocations(w, r, user)
if err != nil {
return errors.E(method, "there was a problem getting locations", err)
}
} else {
return errors.E(method, errors.Malformed, "http method not allowed")
}
return nil
}
func addLocation(w http.ResponseWriter, r *http.Request, u *quartermaster.User) error {
const method errors.Method = "locations/addLocation"
var l *quartermaster.Location
err := json.NewDecoder(r.Body).Decode(&l)
if err != nil {
return errors.E(method, errors.Malformed, "failed to decode location request", err)
}
if l.Name == "" || l.Description == "" {
return errors.E(method, errors.Malformed, "name and description are required")
}
if l.Parent != nil {
_, err := services.LocationService.Location(l.Parent.ID, u)
if err != nil {
return errors.E(method, errors.Malformed, "parent does not exist", err)
}
}
l, err = services.LocationService.AddLocation(l, u)
if err != nil {
return errors.E(method, errors.Internal, err)
}
lJson, err := json.Marshal(l)
if err != nil {
return errors.E(method, errors.Internal, "error marshalling location", err)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
w.Write(lJson)
return nil
}
func getTopLocations(w http.ResponseWriter, r *http.Request, u *quartermaster.User) error {
const method = "locations/getTopLocations"
locations, err := services.LocationService.GetTopLocations(u)
if err != nil {
return errors.E(method, errors.Internal, "error getting locations", err)
}
locationsJson, err := json.Marshal(locations)
if err != nil {
return errors.E(method, errors.Internal, "error marshalling locations", err)
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(locationsJson)
return nil
}

@ -13,6 +13,7 @@ import (
"golang.org/x/crypto/bcrypt"
)
// TODO: refactor login and refresh endpoints so we only have to import this in the internal.utils package
var jwtKey = []byte(os.Getenv("JWT_KEY"))
func loginHandler(w http.ResponseWriter, r *http.Request) error {

Loading…
Cancel
Save