public List<Favorite> search(String userId, String appId, Pageable page) {
    boolean isUserIdEmpty = Strings.isNullOrEmpty(userId);
    boolean isAppIdEmpty = Strings.isNullOrEmpty(appId);

    if (isAppIdEmpty && isUserIdEmpty) {
      throw new BadRequestException("user id and app id can't be empty at the same time");
    }

    if (!isUserIdEmpty) {
      UserInfo loginUser = userInfoHolder.getUser();
      //user can only search his own favorite app
      if (!Objects.equals(loginUser.getUserId(), userId)) {
        userId = loginUser.getUserId();
      }
    }

    //search by userId
    if (isAppIdEmpty && !isUserIdEmpty) {
      return favoriteRepository.findByUserIdOrderByPositionAscDataChangeCreatedTimeAsc(userId, page);
    }

    //search by appId
    if (!isAppIdEmpty && isUserIdEmpty) {
      return favoriteRepository.findByAppIdOrderByPositionAscDataChangeCreatedTimeAsc(appId, page);
    }

    //search by userId and appId
    return Collections.singletonList(favoriteRepository.findByUserIdAndAppId(userId, appId));
  }
@Test
  @Sql(scripts = "/sql/favorites/favorites.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
  @Sql(scripts = "/sql/cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
  public void testSearchByUserId() {
    List<Favorite> favorites = favoriteService.search(testUser, null, PageRequest.of(0, 10));
    Assert.assertEquals(4, favorites.size());
  }