package com.devicehive.dao.riak;
/*
* #%L
* DeviceHive Dao Riak Implementation
* %%
* Copyright (C) 2016 DataArt
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import com.basho.riak.client.api.commands.kv.DeleteValue;
import com.basho.riak.client.api.commands.kv.FetchValue;
import com.basho.riak.client.api.commands.kv.StoreValue;
import com.basho.riak.client.api.commands.mapreduce.BucketMapReduce;
import com.basho.riak.client.api.commands.mapreduce.MapReduce;
import com.basho.riak.client.core.query.Location;
import com.basho.riak.client.core.query.Namespace;
import com.devicehive.dao.DeviceDao;
import com.devicehive.dao.NetworkDao;
import com.devicehive.dao.UserDao;
import com.devicehive.dao.riak.model.RiakUser;
import com.devicehive.exceptions.HivePersistenceLayerException;
import com.devicehive.model.enums.UserRole;
import com.devicehive.model.enums.UserStatus;
import com.devicehive.vo.DeviceVO;
import com.devicehive.vo.NetworkVO;
import com.devicehive.vo.UserVO;
import com.devicehive.vo.UserWithNetworkVO;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class UserDaoRiakImpl extends RiakGenericDao implements UserDao {
private static final Namespace USER_NS = new Namespace("user");
private static final Location COUNTERS_LOCATION = new Location(new Namespace("counters", "dh_counters"),
"userCounter");
@Autowired
private UserNetworkDaoRiakImpl userNetworkDao;
@Autowired
private NetworkDao networkDao;
@Autowired
private NetworkDeviceDaoRiakImpl networkDeviceDao;
@Autowired
private DeviceDao deviceDao;
public UserDaoRiakImpl() {
}
@PostConstruct
public void init() {
((NetworkDaoRiakImpl) networkDao).setUserDao(this);
}
@Override
public Optional<UserVO> findByName(String name) {
RiakUser riakUser = findBySecondaryIndex("login", name, USER_NS, RiakUser.class);
RiakUser.convertToVo(riakUser);
return Optional.ofNullable(RiakUser.convertToVo(riakUser));
}
@Override
public UserVO findByGoogleName(String name) {
RiakUser riakUser = findBySecondaryIndex("googleLogin", name, USER_NS, RiakUser.class);
RiakUser.convertToVo(riakUser);
return RiakUser.convertToVo(riakUser);
}
@Override
public UserVO findByFacebookName(String name) {
RiakUser riakUser = findBySecondaryIndex("facebookLogin", name, USER_NS, RiakUser.class);
RiakUser.convertToVo(riakUser);
return RiakUser.convertToVo(riakUser);
}
@Override
public UserVO findByGithubName(String name) {
RiakUser riakUser = findBySecondaryIndex("githubLogin", name, USER_NS, RiakUser.class);
RiakUser.convertToVo(riakUser);
return RiakUser.convertToVo(riakUser);
}
@Override
public Optional<UserVO> findByIdentityName(String login, String googleLogin, String facebookLogin, String githubLogin) {
UserVO userToCheck;
userToCheck = findByGoogleName(googleLogin);
if (userToCheck != null) {
if (doesUserAlreadyExist(userToCheck, login)) {
return Optional.of(userToCheck);
}
}
userToCheck = findByFacebookName(facebookLogin);
if (userToCheck != null) {
if (doesUserAlreadyExist(userToCheck, login)) {
return Optional.of(userToCheck);
}
}
userToCheck = findByGithubName(githubLogin);
if (userToCheck != null) {
if (doesUserAlreadyExist(userToCheck, login)) {
return Optional.of(userToCheck);
}
}
return Optional.empty();
}
@Override
public long hasAccessToNetwork(UserVO user, NetworkVO network) {
Set<Long> networks = userNetworkDao.findNetworksForUser(user.getId());
if (networks != null && networks.contains(network.getId())) {
return 1L;
} else {
return 0L;
}
}
@Override
public long hasAccessToDevice(UserVO user, String deviceGuid) {
Set<Long> networkIds = userNetworkDao.findNetworksForUser(user.getId());
for (Long networkId : networkIds) {
Set<DeviceVO> devices = networkDeviceDao.findDevicesForNetwork(networkId).stream()
.map(deviceDao::findByUUID)
.collect(Collectors.toSet());
if (devices != null) {
long guidCount = devices
.stream()
.map(DeviceVO::getGuid)
.filter(g -> g.equals(deviceGuid))
.count();
if (guidCount > 0) {
return guidCount;
}
}
}
return 0L;
}
@Override
public UserWithNetworkVO getWithNetworksById(long id) {
UserVO user = find(id);
if (user == null) {
return null;
}
Set<Long> networkIds = userNetworkDao.findNetworksForUser(id);
UserWithNetworkVO userWithNetworkVO = UserWithNetworkVO.fromUserVO(user);
if (networkIds != null) {
//TODO [rafa] [implement bulk fetch method here]
Set<NetworkVO> networks = new HashSet<>();
for (Long networkId : networkIds) {
NetworkVO network = networkDao.find(networkId);
networks.add(network);
}
userWithNetworkVO.setNetworks(networks);
}
return userWithNetworkVO;
}
@Override
public int deleteById(long id) {
Location location = new Location(USER_NS, String.valueOf(id));
DeleteValue deleteOp = new DeleteValue.Builder(location).build();
try {
client.execute(deleteOp);
return 1;
} catch (ExecutionException | InterruptedException e) {
throw new HivePersistenceLayerException("Cannot delete by id", e);
}
}
@Override
public UserVO find(Long id) {
try {
Location location = new Location(USER_NS, String.valueOf(id));
FetchValue fetchOp = new FetchValue.Builder(location)
.withOption(quorum.getReadQuorumOption(), quorum.getReadQuorum())
.build();
RiakUser riakUser = getOrNull(client.execute(fetchOp), RiakUser.class);
return RiakUser.convertToVo(riakUser);
} catch (ExecutionException | InterruptedException e) {
throw new HivePersistenceLayerException("Cannot find by id", e);
}
}
@Override
public void persist(UserVO user) {
merge(user);
}
@Override
public UserVO merge(UserVO user) {
RiakUser entity = RiakUser.convertToEntity(user);
try {
if (entity.getId() == null) {
entity.setId(getId(COUNTERS_LOCATION));
}
Location location = new Location(USER_NS, String.valueOf(entity.getId()));
StoreValue storeOp = new StoreValue.Builder(entity)
.withLocation(location)
.withOption(quorum.getWriteQuorumOption(), quorum.getWriteQuorum())
.build();
client.execute(storeOp);
user.setId(entity.getId());
return user;
} catch (ExecutionException | InterruptedException e) {
throw new HivePersistenceLayerException("Cannot merge user.", e);
}
}
@Override
public void unassignNetwork(@NotNull UserVO existingUser, @NotNull long networkId) {
userNetworkDao.delete(existingUser.getId(), networkId);
}
@Override
public List<UserVO> list(String login, String loginPattern,
Integer role, Integer status,
String sortField, Boolean isSortOrderAsc,
Integer take, Integer skip) {
BucketMapReduce.Builder builder = new BucketMapReduce.Builder()
.withNamespace(USER_NS);
addMapValues(builder);
if (login != null) {
addReduceFilter(builder, "login", FilterOperator.EQUAL, login);
} else if (loginPattern != null) {
loginPattern = loginPattern.replace("%", "");
addReduceFilter(builder, "login", FilterOperator.REGEX, loginPattern);
}
if (role != null) {
String roleString = UserRole.getValueForIndex(role).name();
addReduceFilter(builder, "role", FilterOperator.EQUAL, roleString);
}
if (status != null) {
String statusString = UserStatus.getValueForIndex(status).name();
addReduceFilter(builder, "status", FilterOperator.EQUAL, statusString);
}
addReduceSort(builder, sortField, isSortOrderAsc);
addReducePaging(builder, true, take, skip);
try {
MapReduce.Response response = client.execute(builder.build());
Collection<RiakUser> users = response.getResultsFromAllPhases(RiakUser.class);
return users.stream().map(RiakUser::convertToVo).collect(Collectors.toList());
} catch (InterruptedException | ExecutionException e) {
throw new HivePersistenceLayerException("Cannot execute search user.", e);
}
}
private boolean doesUserAlreadyExist(UserVO user, String login) {
return (!user.getLogin().equals(login) && user.getStatus() != UserStatus.DELETED);
}
}