The profile list will now properly display the users recipes! The favorites list does not exist yet, since there is no backend support for favoriting/saving recipes. So the list displays the same content as the user recipe list. Same goes for the activity list, not yet implemented.
157 lines
5.1 KiB
Go
157 lines
5.1 KiB
Go
package service
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
domain "github.com/haydenhargreaves/Potion/internal/domain/recipe"
|
|
domainServer "github.com/haydenhargreaves/Potion/internal/domain/server"
|
|
)
|
|
|
|
// RecipeService implements the domain.RecipeService defined in the domain module.
|
|
type RecipeService struct {
|
|
recipeRepository domain.RecipeRepository
|
|
}
|
|
|
|
// Compile-time check to ensure the RecipeService implements domain.RecipeService
|
|
var _ domain.RecipeService = (*RecipeService)(nil)
|
|
|
|
// NewRecipeService creates a user service object which can be passed into the context. The service
|
|
// requires a recipe repository which it will use to hit the database when needed.
|
|
func NewRecipeService(recipeRepository domain.RecipeRepository) domain.RecipeService {
|
|
return &RecipeService{recipeRepository: recipeRepository}
|
|
}
|
|
|
|
// CreateRecipe creates a recipe in the database using the recipe repository. This function requires
|
|
// all the data to be present, though validation does not occur in this function. However, the UI
|
|
// will enforce validation, as will the database. Errors will be returned to the called when they
|
|
// occur.
|
|
//
|
|
// TODO: Implement validation in the API.
|
|
// TODO: Implement image creation and tag creation.
|
|
func (s *RecipeService) CreateRecipe(ctx *gin.Context) (*domain.Recipe, error) {
|
|
// Ensure user is logged in
|
|
if !domainServer.IsLoggedIn(ctx) {
|
|
return nil, fmt.Errorf("User is not logged in.")
|
|
}
|
|
|
|
title := ctx.PostForm("title")
|
|
description := ctx.PostForm("description")
|
|
preparation := ctx.PostForm("preparation-time")
|
|
cook := ctx.PostForm("cook-time")
|
|
serving := ctx.PostForm("serving-size")
|
|
category := ctx.PostForm("category")
|
|
difficulty := ctx.PostForm("difficulty")
|
|
ingredients := ctx.PostFormArray("ingredients")
|
|
quantity := ctx.PostFormArray("quantity")
|
|
instructions := ctx.PostFormArray("instructions")
|
|
tags := strings.Split(ctx.PostForm("tags"), ",")
|
|
userId := ctx.MustGet("userId").(int)
|
|
|
|
// Have to get the image differently
|
|
image, err := ctx.FormFile("image")
|
|
if err != nil && !errors.Is(err, http.ErrMissingFile) {
|
|
// Error getting image
|
|
}
|
|
|
|
// Convert to proper values
|
|
servingInt, _ := strconv.Atoi(serving)
|
|
difficultyInt, _ := strconv.Atoi(difficulty)
|
|
prepInt, _ := strconv.Atoi(preparation)
|
|
cookInt, _ := strconv.Atoi(cook)
|
|
|
|
var ingredientSlice []domain.RecipeIngredient
|
|
for i := range len(ingredients) {
|
|
if strings.TrimSpace(ingredients[i]) != "" {
|
|
ins := domain.RecipeIngredient{
|
|
Name: ingredients[i],
|
|
Quantity: quantity[i],
|
|
}
|
|
|
|
ingredientSlice = append(ingredientSlice, ins)
|
|
}
|
|
}
|
|
|
|
var instructionSlice []string
|
|
for _, ins := range instructions {
|
|
if ins != "" {
|
|
instructionSlice = append(instructionSlice, ins)
|
|
}
|
|
}
|
|
|
|
// Create the recipe
|
|
recipe := domain.Recipe{
|
|
Title: title,
|
|
Description: description,
|
|
Instructions: instructionSlice,
|
|
Serves: servingInt,
|
|
Difficulty: difficultyInt,
|
|
Duration: domain.RecipeDuration{
|
|
Total: prepInt + cookInt,
|
|
Prep: prepInt,
|
|
Cook: cookInt,
|
|
},
|
|
Category: domain.RecipeMeal(category),
|
|
Ingredients: ingredientSlice,
|
|
UserId: userId,
|
|
Created: time.Now(),
|
|
}
|
|
|
|
if err := s.recipeRepository.CreateRecipe(&recipe); err != nil {
|
|
return &recipe, err
|
|
}
|
|
|
|
// TODO: Upload the image
|
|
if image != nil {
|
|
}
|
|
|
|
// TODO: Create the tags in the database
|
|
if len(tags) > 0 {
|
|
if err := s.recipeRepository.CreateRecipeTags(recipe, tags); err != nil {
|
|
return &recipe, fmt.Errorf("Failed to attach/create tags. %s\n", err.Error())
|
|
}
|
|
}
|
|
|
|
return &recipe, nil
|
|
}
|
|
|
|
// GetRecipe will get a recipe via its ID. Any errors will be bubbled to the caller. Furthermore,
|
|
// if the recipe is nil, an error will be returned, so the caller does not need to check for a nil
|
|
// recipe (e.g., if the error is nil the recipe exists)
|
|
func (s *RecipeService) GetRecipe(id int) (*domain.Recipe, error) {
|
|
recipe, err := s.recipeRepository.GetRecipe(id)
|
|
|
|
if recipe == nil {
|
|
return nil, fmt.Errorf("Failed to get recipe from database. Nil result.")
|
|
}
|
|
|
|
return recipe, err
|
|
}
|
|
|
|
// SearchRecipes will search the database using the filters provided. The recipes can be passed into
|
|
// a template and displayed in the UI as the search result. A more detailed definition of the
|
|
// filters is provided below.
|
|
//
|
|
// Each input is given a bit value (e.g., 00001 for 1) and will be passed
|
|
// back to this handler as an array. The values are then added together
|
|
// and will result in a integer which represents bit values. These bits
|
|
// can then be passed to the repository and are then parsed to determine
|
|
// which filters should be applied.
|
|
// Parsing these is simple, for each filter option, use the bitwise and (&)
|
|
// operator with the value we expect for the filter. When 1, we can ensure
|
|
// the filter is provided.
|
|
// A function `isBitActive` in the recipe repository provides an example of
|
|
// testing of testing the filter parsing.
|
|
func (s *RecipeService) SearchRecipes(filters domain.SearchFilters) ([]domain.Recipe, error) {
|
|
return s.recipeRepository.SearchRecipes(filters)
|
|
}
|
|
|
|
func (s *RecipeService) GetUserRecipes(id int) ([]domain.Recipe, error) {
|
|
return s.recipeRepository.GetUserRecipes(id)
|
|
}
|