Check the Go documentation to download and install.
$> go get github.com/iancoleman/strcase
$> go get github.com/jinzhu/inflection
$> go get github.com/unity26org/wheel
$> cd GOPATH/src/github.com/unity26org/wheel
$> go build -o wheel main.go
$> sudo mv wheel /usr/bin
Quick tip!
In the example above, GOPATH is where the Go packages and sources are installed. The executable file wheel was moved to /usr/bin. But feel free to set it up to any directory you want. Just add the path to your .profile, as you can see below:
export PATH=$PATH:YOUR_DESIRED_PATH
Wheel has only two options: new and generate.
Check help for more details.
wheel --help
Let's create an API for a car catalog.
In this example Let's assume that we are working on the path github.com/account_name/car_catalog. Where github.com means the code repository system, account_name is your account name on github.com and car_catalog is the name of the application.
$> wheel new github.com/account_name/car_catalog
It will output something like this:
"Go" seems installed Checking dependences... ... Generating new app... created: GOPATH/src/github.com/account_name/car_catalog ... Your RESTful API was successfully created! Change to the root directory using the command line below: cd GOPATH/src/github.com/account_name/car_catalog Set up your database connection modifying the file config/database.yml For more details call help: go run main.go --help
Very important!
Don't forget to go to the directory GOPATH/src/github.com/account_name/car_catalog.
And remember: GOPATH is where Go packages and sources are installed.
Quick tip!
Once you have created a new project, Wheel gives, by default, RESTful resources for session control, user management and more. To check these default resources click here.
Directory/File | Definition |
---|---|
app/ | Holds the models, views and handlers (controllers) of you application. The models and views of each resource are grouped inside the same directory. All handlers are together inside the directory "handlers". |
commons/ | Contains internals functions for log, send mail, locale, cryptograph and type convertors. Also, inside the directory app, it has commons functions for the models, handlers and views. |
config/ | Configure your API, database and send mail. |
db/ | Manages the database schema and contains all information about its entities. |
main.go | The starting point. |
routes/ | Contains the routes, middleware and authorization functionalities. |
Configure your application.
Currently, Wheel has support only for Postgresql. Edit config/database.yml and set up your database connection.
Edit config/email.yml and set up with your send mail account.
Edit config/app.yml and set the following options:
Item | Definition |
---|---|
app_name | Your app name |
app_repository | Repository name |
frontend_base_url | URL to be used on your frontend |
secret_key | Key to encrypt passwords on database |
reset_password_expiration_seconds | Expiration time just after password reset functionality is requested |
token_expiration_seconds | Access token expiration time just after sign in |
locales | List of available locales |
Available locales files that contains words and phrases for internacionalization. You can add your own locales files, but remember to add to config/app.yml configuration file first.
Very important!
Before running, you must be sure your database schema is up to date, that's why you need to run the migrate mode.
$> go run main.go -mode=migrate
Quick tip!
To learn more about the migration functionality check its section clicking here.
Run:
$> go run main.go
Now go to http://localhost:8081 and you'll see:
{
"system_message": {
"type": "notice",
"content": "Yeah! Your API is working!"
}
}
Very important!
Don't forget to call the directory where the application were generated.
$> wheel g scaffold car description:string year:integer price:decimal available:bool
And it will return something like that:
"Go" seems installed
Checking dependences...
...
Generating new CRUD...
created: app/post/car_model.go
created: app/post/car_view.go
created: db/entities/car_entity.go
created: app/handlers/car_handler.go
updated: routes/routes.go
updated: db/schema/migrate.go
updated: routes/authorize.go
Quick tip!
For more information how to create custom resources click here.
Run the migration mode to update the database schema.
$> go run main.go -mode=migrate
Don't forget run your application again.
$> go run main.go
When generates a new CRUD, Wheel adds to file routes/routes.go the cars RESTful interfaces.
router.HandleFunc("/cars", handlers.CarList).Methods("GET")
router.HandleFunc("/cars/{id}", handlers.CarShow).Methods("GET")
router.HandleFunc("/cars", handlers.CarCreate).Methods("POST")
router.HandleFunc("/cars/{id}", handlers.CarUpdate).Methods("PUT")
router.HandleFunc("/cars/{id}", handlers.CarDestroy).Methods("DELETE")
Each route is identified by a combination of a path (/cars or /cars/{id}) and a HTTP verb (GET, POST, PUT or DELETE). And this combination represents an interface to a handler function (handlers.CarCreate, handlers.CarList, handlers.CarShow, handlers.CarUpdate or handlers.CarDelete).
Very important!
{id} is the parameter that identifies uniquely a record on the database.
Quick tip!
Check the routes feature section for more details.
At this point we already have the resources to interact with the API. To make requests to the API we are going to use the curl program. If you are not confortable with command lines, we recommend you to use the software Postman. But feel free to use another similar software.
To create new car records, we need to make HTTP requests to the path /cars using the HTTP verb POST. As you can see in the previous item.
Let's build the request filling the form params with appropriated values and execute it.
curl -X POST -H 'Content-Type: application/json' \
-d '{ \
"description": "Chevrolet", \
"available": "true", \
"price": "25000.00", \
"year": "2019" \
}' \
http://localhost:8081/cars
The result will be something like this:
{
"system_message": {
"type": "notice",
"content": "car was successfully created"
},
"car": {
"id": 1,
"description": "Chevrolet",
"year": 2019,
"price": 25000.00,
"available": true,
"created_at": "2019-08-26T23:30:26.820820046Z",
"updated_at": "2019-08-26T23:30:26.820820046Z"
}
}
To list the car records, we need to make HTTP requests to the path /cars using the HTTP verb GET.
curl -X GET http://localhost:8081/cars
The result will contain the pagination information and an array of cars.
{
"pagination": {
"current_page": 1,
"total_pages": 1,
"total_entries": 1
},
"cars": [
{
"id": 1,
"description": "Chevrolet",
"year": 2019,
"price": 25000.00,
"available": true,
"created_at": "2019-08-26T23:30:26.820820046Z",
"updated_at": "2019-08-26T23:30:26.820820046Z"
},
]
}
Quick tip!
To interact with pagination you can submit the page and per_page parameters that mean the number of the page and the maximum numbers of records per page, respectively. Check the pagination feature section for more details.
To show one single car record, we need to make HTTP requests to the path /cars/{id} using the HTTP verb GET. Again, {id} is the parameter that identifies uniquely a record on the database.
curl -X GET http://localhost:8081/cars/1
The result will be something like this:
{
"id": 1,
"description": "Chevrolet",
"year": 2019,
"price": 25000.00,
"available": false,
"created_at": "2019-08-26T23:30:26.820820046Z",
"updated_at": "2019-08-26T23:30:26.820820046Z"
}
To update a car record, we need to make HTTP requests to the path /cars/{id} using the HTTP verb PUT. Again and again, {id} is the parameter that identifies uniquely a record on the database. At this example we will update only the field price and year. But you can try to update the fields description and available too. The fields id, created_at and updated_at should not be updated by this application.
curl -X PUT -H 'Content-Type: application/json' \
-d '{ \
"description": "Chevrolet", \
"available":"true", \
"price": "18500.00", \
"year":"2018" \
}' \
http://localhost:8081/cars
The result will be something like this:
{
"system_message": {
"type": "notice",
"content": "car was successfully updated"
},
"car": {
"id": 1,
"description": "Chevrolet",
"year": 2018,
"price": 18500.00,
"available": true,
"created_at": "2019-08-26T23:30:26.820820046Z",
"updated_at": "2019-08-27T00:08:26.820820046Z"
}
}
To desroy a car record, we need to make HTTP requests to the path /cars/{id} using the HTTP verb DELETE. One more time, {id} is the parameter that identifies uniquely a record on the database.
curl -X DELETE http://localhost:8081/cars/1
The result will be identical to:
{
"system_message": {
"type": "notice",
"content": "car was successfully destroyed"
}
}
Now let's see how to validate the parameters sent by the users. Open the file app/car/car_model.go and find the function IsValid. Inside it you can write the validation code.
func IsValid(car *entities.Car) (bool, []error) {
var errs []error
// write your validation code here
// if CONDITION {
// errs = append(errs, errors.New("an error message"))
// }
return (len(errs) == 0), errs
}
For each not valid input you should append to the variable errs a error message. See the example below:
func IsValid(car *entities.Car) (bool, []error) {
var errs []error
if len(car.Description) < 3 {
errs = append(errs, errors.New("description is too short"))
} else if len(car.Description) > 255 {
errs = append(errs, errors.New("description is too long"))
}
if car.Price >= 0.01 {
errs = append(errs, errors.New("price can't be less than $ 0.01"))
}
return (len(errs) == 0), errs
}
The example above means that the field description should have at least three character and should not have more than 255. Also, the field price should be at least one cent.
To validate the field year we are going to need import the package time to make sure users can't register car years in the future. Also, for this example, is not allowed to register car's years before 2010. As you can see in the following code:
import "time"
...
func IsValid(car *entities.Car) (bool, []error) {
...
currentTime := time.Now()
if car.Year < 2010 {
errs = append(errs, errors.New("year can't be less than 2010"))
} else if car.Year > int64(currentTime.Year()) {
errs = append(errs, errors.New("year can't be greater than current year"))
}
...
}
Now let's try to create a new car with invalid parameters:
curl -X POST -H 'Content-Type: application/json' \
-d '{ \
"description": "", \
"available":"true", \
"price": "0", \
"year":"3000" \
}' \
http://localhost:8081/cars
The result will be identical to:
{
"system_message": {
"type": "alert",
"content": "car was not created"
},
"errors": [
"description is too short",
"price can't be less than $ 0.01",
"year can't be greater than current year"
]
}
Now it's up to you write more validations.
Authentication is one of many default features generated by Wheel. And it creates a admin user when you run the migration. Check file db/schema/migrate.go. If you didn't change the default admin credentials, here they are:
user@example.com | |
password | Secret123! |
Quick tip!
The sign_in feature is fully documented in Default Resources section.
The sign in request is:
curl -X POST -H 'Content-Type: application/json' \
-d '{ \
"email": "user@example.com", \
"password": "Secret123!" \
}' \
http://localhost:8081/sessions/sign_in
The result will be something like this:
{
"system_message": {
"type": "notice",
"content": "signed in successfully"
},
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires": 7200
}
Very important!
To identify the user, you need to send this returned token in the HTTP header of subsequent requests. But this token expires. The returned field expires is the time to live of the token in seconds. To renew the session getting a new token and expiration time see Session Refresh in Default Resources section.
Quick tip!
The sign_out feature is fully documented in Default Resources section.
To sign out, the user must to be currently signed in and have a valid token.
curl -X DELETE -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
http://localhost:8081/sessions/sign_out
The result will be something like this:
{
"system_message": {
"type": "notice",
"content": "signed out successfully"
}
}
In the real-world, developers should grant access to application, or its parts, for authorized users only. That's why Wheel generates the file routes/authorization.go. This file has permissions to grant users access to resources.
When a new CRUD is generated, Wheel generates automatically the permissions for these new resources, but it has no restrictions, yet.
To grant permission, it must match a combination of these three items:
Permissions = append(Permissions,
Permission{
UrlRegexp: regexp.MustCompile(`\A\/cars(\/){0,1}.*\z`),
Methods: []string{"GET", "POST", "DELETE", "PUT"},
UserRoles: []string{"public", "signed_in", "admin"},
})
Our goal now is to garantee the only admin users can create, update and delete cars. So let's separate two parts.
// part 1
Permissions = append(Permissions,
Permission{
UrlRegexp: regexp.MustCompile(`\A\/cars(\/){0,1}.*\z`),
Methods: []string{"GET"},
UserRoles: []string{"public", "signed_in", "admin"},
})
// part 2
Permissions = append(Permissions,
Permission{
UrlRegexp: regexp.MustCompile(`\A\/cars(\/){0,1}.*\z`),
Methods: []string{"POST", "DELETE", "PUT"},
UserRoles: []string{"admin"},
})
Let's create a new car, supposing the user is signed in as admin and has a valid token.
curl -X POST -H 'Content-Type: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
-d '{ \
"description":"Ford", \
"available":"true", \
"price":"15300.00", \
"year":"2016" \
}' \
http://localhost:8081/cars
{
"system_message": {
"type": "notice",
"content": "car was successfully created"
},
"car": {
"id": 1,
"description": "Ford",
"year": 2016,
"price": 15300.00,
"available": true,
"created_at": "2019-08-31T19:16:08.685901034Z",
"updated_at": "2019-08-31T19:16:08.685901034Z"
}
}
Following the previous item, if you do not include a valid token in the request header it will return the 401 HTTP error:
curl -X POST -H 'Content-Type: application/json' \
-d '{ \
"description":"Ford", \
"available":"true", \
"price":"15300.00", \
"year":"2016" \
}' \
http://localhost:8081/cars
{
"system_message": {
"type": "alert",
"content": "401 Unauthorized"
}
}
If a signed_in user (not admin) tries to reach the same resource above it will return the 403 HTTP error:
curl -X POST -H 'Content-Type: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
-d '{ \
"description": "Ford", \
"available": "true", \
"price": "15300.00", \
"year": "2016" \
}' \
http://localhost:8081/cars
{
"system_message": {
"type": "alert",
"content": "403 Forbidden"
}
}
Quick tip!
Go ahead and register 5 cars. We are goning to need them for furthers examples.
Now let's emphasize the relationship between database entities. In this example, let's keep in mind these requirements:
$> wheel generate scaffold wishlist user:reference car:reference
...
Generating new CRUD...
created: app/wishlist/wishlist_model.go
created: app/wishlist/wishlist_view.go
created: db/entities/wishlist_entity.go
created: app/handlers/wishlist_handler.go
updated: routes/routes.go
updated: db/schema/migrate.go
updated: routes/authorize.go
The reference type means that the entity wishlists has references to entity users and to entity cars as well.
Very important!
Run the migration to update the database schema.
$> go run main.go -mode=migration
Users can add cars only to their own wishlist. To identify the current user we need the internal function SessionCheck and pass the token as the parameter. The token should be in the header of the request.
wishlistNew.UserID, _ = SessionCheck(r.Header.Get("token"))
Now let's add this line (above) to right position in the file app/handlers/wishlist_handler.go inside the function WishlistCreate:
func WishlistCreate(w http.ResponseWriter, r *http.Request) {
var wishlistNew = entities.Wishlist{}
log.Info.Println("Handler: WishlistCreate")
w.Header().Set("Content-Type", "application/json")
var whishListParams WhishListPermittedParams
_ = json.NewDecoder(r.Body).Decode(&whishListParams)
handler.SetPermittedParamsToEntity(&whishListParams, &wishlistNew)
// Add this line (below) at this position
wishlistNew.UserID, _ = SessionCheck(r.Header.Get("token"))
valid, errs := wishlist.Create(&wishlistNew)
if valid {
json.NewEncoder(w).Encode(wishlist.SuccessfullySavedJson{SystemMessage: view.SetSystemMessage("notice", "wishlist was successfully created"), Wishlist: wishlist.SetJson(newWishlist)})
} else {
json.NewEncoder(w).Encode(view.SetErrorMessage("alert", "wishlist was not created", errs))
}
}
Make sure you have a valid token, a valid car_id and execute this request:
curl -X POST -H 'Content-Type: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
-d '{ \
"car_id": "8" \
}' \
http://localhost:8081/wishlist
It will return something like this:
{
"system_message": {
"type": "notice",
"content": "wishlist was successfully created"
},
"wishlist": {
"id":1,
"user_id":1,
"car_id":8,
"created_at":"2019-09-01T16:31:30.701116524Z",
"updated_at":"2019-09-01T16:31:30.701116524Z"
}
}
Quick tip!
If the token gets expired or you lost it, nevermind. Just sign in again and use the new token.
One more:
curl -X POST -H 'Content-Type: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
-d '{ \
"car_id":"5" \
}' \
http://localhost:8081/wishlist
It will return something like this:
{
"system_message": {
"type": "notice",
"content": "wishlist was successfully created"
},
"wishlist": {
"id":2,
"user_id":1,
"car_id":5,
"created_at":"2019-09-01T16:34:38.621803316Z",
"updated_at":"2019-09-01T16:34:38.621803316Z"
}
}
Great! We have just created a wishlist with two items.
Users can just list their own wishlist items. To filter the wishlist items for the current user we need to add these lines, below, to file app/handlers/wishlist_handler.go.
import strconv
...
currentUserID, _ := SessionCheck(r.Header.Get("token"))
criteria["user_id_eq"] = strconv.FormatUint(uint64(currentUserID), 10)
Package strconv shoud be included to import statement. The others two lines should be inserted in the function WishlistList, as you can see the code below.
Very important!
The line criteria["user_id_eq"] = strconv.FormatUint(uint64(userID), 10) means we are going to use Wheel's built in search engine. And it goes to search on database registers of the specific user.
Quick tip!
Check the search engine feature section.
import strconv
...
func WishlistList(w http.ResponseWriter, r *http.Request) {
var i, page, entries, pages int
var wishlistList []entities.Wishlist
wishlistJsons := []wishlist.Json{}
log.Info.Println("Handler: WishlistList")
w.Header().Set("Content-Type", "application/json")
order := wishlistSanitizeOrder(r.FormValue("order"))
criteria := handler.QueryParamsToMapCriteria("search", r.URL.Query())
// Add these two lines below
userID, _ := SessionCheck(r.Header.Get("token"))
criteria["user_id_eq"] = strconv.FormatUint(uint64(userID), 10)
wishlistList, page, pages, entries = wishlist.Paginate(criteria, order, r.FormValue("page"), r.FormValue("per_page"))
for i = 0; i < len(wishlistList); i++ {
wishlistJsons = append(wishlistJsons, wishlist.SetJson(wishlistList[i]))
}
pagination := view.MainPagination{CurrentPage: page, TotalPages: pages, TotalEntries: entries}
json.NewEncoder(w).Encode(wishlist.PaginationJson{Pagination: pagination, Wishlists: wishlistJsons})
}
The request is:
curl -X GET -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
http://localhost:8081/wishlist
It will return something like this:
{
"system_message": {
"current_page":1,
"total_pages":1,
"total_entries":2
},
"wishlists": [
{
"id":1,
"user_id":1,
"car_id":8,
"created_at":"2019-09-01T16:31:30.701117Z",
"updated_at":"2019-09-01T16:31:30.701117Z"
},
{
"id":2,
"user_id":1,
"car_id":5,
"created_at":"2019-09-01T16:34:38.621803316Z",
"updated_at":"2019-09-01T16:34:38.621803316Z"
}
]
}
As you can see, the information card_id is not conclusive. Don't worry, the framework generated by Wheel is fexibe, let's customize it changing the file app/wishlist/wishlist_view.go.
First: import the car resources adding this line to the import statement.
import (
"github.com/adilsonchacon/car_store/app/car"
...
)
Second: at Json struct, replace the CarID field by this line:
type Json struct {
ID uint `json:"id"`
UserID uint `json:"user_id"`
// CarID uint `json:"car_id"` // remove this line
Car car.Json `json:"car"` // add this line
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Third: at the function SetJson, do the following changes:
func SetJson(wishlist entities.Wishlist) Json {
wishlistCar, _ := car.Find(wishlist.CarID) // add this line
return Json{
ID: wishlist.ID,
UserID: wishlist.UserID,
// CarID: wishlist.CardID, // remove this line
Car: car.SetJson(wishlistCar), // add this line
CreatedAt: wishlist.CreatedAt,
UpdatedAt: wishlist.UpdatedAt,
}
}
Restart the application and repeat the previous request:
curl -X GET -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
http://localhost:8081/wishlist
It will return something like this:
{
"system_message": {
"current_page":1,
"total_pages":1,
"total_entries":2
},
"wishlists": [
{
"id":1,
"user_id":1,
"car":{
"id":8,
"description":"VolksWagen",
"year":2019,
"price":19990,
"available":true,
"created_at":"2019-09-01T16:25:26.189186Z",
"updated_at":"2019-09-01T16:25:26.189186Z"
},
"created_at":"2019-09-01T16:31:30.701117Z",
"updated_at":"2019-09-01T16:31:30.701117Z"
},
{
"id":2,
"user_id":1,
"car":{
"id":5,
"description":"Ferrari",
"year":2018,
"price":200000,
"available":true,
"created_at":"2019-09-01T16:12:45.004372Z",
"updated_at":"2019-09-01T16:12:45.004372Z"
},
"created_at":"2019-09-01T16:34:38.621803316Z",
"updated_at":"2019-09-01T16:34:38.621803316Z"
}
]
}
Add this function to the file app/wishlist/wishlist_model.go to make sure current users just can find their own wishlist items:
func FindByIdAndUserId(id interface{}, userID interface{}) (entities.Wishlist, error) {
var wishlist entities.Wishlist
var err error
model.Db.Where("id = ? AND user_id = ?", id, userID).First(&wishlist)
if model.Db.NewRecord(wishlist) {
err = errors.New(NotFound)
}
return wishlist, err
}
Very important!
The function above is going to used for update and destroy functions, as well.
Edit file app/handlers/wishlist_handler.go and make the following changes at WishlistShow function:
func WishlistShow(w http.ResponseWriter, r *http.Request) {
log.Info.Println("Handler: WishlistShow")
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
// wishlistCurrent, err := wishlist.Find(params["id"]) // remove this line
// Add these two following lines
userID, _ := SessionCheck(r.Header.Get("token"))
wishlistCurrent, err := wishlist.FindByIdAndUserId(params["id"], userID)
if err == nil {
json.NewEncoder(w).Encode(wishlist.SetJson(wishlistCurrent))
} else {
json.NewEncoder(w).Encode(view.SetSystemMessage("alert", "wishlist was not found"))
}
}
Run the request (don't forget to set the wishlist's id at the end of the URL):
curl -X GET -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
http://localhost:8081/wishlist/2
It will return something like this:
{
"id":1,
"user_id":1,
"car":{
"id":8,
"description":"VolksWagen",
"year":2019,
"price":19990,
"available":true,
"created_at":"2019-09-01T16:25:26.189186Z",
"updated_at":"2019-09-01T16:25:26.189186Z"
},
"created_at":"2019-09-01T16:31:30.701117Z",
"updated_at":"2019-09-01T16:31:30.701117Z"
}
Edit file app/handlers/wishlist_handler.go and make the following changes at WishlistUpdate function:
func WishlistUpdate(w http.ResponseWriter, r *http.Request) {
log.Info.Println("Handler: WishlistUpdate")
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
userID, _ := SessionCheck(r.Header.Get("token")) // add this line
// and replace this line
// wishlistCurrent, err := wishlist.Find(params["id"])
// for this single one
wishlistCurrent, err := wishlist.FindByIdAndUserId(params["id"], userID)
if err != nil {
json.NewEncoder(w).Encode(view.SetErrorMessage("alert", "wishlist was not updated", []error{err}))
return
}
var wishlistParams WishlistPermittedParams
_ = json.NewDecoder(r.Body).Decode(&wishlistParams)
handler.SetPermittedParamsToEntity(&wishlistParams, &wishlistCurrent)
// Add this line (below) for avoiding change the wish list user
wishlistCurrent.UserId = UserID
if valid, errs := wishlist.Update(&wishlistCurrent); valid {
json.NewEncoder(w).Encode(wishlist.SuccessfullySavedJson{SystemMessage: view.SetSystemMessage("notice", "wishlist was successfully updated"), Wishlist: wishlist.SetJson(wishlistCurrent)})
} else {
json.NewEncoder(w).Encode(view.SetErrorMessage("alert", "wishlist was not updated", errs))
}
}
Update request:
curl -X PUT -H 'Content-Type: application/json' \
-H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
-d '{ \
"car_id": "7" \
}' \
http://localhost:8081/wishlist/2
It will return something like this:
{
"system_message": {
"type": "notice",
"content": "wishlist was successfully created"
},
"wishlist": {
"id":2,
"user_id":1,
"car":{
"id":7,
"description":"Audi",
"year":2017,
"price":78000,
"available":true,
"created_at":"2019-09-01T16:25:26.189186Z",
"updated_at":"2019-09-01T16:25:26.189186Z"
},
"created_at":"2019-09-01T20:31:30.701117Z",
"updated_at":"2019-09-01T29:31:30.701117Z"
}
}
Edit file app/handlers/wishlist_handler.go and make the following changes at WishlistDestroy function:
func WishlistDestroy(w http.ResponseWriter, r *http.Request) {
log.Info.Println("Handler: WishlistDestroy")
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
// replace this single line below
// wishlistCurrent, err := wishlist.Find(params["id"])
// for these two following lines
userID, _ := SessionCheck(r.Header.Get("token"))
wishlistCurrent, err := wishlist.FindByIdAndUserId(params["id"], userID)
if err == nil && wishlist.Destroy(&wishlistCurrent) {
json.NewEncoder(w).Encode(view.SetDefaultMessage("notice", "wishlist was successfully destroyed"))
} else {
json.NewEncoder(w).Encode(view.SetDefaultMessage("alert", "wishlist was not found"))
}
}
Destroy request:
curl -X DELETE -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...' \
http://localhost:8081/wishlist/2
It will return something like this:
{
"system_message": {
"type": "notice",
"content": "wishlist was successfully destroyed"
}
}