You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

350 lines
7.8 KiB

package api
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"strconv"
"git.tovijaeschke.xyz/tovi/JumboPetstore/database"
"git.tovijaeschke.xyz/tovi/JumboPetstore/models"
schema "github.com/Kangaroux/go-map-schema"
"github.com/gorilla/mux"
)
func verifyStatus(status string) error {
if status != "available" && status != "pending" && status != "sold" {
return errors.New("Invalid status")
}
return nil
}
func deserialsePetJson(data []byte) (models.Pet, error) {
var (
petData models.Pet = models.Pet{}
jsonStructureTest map[string]interface{} = make(map[string]interface{})
jsonStructureTestResults *schema.CompareResults
err error
)
// Verify the JSON has the correct structure
json.Unmarshal(data, &jsonStructureTest)
jsonStructureTestResults, err = schema.CompareMapToStruct(
&petData,
jsonStructureTest,
&schema.CompareOpts{
ConvertibleFunc: CanConvert,
TypeNameFunc: schema.DetailedTypeName,
})
if err != nil {
return petData, err
}
if len(jsonStructureTestResults.MismatchedFields) > 0 {
return petData, errors.New(fmt.Sprintf(
"MismatchedFields found when deserializing data, %s",
jsonStructureTestResults.Errors().Error(),
))
}
if len(jsonStructureTestResults.MissingFields) > 0 {
return petData, errors.New(fmt.Sprintf(
"MissingFields found when deserializing data, %s",
jsonStructureTestResults.Errors().Error(),
))
}
// Deserialize the JSON into the struct
err = json.Unmarshal(data, &petData)
if err != nil {
return petData, err
}
err = verifyStatus(petData.Status)
return petData, err
}
func genericJsonReturn(w http.ResponseWriter, code int, msg, typ string) {
var (
responseJsonMap map[string]interface{}
responseJson []byte
err error
)
responseJsonMap = make(map[string]interface{})
w.WriteHeader(code)
responseJsonMap["code"] = code
responseJsonMap["type"] = typ
responseJsonMap["message"] = msg
responseJson, err = json.MarshalIndent(responseJsonMap, "", " ")
if err != nil {
log.Printf("Error occured creating response: %s\n", err.Error())
}
w.Write(responseJson)
}
func PetHandlerCreateUpdate(w http.ResponseWriter, r *http.Request) {
var (
requestBody []byte
petData models.Pet
returnJson []byte
err error
)
log.Printf("Pet handler recieved %s request", r.Method)
requestBody, err = ioutil.ReadAll(r.Body)
if err != nil {
log.Printf("Error encountered reading POST body: %s\n", err.Error())
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
petData, err = deserialsePetJson(requestBody)
if err != nil {
log.Printf("Invalid data provided to pet POST API: %s\n", err.Error())
genericJsonReturn(w, 405, "Invalid data", "unknown")
return
}
switch r.Method {
case "POST":
err = database.CreatePet(&petData)
if err != nil {
genericJsonReturn(w, 405, "Invalid data", "unknown")
}
break
case "PUT":
err = database.UpdatePet(&petData)
if err != nil {
genericJsonReturn(w, 405, "Invalid data", "unknown")
}
break
}
returnJson, err = json.MarshalIndent(petData, "", " ")
if err != nil {
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
// Return updated json
w.WriteHeader(http.StatusOK)
w.Write(returnJson)
}
func getPetDataById(w http.ResponseWriter, r *http.Request) (models.Pet, bool) {
var (
petData models.Pet
urlVars map[string]string
idStr string
id int
ok bool
err error
)
urlVars = mux.Vars(r)
idStr, ok = urlVars["petId"]
if !ok {
log.Printf("Error encountered getting id\n")
genericJsonReturn(w, 500, "An error occured", "unknown")
return petData, false
}
id, err = strconv.Atoi(idStr)
if err != nil {
log.Printf("Error encountered converting id to string: %s\n", err.Error())
genericJsonReturn(w, 500, "An error occured", "unknown")
return petData, false
}
petData = database.GetPetById(id)
if petData.Id == 0 {
log.Printf("Could not find pet with id %d\n", id)
genericJsonReturn(w, 404, "Not found", "unknown")
return petData, false
}
return petData, true
}
func GetPetHandler(w http.ResponseWriter, r *http.Request) {
var (
petData models.Pet
returnJson []byte
ok bool
err error
)
petData, ok = getPetDataById(w, r)
if !ok {
return
}
returnJson, err = json.MarshalIndent(petData, "", " ")
if err != nil {
log.Printf("Error encountered when creating pet record: %s\n", err.Error())
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
// Return updated json
w.WriteHeader(http.StatusOK)
w.Write(returnJson)
}
func UpdatePetHandler(w http.ResponseWriter, r *http.Request) {
var (
petData models.Pet
ok bool
err error
)
petData, ok = getPetDataById(w, r)
if !ok {
return
}
err = r.ParseForm()
if err != nil {
log.Printf("Error encountered parsing form: %s\n", err.Error())
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
if r.FormValue("name") != "" {
petData.Name = r.FormValue("name")
}
if r.FormValue("status") != "" {
petData.Status = r.FormValue("status")
}
err = database.UpdatePet(&petData)
if err != nil {
log.Printf("Error updating pet: %s\n", err.Error())
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
}
func DeletePetHandler(w http.ResponseWriter, r *http.Request) {
var (
petData models.Pet
id_str string
ok bool
)
petData, ok = getPetDataById(w, r)
if !ok {
return
}
log.Printf("Marking pet %d as deleted\n", petData.Id)
database.DeletePet(petData)
id_str = strconv.Itoa(petData.Id)
genericJsonReturn(w, 200, id_str, "unknown")
}
func FindByStatusPetHandler(w http.ResponseWriter, r *http.Request) {
var (
status string
petData []models.Pet
returnJson []byte
err error
)
status = r.URL.Query().Get("status")
err = verifyStatus(status)
if err != nil {
log.Printf("Invalid status provided to /pet/findByStatus")
genericJsonReturn(w, 422, "Invalid status", "unknown")
return
}
petData, err = database.GetPetsByStatus(status)
if err != nil {
log.Printf("An error occured in GetPetsByStatus")
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
returnJson, err = json.MarshalIndent(petData, "", " ")
if err != nil {
log.Printf("An error occured while serializing pet data to JSON")
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
w.Write(returnJson)
}
func UploadImagePetHandler(w http.ResponseWriter, r *http.Request) {
var (
petData models.Pet
additionalMetadata string
file multipart.File
handler *multipart.FileHeader
fileBytes []byte
fileName string
ok bool
err error
)
petData, ok = getPetDataById(w, r)
if !ok {
return
}
// Upload 10Mb files
r.ParseMultipartForm(10 << 20)
file, handler, err = r.FormFile("file")
if err != nil {
log.Printf("Error retrieving file: %s\n", err.Error())
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
defer file.Close()
additionalMetadata = r.FormValue("additionalMetadata")
if additionalMetadata == "" {
additionalMetadata = "null"
}
fileBytes, err = ioutil.ReadAll(file)
if err != nil {
log.Printf("An error occured reading POST file: %s\n", err.Error())
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
fileName, err = database.AddPhotoToPet(&petData, handler.Filename, additionalMetadata, fileBytes)
if err != nil {
log.Printf("An error occured adding file to database: %s\n", err.Error())
genericJsonReturn(w, 500, "An error occured", "unknown")
return
}
genericJsonReturn(
w,
200,
fmt.Sprintf(
"additionalMetadata: %s\nFile uploaded to %s, %d bytes",
additionalMetadata,
fileName,
handler.Size,
),
"unknown",
)
}