From 8b3aad50b087d6584eae488e3f7cb4e621c7a5c5 Mon Sep 17 00:00:00 2001 From: GenZmeY Date: Thu, 18 Mar 2021 21:52:41 +0300 Subject: [PATCH] work in progress --- .gitignore | 2 + LICENSE | 29 ++---- Makefile | 23 +++++ cmd/go-jsonapi-example/main.go | 36 ++++++++ go.mod | 8 ++ go.sum | 103 +++++++++++++++++++++ internal/model/model_car.go | 56 ++++++++++++ internal/resource/resource_car.go | 146 ++++++++++++++++++++++++++++++ internal/resource/response.go | 23 +++++ internal/storage/storage_car.go | 61 +++++++++++++ 10 files changed, 467 insertions(+), 20 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 cmd/go-jsonapi-example/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/model/model_car.go create mode 100644 internal/resource/resource_car.go create mode 100644 internal/resource/response.go create mode 100644 internal/storage/storage_car.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3bbad19 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin/ +/cmd/multini/multini diff --git a/LICENSE b/LICENSE index fdddb29..d1df151 100644 --- a/LICENSE +++ b/LICENSE @@ -1,24 +1,13 @@ -This is free and unencumbered software released into the public domain. + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. + Copyright (C) 2021 GenZmeY -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION -For more information, please refer to + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..73e99c0 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +NAME = go-jsonapi-example +GOCMD = go +GOBUILD = $(GOCMD) build +SRCMAIN = ./cmd/$(NAME) +BINDIR = bin +BIN = $(BINDIR)/$(NAME) + +.PHONY: all prep build check-build clean + +all: build + +prep: clean + go mod init $(NAME); go mod tidy + mkdir $(BINDIR) + +build: prep + $(GOBUILD) -o $(BIN) $(SRCMAIN) + +check-build: + test -e $(BIN) + +clean: + rm -rf $(BINDIR) diff --git a/cmd/go-jsonapi-example/main.go b/cmd/go-jsonapi-example/main.go new file mode 100644 index 0000000..aa6e5f9 --- /dev/null +++ b/cmd/go-jsonapi-example/main.go @@ -0,0 +1,36 @@ +/* +Разработать CRUD (REST API) для модели автомобиля, который имеет следующие поля: + +1. Уникальный идентификатор (любой тип, общение с БД не является критерием чего-либо, можно сделать и in-memory хранилище на время жизни сервиса) +2. Бренд автомобиля (текст) +3. Модель автомобиля (текст) +4. Цена автомобиля (целое, не может быть меньше 0) +5. Статус автомобиля (В пути, На складе, Продан, Снят с продажи) +6. Пробег (целое) + +Формат ответа api - json api (https://jsonapi.org/) +*/ + +package main + +import ( + "fmt" + "net/http" + + "go-jsonapi-example/internal/model" + "go-jsonapi-example/internal/resource" + "go-jsonapi-example/internal/storage" + + "github.com/manyminds/api2go" +) + +func main() { + port := 8080 + baseURL := fmt.Sprintf("http://localhost:%d", port) + api := api2go.NewAPIWithBaseURL("v1", baseURL) + carStorage := storage.NewCarStorage() + api.AddResource(model.Car{}, resource.CarResource{CarStorage: carStorage}) + + fmt.Printf("Listening on :%d", port) + http.ListenAndServe(fmt.Sprintf(":%d", port), api.Handler()) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..58c72b6 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module go-jsonapi-example + +go 1.14 + +require ( + github.com/gorilla/mux v1.8.0 // indirect + github.com/manyminds/api2go v0.0.0-20210211132652-5457038544fa +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3cf8ac6 --- /dev/null +++ b/go.sum @@ -0,0 +1,103 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 h1:Uc+IZ7gYqAf/rSGFplbWBSHaGolEQlNLgMgSE3ccnIQ= +github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813/go.mod h1:P+oSoE9yhSRvsmYyZsshflcR6ePWYLql6UU1amW13IM= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= +github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +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/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +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/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/manyminds/api2go v0.0.0-20210211132652-5457038544fa h1:rqhEZu3Xpi8KZauuX9f2ZWxu+5NlNJbomtKhgZzHHck= +github.com/manyminds/api2go v0.0.0-20210211132652-5457038544fa/go.mod h1:7X0DU7oiyM2xvWleZb5baM1LxOUHtQvaC1Swq0yYUt4= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 h1:fpnn/HnJONpIu6hkXi1u/7rR0NzilgWr4T0JmWkEitk= +golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/guregu/null.v3 v3.4.0 h1:AOpMtZ85uElRhQjEDsFx21BkXqFPwA7uoJukd4KErIs= +gopkg.in/guregu/null.v3 v3.4.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/model/model_car.go b/internal/model/model_car.go new file mode 100644 index 0000000..0d80875 --- /dev/null +++ b/internal/model/model_car.go @@ -0,0 +1,56 @@ +package model + +import ( + "errors" + + "github.com/manyminds/api2go/jsonapi" +) + +type Car struct { + ID string `json:"-"` + Brand string `json:"brand"` + Model string `json:"model"` + Price uint `json:"price"` + Status string `json:"status"` +} + +// GetID to satisfy jsonapi.MarshalIdentifier interface +func (u Car) GetID() string { + return u.ID +} + +// SetID to satisfy jsonapi.UnmarshalIdentifier interface +func (u *Car) SetID(id string) error { + u.ID = id + return nil +} + +// GetReferences to satisfy the jsonapi.MarshalReferences interface +func (u Car) GetReferences() []jsonapi.Reference { + return []jsonapi.Reference{} +} + +// GetReferencedIDs to satisfy the jsonapi.MarshalLinkedRelations interface +func (u Car) GetReferencedIDs() []jsonapi.ReferenceID { + return []jsonapi.ReferenceID{} +} + +// GetReferencedStructs to satisfy the jsonapi.MarhsalIncludedRelations interface +func (u Car) GetReferencedStructs() []jsonapi.MarshalIdentifier { + return []jsonapi.MarshalIdentifier{} +} + +// SetToManyReferenceIDs sets the sweets reference IDs and satisfies the jsonapi.UnmarshalToManyRelations interface +func (u *Car) SetToManyReferenceIDs(name string, IDs []string) error { + return errors.New("There is no to-many relationship with the name " + name) +} + +// AddToManyIDs adds some new sweets that a users loves so much +func (u *Car) AddToManyIDs(name string, IDs []string) error { + 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 +func (u *Car) DeleteToManyIDs(name string, IDs []string) error { + return errors.New("There is no to-many relationship with the name " + name) +} diff --git a/internal/resource/resource_car.go b/internal/resource/resource_car.go new file mode 100644 index 0000000..f95dced --- /dev/null +++ b/internal/resource/resource_car.go @@ -0,0 +1,146 @@ +package resource + +import ( + "errors" + "net/http" + "sort" + "strconv" + + "go-jsonapi-example/internal/model" + "go-jsonapi-example/internal/storage" + + "github.com/manyminds/api2go" +) + +// CarResource for api2go routes +type CarResource struct { + CarStorage *storage.CarStorage +} + +// FindAll to satisfy api2go data source interface +func (s CarResource) FindAll(r api2go.Request) (api2go.Responder, error) { + var result []model.Car + cars := s.CarStorage.GetAll() + + for _, car := range cars { + result = append(result, *car) + } + + return &Response{Res: result}, 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 + number, size, offset, limit string + keys []int + ) + cars := s.CarStorage.GetAll() + + for k := range cars { + i, err := strconv.ParseInt(k, 10, 64) + if err != nil { + return 0, &Response{}, err + } + + keys = append(keys, int(i)) + } + sort.Ints(keys) + + numberQuery, ok := r.QueryParams["page[number]"] + if ok { + number = numberQuery[0] + } + sizeQuery, ok := r.QueryParams["page[size]"] + if ok { + size = sizeQuery[0] + } + offsetQuery, ok := r.QueryParams["page[offset]"] + if ok { + offset = offsetQuery[0] + } + limitQuery, ok := r.QueryParams["page[limit]"] + if ok { + limit = limitQuery[0] + } + + if size != "" { + sizeI, err := strconv.ParseUint(size, 10, 64) + if err != nil { + return 0, &Response{}, err + } + + numberI, err := strconv.ParseUint(number, 10, 64) + if err != nil { + return 0, &Response{}, err + } + + start := sizeI * (numberI - 1) + for i := start; i < start+sizeI; i++ { + if i >= uint64(len(cars)) { + break + } + result = append(result, *cars[strconv.FormatInt(int64(keys[i]), 10)]) + } + } else { + limitI, err := strconv.ParseUint(limit, 10, 64) + if err != nil { + return 0, &Response{}, err + } + + offsetI, err := strconv.ParseUint(offset, 10, 64) + if err != nil { + return 0, &Response{}, err + } + + for i := offsetI; i < offsetI+limitI; i++ { + if i >= uint64(len(cars)) { + break + } + result = append(result, *cars[strconv.FormatInt(int64(keys[i]), 10)]) + } + } + + return uint(len(cars)), &Response{Res: result}, nil +} + +// FindOne to satisfy `api2go.DataSource` interface +// this method should return the car with the given ID, otherwise an error +func (s CarResource) FindOne(ID string, r api2go.Request) (api2go.Responder, error) { + car, err := s.CarStorage.GetOne(ID) + if err != nil { + return &Response{}, api2go.NewHTTPError(err, err.Error(), http.StatusNotFound) + } + return &Response{Res: car}, nil +} + +// Create method to satisfy `api2go.DataSource` interface +func (s CarResource) Create(obj interface{}, r api2go.Request) (api2go.Responder, error) { + car, ok := obj.(model.Car) + if !ok { + return &Response{}, api2go.NewHTTPError(errors.New("Invalid instance given"), "Invalid instance given", http.StatusBadRequest) + } + + id := s.CarStorage.Insert(car) + car.ID = id + + return &Response{Res: car, Code: http.StatusCreated}, nil +} + +// Delete to satisfy `api2go.DataSource` interface +func (s CarResource) Delete(id string, r api2go.Request) (api2go.Responder, error) { + err := s.CarStorage.Delete(id) + return &Response{Code: http.StatusNoContent}, err +} + +//Update stores all changes on the car +func (s CarResource) Update(obj interface{}, r api2go.Request) (api2go.Responder, error) { + car, ok := obj.(model.Car) + if !ok { + return &Response{}, api2go.NewHTTPError(errors.New("Invalid instance given"), "Invalid instance given", http.StatusBadRequest) + } + + err := s.CarStorage.Update(car) + return &Response{Res: car, Code: http.StatusNoContent}, err +} diff --git a/internal/resource/response.go b/internal/resource/response.go new file mode 100644 index 0000000..d7557af --- /dev/null +++ b/internal/resource/response.go @@ -0,0 +1,23 @@ +package resource + +// The Response struct implements api2go.Responder +type Response struct { + Res interface{} + Code int +} + +func (r Response) Metadata() map[string]interface{} { + return map[string]interface{}{ + "author": "GenZmeY", + "license": "wtfpl", + "license-url": "http://www.wtfpl.net", + } +} + +func (r Response) Result() interface{} { + return r.Res +} + +func (r Response) StatusCode() int { + return r.Code +} diff --git a/internal/storage/storage_car.go b/internal/storage/storage_car.go new file mode 100644 index 0000000..2a9929d --- /dev/null +++ b/internal/storage/storage_car.go @@ -0,0 +1,61 @@ +package storage + +import ( + "errors" + "fmt" + "net/http" + + "go-jsonapi-example/internal/model" + + "github.com/manyminds/api2go" +) + +type CarStorage struct { + cars map[string]*model.Car + idCount int +} + +func NewCarStorage() *CarStorage { + return &CarStorage{make(map[string]*model.Car), 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 + } + 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 + s.idCount++ + return id +} + +func (s *CarStorage) Delete(id string) error { + _, exists := s.cars[id] + if !exists { + return fmt.Errorf("Car with id %s does not exist", id) + } + delete(s.cars, id) + + return nil +} + +func (s *CarStorage) Update(c model.Car) error { + _, exists := s.cars[c.ID] + if !exists { + return fmt.Errorf("Car with id %s does not exist", c.ID) + } + s.cars[c.ID] = &c + + return nil +}