if category == 0 {
rows, err := h.Repo.GetAllLatestProducts(c.Context())
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
}
result := make([]interface{}, len(rows))
for i, product := range rows {
result[i] = dbrow.ConvertToAllLatestProducts(product)
}
} else {
rows, err := h.Repo.GetLatestProductsByCategory(c.Context(), int16(category))
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
}
result := make([]interface{}, len(rows))
for i, product := range rows {
result[i] = dbrow.ConvertToCategoryLatestProducts(product)
}
}
Both the if and else condition follows the same code process, just the functions and struct are different, how to merge them, so that the code is smaller. I mean like:
var rows []postgres.GetAllLatestProductsRow
var rows []postgres.GetLatestProductsByCategoryRow
if category == 0 {
rows, err = h.Repo.GetAllLatestProducts(c.Context())
} else {
rows, err = h.Repo.GetLatestProductsByCategory(c.Context(), int16(category))
}
//Rest of the code ...
can't touch the h.Repo.GetAllLatestProducts or h.Repo.GetLatestProductsByCategory as those are external functions. Type safety is also important.
There can be multiple functions like featuredproducts, newproducts, I want to make a generic function to return the products as json based on the sql function selected dynamically.
You got the problem, dozens of functions with the same code structure is bad, at least to me it doesn't matter its readable or not, its just duplicate copy/paste thing without any sense, mostly copy/paste with only the function name/SQLC generated function name and structure name changes, rest the code flow is the same.
The SQLC generates automatic code based on the SQL query, now its a wastage of time to write repeated code to just transform the result to JSON and return it to the client. There could be dozens of SQL functions to return the latest products, featured products, products in a category, wishlist product, blah blah blah...
All the website understand is the Product structure, but the SQLC returns different structures, so there is no single dbResult kind of thing. Anyway the mapping is not a big thing, using reflection we can check fields with same name, and convert SQL.NullString to string, etc. in the mapping function.
The real issue is the if/else statements for me. You have moved the code in different functions, but for me in the situation it doesn't make sense. As the web handler will anyway have to check the request is valid or not, whether category is defined or not, then check whether category is 0 or not, and then call different functions and then get the result and return to the client. For a single function it may be looking better, but for real production, it will make thing worse, and instead of a single function and an if/else block, now you have 3 functions for each API.
What I am looking it to just map the SQLC result to the route handler. The code flow is always the same, only the function name and the structure name changes. How to make it dynamic so that in my http handler,I can simply write:
return SQLCResult(c.Query("category"), GetAllFeaturedProducts, GetFeaturedProductsByCategory)
and then based on the value of the category from c.Query("category"), the SQLCResult will automatically call either GetAllFeaturedProducts or GetFeaturedProductsByCategory. something like function as a callback, but the function signature is different, that's one issue.
func (q *Queries) GetAllFeaturedProducts(ctx context.Context) ([]GetAllFeaturedProductsRow, error)
func (q *Queries) GetFeaturedProductsByCategory(ctx context.Context, idCategory int16)
the mapping function is not required because in the SQLCResult, we can do something like:
MapDBStructToRestAPIStruct(&Product{}, &row, MapFields(&row))
that will create a map of the field name and the index, and pass the dbresult row and it will convert it to Product structure using reflection and return the same, i.e. return the first parameter as result after modifying its fields.
I am still looking for how to write a SQLCResult function, to take the SQLC function name as input and then return the result, or may be make it more generic by putting the Product{} atructure itself in the SQLCResult function like:
var result := SQLCResult(&Product{}, c.Query("category") == 0, GetAllFeaturedProducts, GetFeaturedProductsByCategory)
return c.Status(fiber.StatusOK).JSON(result)
where the SQLCResult will call the GetAllFeaturedProducts or GetFeaturedProductsByCategory based on the boolean condition and create map the function result to the structure passed as 1st argument, and return back that structure.
or may be like this is the final goal:
func (h *Handlers) GetLatestProducts(c *fiber.Ctx) error {
if c.Query("category") == 0
return c.JSON(SQLCResult(&Product{}, GetAllLatestProducts)
else
return c.JSON(SQLCResult(&Product{}, GetLatestProductsByCategory, c.Query("category"))
}
func (h *Handlers) GetFeaturedProducts(c *fiber.Ctx) error {
if c.Query("category") == 0
return c.JSON(SQLCResult(&Product{}, GetAllFeaturedProducts)
else
return c.JSON(SQLCResult(&Product{}, GetFeaturedProductsByCategory, c.Query("category"))
}