not tested
This commit is contained in:
parent
8b3aad50b0
commit
2a566ae019
3
Makefile
3
Makefile
@ -21,3 +21,6 @@ check-build:
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BINDIR)
|
rm -rf $(BINDIR)
|
||||||
|
|
||||||
|
run: check-build
|
||||||
|
$(BIN)
|
||||||
|
BIN
cmd/go-jsonapi-example/go-jsonapi-example.exe
Normal file
BIN
cmd/go-jsonapi-example/go-jsonapi-example.exe
Normal file
Binary file not shown.
@ -17,20 +17,51 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"go-jsonapi-example/internal/model"
|
"go-jsonapi-example/internal/model"
|
||||||
"go-jsonapi-example/internal/resource"
|
"go-jsonapi-example/internal/resource"
|
||||||
"go-jsonapi-example/internal/storage"
|
"go-jsonapi-example/internal/storage"
|
||||||
|
|
||||||
"github.com/manyminds/api2go"
|
"github.com/manyminds/api2go"
|
||||||
|
|
||||||
|
"github.com/juju/gnuflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
port := 8080
|
host := gnuflag.String("host", "localhost", "host")
|
||||||
baseURL := fmt.Sprintf("http://localhost:%d", port)
|
port := gnuflag.Int("port", 8080, "port")
|
||||||
api := api2go.NewAPIWithBaseURL("v1", baseURL)
|
gnuflag.Parse(true)
|
||||||
carStorage := storage.NewCarStorage()
|
|
||||||
api.AddResource(model.Car{}, resource.CarResource{CarStorage: carStorage})
|
|
||||||
|
|
||||||
fmt.Printf("Listening on :%d", port)
|
addr := fmt.Sprintf("%s:%d", *host, *port)
|
||||||
http.ListenAndServe(fmt.Sprintf(":%d", port), api.Handler())
|
baseURL := fmt.Sprintf("http://%s", addr)
|
||||||
|
|
||||||
|
api := api2go.NewAPIWithBaseURL("v1", baseURL)
|
||||||
|
api.AddResource(model.Car{}, resource.CarResource{CarStorage: storage.NewCarStorage()})
|
||||||
|
|
||||||
|
server := &http.Server{Addr: addr, Handler: api.Handler()}
|
||||||
|
closeHandler(server)
|
||||||
|
|
||||||
|
fmt.Printf("Listening on %s\n", addr)
|
||||||
|
if err := server.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
|
fmt.Printf("error: %s\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeHandler(server *http.Server) {
|
||||||
|
interrupt := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
|
||||||
|
go func() {
|
||||||
|
<-interrupt
|
||||||
|
if err := server.Shutdown(context.Background()); err != nil {
|
||||||
|
fmt.Printf("error: %s\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -4,5 +4,6 @@ go 1.14
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d
|
||||||
github.com/manyminds/api2go v0.0.0-20210211132652-5457038544fa
|
github.com/manyminds/api2go v0.0.0-20210211132652-5457038544fa
|
||||||
)
|
)
|
||||||
|
2
go.sum
2
go.sum
@ -28,6 +28,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
|||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5jSxyOJmFHuzcihnslQiX8Urwo=
|
||||||
|
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||||
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
|
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
|
||||||
|
@ -3,6 +3,9 @@ package model
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/manyminds/api2go"
|
||||||
"github.com/manyminds/api2go/jsonapi"
|
"github.com/manyminds/api2go/jsonapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,47 +13,97 @@ type Car struct {
|
|||||||
ID string `json:"-"`
|
ID string `json:"-"`
|
||||||
Brand string `json:"brand"`
|
Brand string `json:"brand"`
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
Price uint `json:"price"`
|
Price uint64 `json:"price"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"` // OnTheWay, InStock, Sold, Discontinued
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Car) Verify() (bool, api2go.HTTPError) {
|
||||||
|
var verifyErrors []api2go.Error
|
||||||
|
var httpError api2go.HTTPError
|
||||||
|
|
||||||
|
if c.Brand == "" {
|
||||||
|
newErr := newVerifyError(
|
||||||
|
"Invalid Attribute",
|
||||||
|
"attribute cannot be empty",
|
||||||
|
"/data/attributes/brand")
|
||||||
|
verifyErrors = append(verifyErrors, newErr)
|
||||||
|
}
|
||||||
|
if c.Model == "" {
|
||||||
|
newErr := newVerifyError(
|
||||||
|
"Invalid Attribute",
|
||||||
|
"attribute cannot be empty",
|
||||||
|
"/data/attributes/model")
|
||||||
|
verifyErrors = append(verifyErrors, newErr)
|
||||||
|
}
|
||||||
|
if c.Status != "OnTheWay" &&
|
||||||
|
c.Status != "InStock" &&
|
||||||
|
c.Status != "Sold" &&
|
||||||
|
c.Status != "Discontinued" {
|
||||||
|
newErr := newVerifyError(
|
||||||
|
"Invalid Attribute",
|
||||||
|
"attribute must be one of: OnTheWay, InStock, Sold, Discontinued",
|
||||||
|
"/data/attributes/brand")
|
||||||
|
verifyErrors = append(verifyErrors, newErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := len(verifyErrors) == 0
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
httpError := api2go.NewHTTPError(
|
||||||
|
errors.New("Invalid content"),
|
||||||
|
"Invalid content",
|
||||||
|
http.StatusBadRequest)
|
||||||
|
httpError.Errors = verifyErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok, httpError
|
||||||
|
}
|
||||||
|
|
||||||
|
func newVerifyError(title string, detail string, pointer string) api2go.Error {
|
||||||
|
var newError api2go.Error
|
||||||
|
newError.Title = title
|
||||||
|
newError.Detail = detail
|
||||||
|
newError.Source = &api2go.ErrorSource{Pointer: pointer}
|
||||||
|
return newError
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetID to satisfy jsonapi.MarshalIdentifier interface
|
// GetID to satisfy jsonapi.MarshalIdentifier interface
|
||||||
func (u Car) GetID() string {
|
func (c Car) GetID() string {
|
||||||
return u.ID
|
return c.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetID to satisfy jsonapi.UnmarshalIdentifier interface
|
// SetID to satisfy jsonapi.UnmarshalIdentifier interface
|
||||||
func (u *Car) SetID(id string) error {
|
func (c *Car) SetID(id string) error {
|
||||||
u.ID = id
|
c.ID = id
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReferences to satisfy the jsonapi.MarshalReferences interface
|
// GetReferences to satisfy the jsonapi.MarshalReferences interface
|
||||||
func (u Car) GetReferences() []jsonapi.Reference {
|
func (c Car) GetReferences() []jsonapi.Reference {
|
||||||
return []jsonapi.Reference{}
|
return []jsonapi.Reference{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReferencedIDs to satisfy the jsonapi.MarshalLinkedRelations interface
|
// GetReferencedIDs to satisfy the jsonapi.MarshalLinkedRelations interface
|
||||||
func (u Car) GetReferencedIDs() []jsonapi.ReferenceID {
|
func (c Car) GetReferencedIDs() []jsonapi.ReferenceID {
|
||||||
return []jsonapi.ReferenceID{}
|
return []jsonapi.ReferenceID{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReferencedStructs to satisfy the jsonapi.MarhsalIncludedRelations interface
|
// GetReferencedStructs to satisfy the jsonapi.MarhsalIncludedRelations interface
|
||||||
func (u Car) GetReferencedStructs() []jsonapi.MarshalIdentifier {
|
func (c Car) GetReferencedStructs() []jsonapi.MarshalIdentifier {
|
||||||
return []jsonapi.MarshalIdentifier{}
|
return []jsonapi.MarshalIdentifier{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetToManyReferenceIDs sets the sweets reference IDs and satisfies the jsonapi.UnmarshalToManyRelations interface
|
// SetToManyReferenceIDs sets the sweets reference IDs and satisfies the jsonapi.UnmarshalToManyRelations interface
|
||||||
func (u *Car) SetToManyReferenceIDs(name string, IDs []string) error {
|
func (c *Car) SetToManyReferenceIDs(name string, IDs []string) error {
|
||||||
return errors.New("There is no to-many relationship with the name " + name)
|
return errors.New("There is no to-many relationship with the name " + name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddToManyIDs adds some new sweets that a users loves so much
|
// AddToManyIDs adds some new sweets that a users loves so much
|
||||||
func (u *Car) AddToManyIDs(name string, IDs []string) error {
|
func (c *Car) AddToManyIDs(name string, IDs []string) error {
|
||||||
return errors.New("There is no to-many relationship with the name " + name)
|
return errors.New("There is no to-many relationship with the name " + name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteToManyIDs removes some sweets from a users because they made him very sick
|
// DeleteToManyIDs removes some sweets from a users because they made him very sick
|
||||||
func (u *Car) DeleteToManyIDs(name string, IDs []string) error {
|
func (c *Car) DeleteToManyIDs(name string, IDs []string) error {
|
||||||
return errors.New("There is no to-many relationship with the name " + name)
|
return errors.New("There is no to-many relationship with the name " + name)
|
||||||
}
|
}
|
||||||
|
@ -122,6 +122,10 @@ func (s CarResource) Create(obj interface{}, r api2go.Request) (api2go.Responder
|
|||||||
return &Response{}, api2go.NewHTTPError(errors.New("Invalid instance given"), "Invalid instance given", http.StatusBadRequest)
|
return &Response{}, api2go.NewHTTPError(errors.New("Invalid instance given"), "Invalid instance given", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ok, httpErr := car.Verify(); !ok {
|
||||||
|
return &Response{}, httpErr
|
||||||
|
}
|
||||||
|
|
||||||
id := s.CarStorage.Insert(car)
|
id := s.CarStorage.Insert(car)
|
||||||
car.ID = id
|
car.ID = id
|
||||||
|
|
||||||
@ -141,6 +145,10 @@ func (s CarResource) Update(obj interface{}, r api2go.Request) (api2go.Responder
|
|||||||
return &Response{}, api2go.NewHTTPError(errors.New("Invalid instance given"), "Invalid instance given", http.StatusBadRequest)
|
return &Response{}, api2go.NewHTTPError(errors.New("Invalid instance given"), "Invalid instance given", http.StatusBadRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ok, httpErr := car.Verify(); !ok {
|
||||||
|
return &Response{}, httpErr
|
||||||
|
}
|
||||||
|
|
||||||
err := s.CarStorage.Update(car)
|
err := s.CarStorage.Update(car)
|
||||||
return &Response{Res: car, Code: http.StatusNoContent}, err
|
return &Response{Code: http.StatusOK}, err
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,7 @@ type Response struct {
|
|||||||
|
|
||||||
func (r Response) Metadata() map[string]interface{} {
|
func (r Response) Metadata() map[string]interface{} {
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"author": "GenZmeY",
|
"author": "GenZmeY",
|
||||||
"license": "wtfpl",
|
|
||||||
"license-url": "http://www.wtfpl.net",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user