Skip to content

Commit

Permalink
get principal from env, not context, for favorites
Browse files Browse the repository at this point in the history
  • Loading branch information
barneyb committed Sep 25, 2024
1 parent 135ac5e commit fec01e8
Show file tree
Hide file tree
Showing 13 changed files with 104 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.brennaswitzer.cookbook.graphql;

import com.brennaswitzer.cookbook.domain.Favorite;
import com.brennaswitzer.cookbook.graphql.support.PrincipalUtil;
import com.brennaswitzer.cookbook.services.favorites.UpdateFavorites;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand All @@ -12,13 +14,19 @@ public class FavoriteMutation {
@Autowired
private UpdateFavorites updateFavorites;

Favorite markFavorite(String objectType, Long objectId) {
return updateFavorites.ensureFavorite(objectType,
public Favorite markFavorite(String objectType,
Long objectId,
DataFetchingEnvironment env) {
return updateFavorites.ensureFavorite(PrincipalUtil.from(env),
objectType,
objectId);
}

boolean removeFavorite(String objectType, Long objectId) {
return updateFavorites.ensureNotFavorite(objectType,
public boolean removeFavorite(String objectType,
Long objectId,
DataFetchingEnvironment env) {
return updateFavorites.ensureNotFavorite(PrincipalUtil.from(env),
objectType,
objectId);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.brennaswitzer.cookbook.graphql;

import com.brennaswitzer.cookbook.domain.Favorite;
import com.brennaswitzer.cookbook.graphql.support.PrincipalUtil;
import com.brennaswitzer.cookbook.services.favorites.FetchFavorites;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand All @@ -14,16 +16,22 @@ public class FavoriteQuery {
@Autowired
private FetchFavorites fetchFavorites;

public List<Favorite> all() {
return fetchFavorites.all();
public List<Favorite> all(DataFetchingEnvironment env) {
return fetchFavorites.all(PrincipalUtil.from(env));
}

public List<Favorite> byType(String objectType) {
return fetchFavorites.byType(objectType);
public List<Favorite> byType(String objectType,
DataFetchingEnvironment env) {
return fetchFavorites.byType(PrincipalUtil.from(env),
objectType);
}

public Favorite byObject(String objectType, Long objectId) {
return fetchFavorites.byObject(objectType, objectId)
public Favorite byObject(String objectType,
Long objectId,
DataFetchingEnvironment env) {
return fetchFavorites.byObject(PrincipalUtil.from(env),
objectType,
objectId)
.orElse(null);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.brennaswitzer.cookbook.graphql.loaders;

import com.brennaswitzer.cookbook.domain.Favorite;
import com.brennaswitzer.cookbook.domain.FavoriteType;

public record FavKey(long ownerId, FavoriteType favType, long objectId) {

public static FavKey from(Favorite f) {
return new FavKey(f.getOwner().getId(),
FavoriteType.parse(f.getObjectType()),
f.getObjectId());
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,39 @@
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toMap;

@Component
public class IsFavoriteBatchLoader implements BatchLoader<IsFavorite, Boolean> {
public class IsFavoriteBatchLoader implements BatchLoader<FavKey, Boolean> {

@Autowired
private FavoriteRepository repo;

private record OwnerType(long ownerId, FavoriteType favType) {

public static OwnerType of(IsFavorite key) {
public static OwnerType of(FavKey key) {
return new OwnerType(key.ownerId(), key.favType());
}

public static OwnerType of(Favorite fav) {
return new OwnerType(fav.getOwner().getId(), FavoriteType.parse(fav.getObjectType()));
}

}

@Override
public CompletionStage<List<Boolean>> load(List<IsFavorite> keys) {
public CompletionStage<List<Boolean>> load(List<FavKey> keys) {
return CompletableFuture.supplyAsync(() -> loadInternal(keys));
}

@VisibleForTesting
List<Boolean> loadInternal(List<IsFavorite> keys) {
var favIdsByOwnerType = keys.stream()
List<Boolean> loadInternal(List<FavKey> keys) {
var favsByOwnerType = keys.stream()
.collect(groupingBy(OwnerType::of,
mapping(IsFavorite::objectId,
mapping(FavKey::objectId,
Collectors.toSet())))
.entrySet()
.stream()
Expand All @@ -53,14 +50,10 @@ List<Boolean> loadInternal(List<IsFavorite> keys) {
e.getKey().favType().getKey(),
e.getValue()))
.<Favorite>mapMulti(Iterable::forEach)
.collect(groupingBy(OwnerType::of,
mapping(Favorite::getObjectId,
Collectors.toSet())));
.collect(toMap(FavKey::from,
Function.identity()));
return keys.stream()
.map(k -> favIdsByOwnerType.getOrDefault(
OwnerType.of(k),
Set.of())
.contains(k.objectId()))
.map(favsByOwnerType::containsKey)
.toList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@
import com.brennaswitzer.cookbook.domain.PlanItemStatus;
import com.brennaswitzer.cookbook.domain.PlannedRecipeHistory;
import com.brennaswitzer.cookbook.domain.Recipe;
import com.brennaswitzer.cookbook.graphql.loaders.IsFavorite;
import com.brennaswitzer.cookbook.graphql.loaders.FavKey;
import com.brennaswitzer.cookbook.graphql.loaders.IsFavoriteBatchLoader;
import com.brennaswitzer.cookbook.graphql.support.PrincipalUtil;
import com.brennaswitzer.cookbook.mapper.LabelMapper;
import com.brennaswitzer.cookbook.services.favorites.FetchFavorites;
import graphql.kickstart.tools.GraphQLResolver;
import graphql.schema.DataFetchingEnvironment;
import org.hibernate.Hibernate;
Expand All @@ -33,9 +32,6 @@
@Component
public class RecipeResolver implements GraphQLResolver<Recipe> {

@Autowired
private FetchFavorites fetchFavorites;

@Autowired
private LabelMapper labelMapper;

Expand All @@ -56,10 +52,10 @@ public List<String> labels(Recipe recipe) {

public CompletableFuture<Boolean> favorite(Recipe recipe,
DataFetchingEnvironment env) {
return env.<IsFavorite, Boolean>getDataLoader(IsFavoriteBatchLoader.class.getName())
.load(new IsFavorite(PrincipalUtil.from(env).getId(),
FavoriteType.RECIPE,
recipe.getId()));
return env.<FavKey, Boolean>getDataLoader(IsFavoriteBatchLoader.class.getName())
.load(new FavKey(PrincipalUtil.from(env).getId(),
FavoriteType.RECIPE,
recipe.getId()));
}

public Photo photo(Recipe recipe) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ public interface UserRepository extends BaseEntityRepository<User> {

User getByName(String name);

Boolean existsByEmail(String email);

User getById(Long id);

// kludge for FriendController's pretend implementation
List<User> findByIdNot(Long idToExclude);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@

import com.brennaswitzer.cookbook.domain.Favorite;
import com.brennaswitzer.cookbook.repositories.FavoriteRepository;
import com.brennaswitzer.cookbook.util.UserPrincipalAccess;
import com.brennaswitzer.cookbook.security.UserPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.Set;

@Service
@Transactional
Expand All @@ -18,28 +17,24 @@ public class FetchFavorites {
@Autowired
private FavoriteRepository repo;

@Autowired
private UserPrincipalAccess principalAccess;

public List<Favorite> all() {
return repo.findByOwnerId(principalAccess.getId());
}

public List<Favorite> byType(String objectType) {
return repo.findByOwnerIdAndObjectType(principalAccess.getId(),
objectType);
public List<Favorite> all(UserPrincipal principal) {
return repo.findByOwnerId(principal.getId());
}

public Optional<Favorite> byObject(String objectType, Long objectId) {
return repo.findByOwnerIdAndObjectTypeAndObjectId(principalAccess.getId(),
objectType,
objectId);
public List<Favorite> byType(UserPrincipal principal,
String objectType) {
return repo.findByOwnerIdAndObjectType(
principal.getId(),
objectType);
}

public Iterable<Favorite> byObjects(String objectType, Set<Long> objectIds) {
return repo.findByOwnerIdAndObjectTypeAndObjectIdIn(principalAccess.getId(),
objectType,
objectIds);
public Optional<Favorite> byObject(UserPrincipal principal,
String objectType,
Long objectId) {
return repo.findByOwnerIdAndObjectTypeAndObjectId(
principal.getId(),
objectType,
objectId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import com.brennaswitzer.cookbook.domain.Favorite;
import com.brennaswitzer.cookbook.repositories.FavoriteRepository;
import com.brennaswitzer.cookbook.util.UserPrincipalAccess;
import com.brennaswitzer.cookbook.repositories.UserRepository;
import com.brennaswitzer.cookbook.security.UserPrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -15,25 +16,31 @@ public class UpdateFavorites {
private FavoriteRepository repo;

@Autowired
private UserPrincipalAccess principalAccess;
private UserRepository userRepo;

public Favorite ensureFavorite(String objectType, Long objectId) {
return repo.findByOwnerIdAndObjectTypeAndObjectId(principalAccess.getId(),
objectType,
objectId)
public Favorite ensureFavorite(UserPrincipal principal,
String objectType,
Long objectId) {
return repo.findByOwnerIdAndObjectTypeAndObjectId(
principal.getId(),
objectType,
objectId)
.orElseGet(() -> {
Favorite fav = new Favorite();
fav.setOwner(principalAccess.getUser());
fav.setOwner(userRepo.getReferenceById(principal.getId()));
fav.setObjectType(objectType);
fav.setObjectId(objectId);
return repo.save(fav);
});
}

public boolean ensureNotFavorite(String objectType, Long objectId) {
return 0 < repo.deleteByOwnerIdAndObjectTypeAndObjectId(principalAccess.getId(),
objectType,
objectId);
public boolean ensureNotFavorite(UserPrincipal principal,
String objectType,
Long objectId) {
return 0 < repo.deleteByOwnerIdAndObjectTypeAndObjectId(
principal.getId(),
objectType,
objectId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ default UserPrincipal getUserPrincipal() {
}

default User getUser() {
return getUser(getUserPrincipal());
}

default User getUser(UserPrincipal principal) {
throw new UnsupportedOperationException();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public Optional<UserPrincipal> findUserPrincipal() {
}

@Override
public User getUser() {
return userRepo.getById(getId());
public User getUser(UserPrincipal principal) {
return userRepo.getReferenceById(principal.getId());
}

}
4 changes: 2 additions & 2 deletions src/main/resources/graphqls/favorites.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ extend type Mutation {

type FavoriteMutation {
"""Add the specified object to the current user's favorites, if not already
present, and returns the favorite.
present, and return the favorite.
"""
markFavorite(objectType: String!, objectId: ID!): Favorite!
"""Remove the specified object from the current user's favorites, and return
whether any action was required to ensure this.
whether any action was taken to ensure this.
"""
removeFavorite(objectType: String!, objectId: ID!): Boolean!
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
Expand All @@ -36,13 +37,15 @@ void happyPath() {
when(favTwo.getOwner()).thenReturn(user);
when(favTwo.getObjectType()).thenReturn(FavoriteType.RECIPE.getKey());
when(favTwo.getObjectId()).thenReturn(2L);
when(repo.findByOwnerIdAndObjectTypeAndObjectIdIn(any(), any(), any()))
when(repo.findByOwnerIdAndObjectTypeAndObjectIdIn(eq(123L), any(), any()))
.thenReturn(List.of());
when(repo.findByOwnerIdAndObjectTypeAndObjectIdIn(eq(456L), any(), any()))
.thenReturn(List.of(favTwo));

List<Boolean> result = loader.loadInternal(List.of(
new IsFavorite(123L, FavoriteType.RECIPE, 1L),
new IsFavorite(456L, FavoriteType.RECIPE, 2L),
new IsFavorite(123L, FavoriteType.RECIPE, 3L)));
new FavKey(123L, FavoriteType.RECIPE, 1L),
new FavKey(456L, FavoriteType.RECIPE, 2L),
new FavKey(123L, FavoriteType.RECIPE, 3L)));

assertEquals(List.of(false, true, false), result);
verify(repo).findByOwnerIdAndObjectTypeAndObjectIdIn(
Expand Down

0 comments on commit fec01e8

Please sign in to comment.