tech.aroma.service.operations.ProvisionApplicationOperation Maven / Gradle / Ivy
/*
* Copyright 2016 RedRoma.
*
* 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.
*/
package tech.aroma.service.operations;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import javax.inject.Inject;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sir.wellington.alchemy.collections.sets.Sets;
import tech.aroma.data.ApplicationRepository;
import tech.aroma.data.FollowerRepository;
import tech.aroma.data.MediaRepository;
import tech.aroma.data.UserRepository;
import tech.aroma.thrift.Application;
import tech.aroma.thrift.Image;
import tech.aroma.thrift.LengthOfTime;
import tech.aroma.thrift.TimeUnit;
import tech.aroma.thrift.User;
import tech.aroma.thrift.authentication.ApplicationToken;
import tech.aroma.thrift.authentication.AuthenticationToken;
import tech.aroma.thrift.authentication.TokenType;
import tech.aroma.thrift.authentication.UserToken;
import tech.aroma.thrift.authentication.service.AuthenticationService;
import tech.aroma.thrift.authentication.service.CreateTokenRequest;
import tech.aroma.thrift.authentication.service.CreateTokenResponse;
import tech.aroma.thrift.authentication.service.GetTokenInfoRequest;
import tech.aroma.thrift.authentication.service.GetTokenInfoResponse;
import tech.aroma.thrift.email.EmailMessage;
import tech.aroma.thrift.email.EmailNewApplication;
import tech.aroma.thrift.email.service.EmailService;
import tech.aroma.thrift.email.service.SendEmailRequest;
import tech.aroma.thrift.exceptions.InvalidArgumentException;
import tech.aroma.thrift.exceptions.InvalidTokenException;
import tech.aroma.thrift.exceptions.OperationFailedException;
import tech.aroma.thrift.service.AromaServiceConstants;
import tech.aroma.thrift.service.ProvisionApplicationRequest;
import tech.aroma.thrift.service.ProvisionApplicationResponse;
import tech.sirwellington.alchemy.annotations.access.Internal;
import tech.sirwellington.alchemy.arguments.AlchemyAssertion;
import tech.sirwellington.alchemy.thrift.operations.ThriftOperation;
import static java.time.Instant.now;
import static tech.aroma.data.assertions.AuthenticationAssertions.completeToken;
import static tech.sirwellington.alchemy.arguments.Arguments.checkThat;
import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull;
import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.nonEmptyString;
import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.stringWithLengthLessThanOrEqualTo;
/**
*
* @author SirWellington
*/
@Internal
final class ProvisionApplicationOperation implements ThriftOperation
{
private final static Logger LOG = LoggerFactory.getLogger(ProvisionApplicationOperation.class);
private final ApplicationRepository appRepo;
private final FollowerRepository followerRepo;
private final MediaRepository mediaRepo;
private final UserRepository userRepo;
private final AuthenticationService.Iface authenticationService;
private final EmailService.Iface emailService;
private final Function appTokenMapper;
@Inject
ProvisionApplicationOperation(ApplicationRepository appRepo,
FollowerRepository followerRepo,
MediaRepository mediaRepo,
UserRepository userRepo,
AuthenticationService.Iface authenticationService,
EmailService.Iface emailService,
Function appTokenMapper)
{
checkThat(appRepo,
followerRepo,
mediaRepo,
userRepo,
authenticationService,
emailService,
appTokenMapper)
.are(notNull());
this.appRepo = appRepo;
this.followerRepo = followerRepo;
this.mediaRepo = mediaRepo;
this.userRepo = userRepo;
this.authenticationService = authenticationService;
this.emailService = emailService;
this.appTokenMapper = appTokenMapper;
}
@Override
public ProvisionApplicationResponse process(ProvisionApplicationRequest request) throws TException
{
LOG.info("Received request to provision an Application", request);
checkThat(request)
.throwing(ex -> new InvalidArgumentException(ex.getMessage()))
.is(good());
AuthenticationToken authTokenForUser = getUserTokenFrom(request.token);
User user = userRepo.getUser(authTokenForUser.ownerId);
LOG.debug("Owner ID {} Maps to user {}", authTokenForUser.ownerId, user);
Application app = createAppFrom(request, user);
AuthenticationToken authTokenForApp = createAppTokenFor(app);
ApplicationToken appToken = appTokenMapper.apply(authTokenForApp);
if (hasIcon(request))
{
String mediaIdForIcon = app.applicationId;
saveIcon(mediaIdForIcon, request.icon);
app.setApplicationIconMediaId(mediaIdForIcon);
}
//Save time of token expiration
app.setTimeOfTokenExpiration(appToken.timeOfExpiration);
appRepo.saveApplication(app);
saveOwnersAsFollowers(app);
sendOutEmail(user, app, appToken, authTokenForUser);
return new ProvisionApplicationResponse()
.setApplicationInfo(app)
.setApplicationToken(appToken);
}
private AuthenticationToken getUserTokenFrom(UserToken token) throws InvalidTokenException, OperationFailedException
{
GetTokenInfoRequest request = new GetTokenInfoRequest()
.setTokenId(token.tokenId)
.setTokenType(TokenType.USER);
GetTokenInfoResponse response;
try
{
response = authenticationService.getTokenInfo(request);
}
catch (InvalidTokenException ex)
{
throw ex;
}
catch (Exception ex)
{
LOG.error("Failed to get token info from Authentication Service for {}", token, ex);
throw new OperationFailedException("Token invalid: " + ex.getMessage());
}
checkThat(response.token)
.throwing(OperationFailedException.class)
.usingMessage("Auth service returned null response")
.is(notNull());
return response.token;
}
private Application createAppFrom(ProvisionApplicationRequest request, User user)
{
Set owners = Sets.copyOf(request.owners);
//Creating user is automatically an Owner
owners.add(user.userId);
String appId = UUID.randomUUID().toString();
return new Application()
.setApplicationId(appId)
.setName(request.applicationName)
.setApplicationDescription(request.applicationDescription)
.setOrganizationId(request.organizationId)
.setTier(request.tier)
.setProgrammingLanguage(request.programmingLanguage)
.setTimeOfProvisioning(now().toEpochMilli())
.setTotalMessagesSent(0L)
.setOwners(owners);
}
private AuthenticationToken createAppTokenFor(Application app) throws OperationFailedException
{
LengthOfTime lifetime = new LengthOfTime()
.setUnit(TimeUnit.DAYS)
.setValue(180);
CreateTokenRequest request = new CreateTokenRequest()
.setDesiredTokenType(TokenType.APPLICATION)
.setLifetime(lifetime)
.setOrganizationId(app.organizationId)
.setOwnerId(app.applicationId)
.setOwnerName(app.name);
CreateTokenResponse response;
try
{
response = authenticationService.createToken(request);
}
catch (TException ex)
{
LOG.error("Failed to create Token for Application: {}", app, ex);
throw new OperationFailedException("Could not create token for app: " + ex.getMessage());
}
checkThat(response)
.throwing(OperationFailedException.class)
.usingMessage("Authentication Service returned null")
.is(notNull());
checkThat(response.token)
.usingMessage("Auth Service returned incomplete token")
.throwing(OperationFailedException.class)
.is(completeToken());
return response.token;
}
private AlchemyAssertion good()
{
return request ->
{
checkThat(request)
.usingMessage("request is null")
.is(notNull());
checkThat(request.token)
.usingMessage("request missing token")
.is(notNull());
checkThat(request.applicationName)
.usingMessage("Application name is required")
.is(nonEmptyString())
.usingMessage("Application name is too long")
.is(stringWithLengthLessThanOrEqualTo(AromaServiceConstants.APPLICATION_NAME_MAX_LENGTH));
};
}
private void saveIcon(String mediaId, Image icon) throws TException
{
mediaRepo.saveMedia(mediaId, icon);
}
private boolean hasIcon(ProvisionApplicationRequest request)
{
if (request.isSetIcon())
{
byte[] icon = request.getIcon().getData();
if (icon != null && icon.length > 0)
{
return true;
}
}
return false;
}
private void saveOwnersAsFollowers(Application app)
{
Sets.nullToEmpty(app.owners)
.parallelStream()
.map(this::getUserInfo)
.filter(Objects::nonNull)
.forEach(owner -> this.tryToSaveOwner(owner, app));
}
private User getUserInfo(String userId)
{
try
{
return userRepo.getUser(userId);
}
catch (TException ex)
{
LOG.warn("Could not get user info for Owner with ID [{}]", userId, ex);
return null;
}
}
private void tryToSaveOwner(User owner, Application app)
{
try
{
followerRepo.saveFollowing(owner, app);
}
catch (TException ex)
{
LOG.warn("Could not save Following Information between Owner [{}] and App [{}]", owner, app, ex);
}
}
private void sendOutEmail(User user, Application app, ApplicationToken appToken, AuthenticationToken token)
{
EmailNewApplication newApplicationEmail = new EmailNewApplication()
.setApp(app)
.setCreator(user)
.setAppToken(appToken);
EmailMessage message = new EmailMessage();
message.setNewApp(newApplicationEmail);
SendEmailRequest request = new SendEmailRequest()
.setEmailAddress(user.email)
.setEmailMessage(message)
.setToken(token);
try
{
emailService.sendEmail(request);
LOG.debug("Sent out Email for new App creation to {}", user.email);
}
catch (Exception ex)
{
LOG.error("Failed to send out Email to {}", user.email, ex);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy