Skip to content

Instantly share code, notes, and snippets.

@dapperAuteur
Created September 19, 2020 00:06
Show Gist options
  • Save dapperAuteur/8cb13c61389e0f1448eebb78ba679895 to your computer and use it in GitHub Desktop.
Save dapperAuteur/8cb13c61389e0f1448eebb78ba679895 to your computer and use it in GitHub Desktop.
This gist contains the model, service, handler, and route to filter products
// model
// FilterProduct defines what information may be provided to make a request to get Products and filter the results.
// All fields are optional so clients can send just the fields they want changed.
// It uses pointer fields so we can differentiate between a field that was not provided and a field that was provided as explicitly blank.
// Normally we do not want to use pointers to basic types but we make exceptions around marshalling/unmarshalling.
type FilterProduct struct {
Name *string `json:"name"`
Cost *int `json:"cost" validate:"omitempty,gte=0"`
Quantity *int `json:"quantity" validate:"omitempty,gte=1"`
}
// service
// FilterProducts gets all the Products from the database that meet the filter criteria.
// Then encodes them in a response client.
// It returns an empty array if none of the Products fit the criteria.
func FilterProducts(ctx context.Context, db *sqlx.DB, fp FilterProduct) ([]Product, error) {
list := []Product{}
var (
name string
cost,
quantity int
)
if fp.Name != nil {
name = *fp.Name
}
if fp.Cost != nil {
cost = *fp.Cost
}
if fp.Quantity != nil {
quantity = *fp.Quantity
}
const q = `SELECT
p.product_id, p.name, p.cost, p.quantity,
FROM products AS p
WHERE p.Name = $1 OR
WHERE p.cost = $2 OR
WHERE p.quantity = $3
GROUP BY p.Name`
if err := db.GetContext(ctx, &list, q, name, cost, quantity); err != nil {
if err == sql.ErrNoRows {
return nil, ErrNotFound
}
return nil, err
}
return list, nil
}
// handler
// FilterProducts gets all filtered products from the service layer.
func (p *Product) FilterProducts(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
ctx, span := trace.StartSpan(ctx, "handlers.Product.FilterProducts")
defer span.End()
filterProduct := product.FilterProduct{}
if err := web.Decode(r, &filterProduct); err != nil {
return err
}
list, err := product.FilterProducts(ctx, p.DB, filterProduct)
if err != nil {
return errors.Wrapf(err, "filtering products %q", filterProduct)
}
return web.Respond(ctx, w, list, http.StatusOK)
}
// route
app.Handle(http.MethodPost, "/v1/products/filter", p.FilterProducts)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment