diff --git a/internal/model/model_car.go b/internal/model/model_car.go index 075bd25..9449792 100644 --- a/internal/model/model_car.go +++ b/internal/model/model_car.go @@ -3,14 +3,16 @@ package model import ( "errors" + "fmt" "net/http" + "strconv" "github.com/manyminds/api2go" "github.com/manyminds/api2go/jsonapi" ) type Car struct { - ID string `json:"-"` + ID uint64 `json:"-"` Brand string `json:"brand"` Model string `json:"model"` Price uint64 `json:"price"` @@ -70,12 +72,16 @@ func newVerifyError(title string, detail string, pointer string) api2go.Error { // GetID to satisfy jsonapi.MarshalIdentifier interface func (c Car) GetID() string { - return c.ID + return fmt.Sprintf("%d", c.ID) } // SetID to satisfy jsonapi.UnmarshalIdentifier interface func (c *Car) SetID(id string) error { - c.ID = id + intID, err := strconv.ParseUint(id, 10, 64) + if err != nil { + return err + } + c.ID = intID return nil } diff --git a/internal/resource/resource_car.go b/internal/resource/resource_car.go index b264c29..3f6ab08 100644 --- a/internal/resource/resource_car.go +++ b/internal/resource/resource_car.go @@ -3,7 +3,7 @@ package resource import ( "errors" "net/http" - "sort" + _ "sort" "strconv" "go-jsonapi-example/internal/model" @@ -19,17 +19,11 @@ type CarResource struct { // FindAll to satisfy api2go data source interface func (s CarResource) FindAll(r api2go.Request) (api2go.Responder, error) { - cars := s.CarStorage.GetAll() - result := make([]model.Car, 0, len(cars)) - - for _, car := range cars { - result = append(result, *car) - } - - return &Response{Res: result}, nil + return &Response{Res: s.CarStorage.GetAll()}, nil } // PaginatedFindAll can be used to load cars in chunks +/* func (s CarResource) PaginatedFindAll(r api2go.Request) (uint, api2go.Responder, error) { var ( result []model.Car @@ -104,10 +98,15 @@ func (s CarResource) PaginatedFindAll(r api2go.Request) (uint, api2go.Responder, return uint(len(cars)), &Response{Res: result}, nil } - +*/ // FindOne to satisfy `api2go.DataSource` interface func (s CarResource) FindOne(ID string, r api2go.Request) (api2go.Responder, error) { - car, err := s.CarStorage.GetOne(ID) + intID, err := strconv.ParseUint(ID, 10, 64) + if err != nil { + return &Response{}, api2go.NewHTTPError(err, err.Error(), http.StatusNotFound) + } + + car, err := s.CarStorage.GetOne(intID) if err != nil { return &Response{}, api2go.NewHTTPError(err, err.Error(), http.StatusNotFound) } @@ -133,7 +132,10 @@ func (s CarResource) Create(obj interface{}, r api2go.Request) (api2go.Responder // Delete to satisfy `api2go.DataSource` interface func (s CarResource) Delete(id string, r api2go.Request) (api2go.Responder, error) { - err := s.CarStorage.Delete(id) + intID, err := strconv.ParseUint(id, 10, 64) + if err == nil { + err = s.CarStorage.Delete(intID) + } return &Response{Code: http.StatusNoContent}, err } diff --git a/internal/storage/storage_car.go b/internal/storage/storage_car.go index 2a9929d..f6264d4 100644 --- a/internal/storage/storage_car.go +++ b/internal/storage/storage_car.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/http" + "sync" "go-jsonapi-example/internal/model" @@ -11,36 +12,55 @@ import ( ) type CarStorage struct { - cars map[string]*model.Car - idCount int + mutex sync.RWMutex + cars map[uint64]*model.Car + idCount uint64 } +type Cars []model.Car + +func (c Cars) Len() int { return len(c) } +func (c Cars) Swap(i, j int) { c[i], c[j] = c[j], c[i] } +func (c Cars) Less(i, j int) bool { return c[i].ID < c[j].ID } + func NewCarStorage() *CarStorage { - return &CarStorage{make(map[string]*model.Car), 1} + return &CarStorage{cars: make(map[uint64]*model.Car), idCount: 1} } -func (s CarStorage) GetAll() map[string]*model.Car { - return s.cars -} - -func (s CarStorage) GetOne(id string) (model.Car, error) { - user, ok := s.cars[id] - if ok { - return *user, nil +func (s CarStorage) GetAll() Cars { + s.mutex.RLock() + defer s.mutex.RUnlock() + result := make(Cars, 0, len(s.cars)) + for _, car := range s.cars { + result = append(result, *car) } + return result +} + +func (s CarStorage) GetOne(id uint64) (model.Car, error) { + s.mutex.RLock() + car, ok := s.cars[id] + if ok { + defer s.mutex.RUnlock() + return *car, nil + } + s.mutex.RUnlock() errMessage := fmt.Sprintf("Car for id %s not found", id) return model.Car{}, api2go.NewHTTPError(errors.New(errMessage), errMessage, http.StatusNotFound) } -func (s *CarStorage) Insert(c model.Car) string { - id := fmt.Sprintf("%d", s.idCount) - c.ID = id - s.cars[id] = &c +func (s *CarStorage) Insert(c model.Car) uint64 { + s.mutex.Lock() + defer s.mutex.Unlock() + c.ID = s.idCount + s.cars[s.idCount] = &c s.idCount++ - return id + return c.ID } -func (s *CarStorage) Delete(id string) error { +func (s *CarStorage) Delete(id uint64) error { + s.mutex.Lock() + defer s.mutex.Unlock() _, exists := s.cars[id] if !exists { return fmt.Errorf("Car with id %s does not exist", id) @@ -51,6 +71,8 @@ func (s *CarStorage) Delete(id string) error { } func (s *CarStorage) Update(c model.Car) error { + s.mutex.Lock() + defer s.mutex.Unlock() _, exists := s.cars[c.ID] if !exists { return fmt.Errorf("Car with id %s does not exist", c.ID)