All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tech.aroma.data.cassandra.CassandraTokenRepository Maven / Gradle / Ivy

/*
 * Copyright 2017 RedRoma, Inc.
 *
 * 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.data.cassandra;

import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import javax.inject.Inject;

import com.datastax.driver.core.*;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sir.wellington.alchemy.collections.lists.Lists;
import tech.aroma.data.TokenRepository;
import tech.aroma.data.cassandra.Tables.Tokens;
import tech.aroma.thrift.authentication.AuthenticationToken;
import tech.aroma.thrift.exceptions.*;

import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static tech.aroma.data.assertions.AuthenticationAssertions.completeToken;
import static tech.aroma.data.assertions.RequestAssertions.isNullOrEmpty;
import static tech.aroma.data.cassandra.Tables.Tokens.*;
import static tech.sirwellington.alchemy.arguments.Arguments.*;
import static tech.sirwellington.alchemy.arguments.assertions.Assertions.notNull;
import static tech.sirwellington.alchemy.arguments.assertions.StringAssertions.*;

/**
 *
 * @author SirWellington
 */
final class CassandraTokenRepository implements TokenRepository
{

    private final static Logger LOG = LoggerFactory.getLogger(CassandraTokenRepository.class);

    private final Session cassandra;
    private final Function tokenMapper;

    @Inject
    CassandraTokenRepository(Session cassandra,
                             Function tokenMapper)
    {
        checkThat(cassandra, tokenMapper)
            .are(notNull());

        this.cassandra = cassandra;
        this.tokenMapper = tokenMapper;
    }

    @Override
    public boolean containsToken(String tokenId) throws TException
    {
        checkTokenId(tokenId);

        Statement query = createStatementToCheckIfExists(tokenId);

        Row row = tryToGetOneRowFrom(query);

        long count = row.getLong(0);
        return count > 0;
    }

    @Override
    public AuthenticationToken getToken(String tokenId) throws TException, InvalidTokenException
    {
        checkTokenId(tokenId);

        Statement query = createQueryToGetToken(tokenId);

        Row row = tryToGetOneRowFrom(query);

        AuthenticationToken token = tryToConvertRowToToken(row);

        return token;
    }

    @Override
    public void saveToken(AuthenticationToken token) throws TException
    {
        checkThat(token)
            .throwing(InvalidArgumentException.class)
            .is(completeToken());

        Statement insertStatement = createStatementToInsert(token);

        tryToExecute(insertStatement);
        LOG.debug("Token saved in Cassandra");
    }

    @Override
    public List getTokensBelongingTo(String ownerId) throws TException
    {
        checkThat(ownerId)
            .throwing(InvalidArgumentException.class)
            .usingMessage("ownerId missing")
            .is(nonEmptyString())
            .usingMessage("ownerId must be a UUID type")
            .is(validUUID());

        Statement query = createQueryToGetTokensOwnedBy(ownerId);

        ResultSet results = tryToGetResultSetFrom(query);

        List tokens = Lists.create();

        for (Row row : results)
        {
            AuthenticationToken token = tryToConvertRowToToken(row);
            tokens.add(token);
        }

        LOG.debug("Found {} tokens owned by {}", tokens.size(), ownerId);
        return tokens;
    }

    @Override
    public void deleteToken(String tokenId) throws TException
    {
        checkTokenId(tokenId);

        Statement deleteStatement = createStatementToDeleteToken(tokenId);

        tryToExecute(deleteStatement);

        LOG.debug("Successfully deleted Token {}", tokenId);
    }

    private void checkTokenId(String tokenId) throws InvalidArgumentException
    {
        checkThat(tokenId)
            .throwing(InvalidArgumentException.class)
            .usingMessage("missing tokenId")
            .is(nonEmptyString())
            .usingMessage("tokenId must be a UUID")
            .is(validUUID());
    }

    private Statement createStatementToCheckIfExists(String tokenId)
    {
        UUID tokenUuid = UUID.fromString(tokenId);

        return QueryBuilder
            .select()
            .countAll()
            .from(Tokens.TABLE_NAME)
            .where(eq(TOKEN_ID, tokenUuid));
    }

    private Row tryToGetOneRowFrom(Statement query) throws InvalidTokenException, OperationFailedException
    {
        ResultSet results = tryToGetResultSetFrom(query);

        Row row = results.one();

        checkThat(row)
            .throwing(InvalidTokenException.class)
            .usingMessage("Token does not exist")
            .is(notNull());

        return row;
    }

    private Statement createQueryToGetToken(String tokenId)
    {
        UUID tokenUuid = UUID.fromString(tokenId);

        return QueryBuilder
            .select()
            .all()
            .from(Tokens.TABLE_NAME)
            .where(eq(TOKEN_ID, tokenUuid));
    }

    private ResultSet tryToGetResultSetFrom(Statement statment) throws OperationFailedException
    {
        ResultSet results;
        try
        {
            results = cassandra.execute(statment);
        }
        catch (Exception ex)
        {
            LOG.error("Failed to execute Cassandra statement: {}", statment, ex);
            throw new OperationFailedException("Could not query for Token:" + ex.getMessage());
        }
        
        checkThat(results)
            .usingMessage("unexpected null result from cassandra")
            .throwing(OperationFailedException.class)
            .is(notNull());
        
        return results;
    }

    private AuthenticationToken tryToConvertRowToToken(Row row) throws OperationFailedException, InvalidTokenException
    {
        AuthenticationToken token;

        try
        {
            token = tokenMapper.apply(row);
        }
        catch (Exception ex)
        {
            LOG.error("Could not map Row {} to Token", row, ex);
            throw new OperationFailedException("Failed to query for Token: " + ex.getMessage());
        }

        checkThat(token)
            .throwing(InvalidTokenException.class)
            .is(completeToken());

        return token;
    }

    private Statement createStatementToInsert(AuthenticationToken token) throws InvalidArgumentException
    {
        //UUIDs
        UUID tokenId = UUID.fromString(token.tokenId);
        UUID ownerId = UUID.fromString(token.ownerId);
        UUID orgId = null;
        
        //Enums
        String tokenType = null;
        if (token.tokenType != null)
        {
            tokenType = token.tokenType.toString();
        }
        
        String tokenStatus = null;
        if (token.status != null)
        {
            tokenStatus = token.status.toString();
        }
        
        if (!isNullOrEmpty(token.organizationId))
        {
            checkThat(token.organizationId)
                .usingMessage("token organizationId must be a UUID type")
                .throwing(InvalidArgumentException.class)
                .is(validUUID());
            
            orgId = UUID.fromString(token.organizationId);
        }

        BatchStatement batch = new BatchStatement();

        Statement insertIntoMainTable = QueryBuilder
            .insertInto(Tokens.TABLE_NAME)
            .value(TOKEN_ID, tokenId)
            .value(OWNER_ID, ownerId)
            .value(ORG_ID, orgId)
            .value(OWNER_NAME, token.ownerName)
            .value(TIME_OF_EXPIRATION, token.timeOfExpiration)
            .value(TIME_OF_CREATION, token.timeOfCreation)
            .value(TOKEN_TYPE, tokenType)
            .value(TOKEN_STATUS, tokenStatus);

        batch.add(insertIntoMainTable);

        Statement insertIntoOwnersTable = QueryBuilder
            .insertInto(Tokens.TABLE_NAME_BY_OWNER)
            .value(OWNER_ID, ownerId)
            .value(TOKEN_ID, tokenId)
            .value(ORG_ID, orgId)
            .value(OWNER_NAME, token.ownerName)
            .value(TIME_OF_EXPIRATION, token.timeOfExpiration)
            .value(TIME_OF_CREATION, token.timeOfCreation)
            .value(TOKEN_TYPE, tokenType)
            .value(TOKEN_STATUS, tokenStatus);

        batch.add(insertIntoOwnersTable);

        return batch;
    }

    private void tryToExecute(Statement statement) throws OperationFailedException
    {
        try
        {
            cassandra.execute(statement);
        }
        catch (Exception ex)
        {
            LOG.error("Failed to execute CQL Statement: {}", statement, ex);
            throw new OperationFailedException("Could not perform operation: " + ex.getMessage());
        }
    }

    private Statement createQueryToGetTokensOwnedBy(String ownerId)
    {
        UUID ownerUuid = UUID.fromString(ownerId);

        return QueryBuilder
            .select()
            .all()
            .from(Tokens.TABLE_NAME_BY_OWNER)
            .where(eq(OWNER_ID, ownerUuid))
            .limit(1000);
    }

    private Statement createStatementToDeleteToken(String tokenId) throws TException
    {
        UUID tokenUuid = UUID.fromString(tokenId);

        //Need to get Token first
        AuthenticationToken token = this.getToken(tokenId);

        UUID ownerUuid = UUID.fromString(token.ownerId);

        BatchStatement batch = new BatchStatement();

        Statement deleteFromMainTable = QueryBuilder
            .delete()
            .all()
            .from(Tokens.TABLE_NAME)
            .where(eq(TOKEN_ID, tokenUuid));
        
        batch.add(deleteFromMainTable);

        Statement deleteFromOwnersTable = QueryBuilder
            .delete()
            .all()
            .from(Tokens.TABLE_NAME_BY_OWNER)
            .where(eq(OWNER_ID, ownerUuid));

        batch.add(deleteFromOwnersTable);
        
        return batch;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy