1
0
Fork 0
mirror of https://github.com/mealie-recipes/mealie.git synced 2025-08-05 21:45:25 +02:00

feat: Cross-Household Recipes (#4089)

This commit is contained in:
Michael Genson 2024-08-31 21:54:10 -05:00 committed by GitHub
parent 7ef2e91ecf
commit 9acf9ec27c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 545 additions and 92 deletions

View file

@ -4,3 +4,4 @@ export { useRecipes, recentRecipes, allRecipes, useLazyRecipes } from "./use-rec
export { parseIngredientText, useParsedIngredientText } from "./use-recipe-ingredients";
export { useNutritionLabels } from "./use-recipe-nutrition";
export { useTools } from "./use-recipe-tools";
export { useRecipePermissions } from "./use-recipe-permissions";

View file

@ -0,0 +1,81 @@
import { describe, test, expect } from "vitest";
import { useRecipePermissions } from "./use-recipe-permissions";
import { Recipe } from "~/lib/api/types/recipe";
import { UserOut } from "~/lib/api/types/user";
describe("test use recipe permissions", () => {
const commonUserId = "my-user-id";
const commonGroupId = "my-group-id";
const commonHouseholdId = "my-household-id";
const createRecipe = (overrides: Partial<Recipe>, isLocked = false): Recipe => ({
id: "my-recipe-id",
userId: commonUserId,
groupId: commonGroupId,
householdId: commonHouseholdId,
settings: {
locked: isLocked,
},
...overrides,
});
const createUser = (overrides: Partial<UserOut>): UserOut => ({
id: commonUserId,
groupId: commonGroupId,
groupSlug: "my-group",
group: "my-group",
householdId: commonHouseholdId,
householdSlug: "my-household",
household: "my-household",
email: "bender.rodriguez@example.com",
cacheKey: "1234",
...overrides,
});
test("when user is null, cannot edit", () => {
const result = useRecipePermissions(createRecipe({}), null);
expect(result.canEditRecipe.value).toBe(false);
});
test("when user is recipe owner, can edit", () => {
const result = useRecipePermissions(createRecipe({}), createUser({}));
expect(result.canEditRecipe.value).toBe(true);
});
test("when user is not recipe owner, is correct group and household, and recipe is unlocked, can edit", () => {
const result = useRecipePermissions(
createRecipe({}),
createUser({ id: "other-user-id" }),
);
expect(result.canEditRecipe.value).toBe(true);
});
test("when user is not recipe owner, and user is other group, cannot edit", () => {
const result = useRecipePermissions(
createRecipe({}),
createUser({ id: "other-user-id", groupId: "other-group-id"}),
);
expect(result.canEditRecipe.value).toBe(false);
});
test("when user is not recipe owner, and user is other household, cannot edit", () => {
const result = useRecipePermissions(
createRecipe({}),
createUser({ id: "other-user-id", householdId: "other-household-id" }),
);
expect(result.canEditRecipe.value).toBe(false);
});
test("when user is not recipe owner, and recipe is locked, cannot edit", () => {
const result = useRecipePermissions(
createRecipe({}, true),
createUser({ id: "other-user-id"}),
);
expect(result.canEditRecipe.value).toBe(false);
});
test("when user is recipe owner, and recipe is locked, can edit", () => {
const result = useRecipePermissions(createRecipe({}, true), createUser({}));
expect(result.canEditRecipe.value).toBe(true);
});
});

View file

@ -0,0 +1,34 @@
import { computed } from "@nuxtjs/composition-api";
import { Recipe } from "~/lib/api/types/recipe";
import { UserOut } from "~/lib/api/types/user";
export function useRecipePermissions(recipe: Recipe, user: UserOut | null) {
const canEditRecipe = computed(() => {
// Check recipe owner
if (!user?.id) {
return false;
}
if (user.id === recipe.userId) {
return true;
}
// Check group and household
if (user.groupId !== recipe.groupId) {
return false;
}
if (user.householdId !== recipe.householdId) {
return false;
}
// Check recipe
if (recipe.settings?.locked) {
return false;
}
return true;
});
return {
canEditRecipe,
}
}

View file

@ -84,8 +84,10 @@ export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
};
export const useRecipes = (
all = false, fetchRecipes = true,
all = false,
fetchRecipes = true,
loadFood = false,
queryFilter: string | null = null,
publicGroupSlug: string | null = null
) => {
const api = publicGroupSlug ? usePublicExploreApi(publicGroupSlug).explore : useUserApi();
@ -108,7 +110,7 @@ export const useRecipes = (
})();
async function refreshRecipes() {
const { data } = await api.recipes.getAll(page, perPage, { loadFood, orderBy: "created_at" });
const { data } = await api.recipes.getAll(page, perPage, { loadFood, orderBy: "created_at", queryFilter });
if (data) {
recipes.value = data.items;
}