com.cloudimpl.outstack.spring.repo.DynamodbEventRepository Maven / Gradle / Ivy
/*
* Copyright 2021 nuwan.
*
* 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 com.cloudimpl.outstack.spring.repo;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.document.*;
import com.amazonaws.services.dynamodbv2.document.internal.IteratorSupport;
import com.amazonaws.services.dynamodbv2.document.spec.QuerySpec;
import com.amazonaws.services.dynamodbv2.document.utils.ValueMap;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.Delete;
import com.amazonaws.services.dynamodbv2.model.Put;
import com.amazonaws.services.dynamodbv2.model.QueryRequest;
import com.amazonaws.services.dynamodbv2.model.QueryResult;
import com.amazonaws.services.dynamodbv2.model.ReturnConsumedCapacity;
import com.amazonaws.services.dynamodbv2.model.ReturnValuesOnConditionCheckFailure;
import com.amazonaws.services.dynamodbv2.model.TransactWriteItem;
import com.amazonaws.services.dynamodbv2.model.TransactWriteItemsRequest;
import com.amazonaws.services.dynamodbv2.model.Update;
import com.cloudimpl.outstack.common.GsonCodec;
import com.cloudimpl.outstack.runtime.EntityCheckpoint;
import com.cloudimpl.outstack.runtime.EntityContextProvider;
import com.cloudimpl.outstack.runtime.EntityIdHelper;
import com.cloudimpl.outstack.runtime.EventRepositoy;
import com.cloudimpl.outstack.runtime.EventStream;
import com.cloudimpl.outstack.runtime.ResourceHelper;
import com.cloudimpl.outstack.runtime.ResultSet;
import com.cloudimpl.outstack.runtime.domainspec.ChildEntity;
import com.cloudimpl.outstack.runtime.domainspec.Entity;
import com.cloudimpl.outstack.runtime.domainspec.Event;
import com.cloudimpl.outstack.runtime.domainspec.Query;
import com.cloudimpl.outstack.runtime.domainspec.RootEntity;
import com.cloudimpl.outstack.runtime.domainspec.TenantRequirement;
import com.cloudimpl.outstack.runtime.repo.EventRepoUtil;
import com.cloudimpl.outstack.runtime.repo.RepositoryException;
import com.cloudimpl.outstack.spring.component.SpringApplicationConfigManager.Provider;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
*
* @author nuwan
* @param
*/
public class DynamodbEventRepository extends EventRepositoy {
private final Provider.ProviderConfigs configs;
private final DynamoDB dynamodb;
private final AmazonDynamoDB client;
private String entityTable;
private String eventTable;
public static final String partitionKeyColumn = "partitionKey";
public static final String rangeKeyColumn = "rangeKey";
public static final String jsonColumn = "json";
public static final String lastSeqColumn = "lastSeq";
public static final String eventTypeColumn = "eventType";
public static final String eventOwnerColumn = "eventOwner";
public static final String ownerIdColumn = "ownerId";
//private final int partitionCount;
private final String tableName;
ThreadLocal> txContext = ThreadLocal.withInitial(() -> new LinkedList<>());
public DynamodbEventRepository(AmazonDynamoDB client, DynamoDB dynamodb, Class rootType, ResourceHelper resourceHelper, Provider.ProviderConfigs configs) {
super(rootType, resourceHelper);
this.dynamodb = dynamodb;
this.client = client;
//this.loadTables();
this.configs = configs;
this.tableName = this.configs.getOption(rootType.getSimpleName() + "Table").or(()->this.configs.getOption("defaultTable")).get();
System.out.println("table name " + this.tableName + " pick for root type: " + rootType.getSimpleName());
//this.partitionCount = Integer.valueOf(configs.getOption("partitionCount").get());
}
@Override
protected void startTransaction() {
List list = txContext.get();
list.clear();
}
@Override
protected void endTransaction() {
TransactWriteItemsRequest transaction = new TransactWriteItemsRequest()
.withTransactItems(txContext.get())
.withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);
// Execute the transaction and process the result.
try {
client.transactWriteItems(transaction);
System.out.println("Transaction Successful");
} catch (Exception ex) {
throw new RepositoryException(ex);
}
}
@Override
protected void saveRootEntityBrnIfNotExist(RootEntity e) {
String rn = resourceHelper.getFQBrn(e.getBRN());
String prefix = rn.substring(0, rn.lastIndexOf("/"));
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(prefix));
item.put(this.rangeKeyColumn, new AttributeValue(rn));
item.put(this.jsonColumn, new AttributeValue(GsonCodec.encode(e)));
item.put(this.lastSeqColumn, new AttributeValue().withN(String.valueOf(e.getMeta().getLastSeq())));
Put createEntity = new Put()
.withTableName(this.tableName)
.withItem(item)
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
.withConditionExpression("attribute_not_exists(" + this.rangeKeyColumn + ")");
txContext.get().add(new TransactWriteItem().withPut(createEntity));
}
@Override
protected void saveRootEntityTrnIfNotExist(RootEntity e) {
String trn = resourceHelper.getFQTrn(e.getTRN());
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(trn));
item.put(this.rangeKeyColumn, new AttributeValue(trn));
item.put(this.jsonColumn, new AttributeValue(GsonCodec.encode(e)));
item.put(this.lastSeqColumn, new AttributeValue().withN(String.valueOf(e.getMeta().getLastSeq())));
Put createEntity = new Put()
.withTableName(this.tableName)
.withItem(item)
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
.withConditionExpression("attribute_not_exists(" + this.partitionKeyColumn + ")");
txContext.get().add(new TransactWriteItem().withPut(createEntity));
}
@Override
protected void saveRootEntityBrnIfExist(long lastSeq, RootEntity e) {
String rn = resourceHelper.getFQBrn(e.getBRN());
String prefix = rn.substring(0, rn.lastIndexOf("/"));
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(prefix));
item.put(this.rangeKeyColumn, new AttributeValue(rn));
Map expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":lastSeq", new AttributeValue().withN(String.valueOf(e.getMeta().getLastSeq())));
expressionAttributeValues.put(":checkLastSeq", new AttributeValue().withN(String.valueOf(lastSeq)));
expressionAttributeValues.put(":json", new AttributeValue(GsonCodec.encode(e)));
Update updateEntity = new Update()
.withTableName(this.tableName)
.withKey(item)
.withUpdateExpression("SET json = :json , lastSeq = :lastSeq")
.withExpressionAttributeValues(expressionAttributeValues)
.withConditionExpression("lastSeq = :checkLastSeq and attribute_exists(" + this.rangeKeyColumn + ")")
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
txContext.get().add(new TransactWriteItem().withUpdate(updateEntity));
}
@Override
protected void saveRootEntityTrnIfExist(long lastSeq, RootEntity e) {
String trn = resourceHelper.getFQTrn(e.getTRN());
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(trn));
item.put(this.rangeKeyColumn, new AttributeValue(trn));
Map expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":lastSeq", new AttributeValue().withN(String.valueOf(e.getMeta().getLastSeq())));
expressionAttributeValues.put(":checkLastSeq", new AttributeValue().withN(String.valueOf(lastSeq)));
expressionAttributeValues.put(":json", new AttributeValue(GsonCodec.encode(e)));
Update updateEntity = new Update()
.withTableName(this.tableName)
.withKey(item)
.withUpdateExpression("SET json = :json , lastSeq = :lastSeq")
.withExpressionAttributeValues(expressionAttributeValues)
.withConditionExpression("lastSeq = :checkLastSeq and attribute_exists(" + this.rangeKeyColumn + ")")
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
txContext.get().add(new TransactWriteItem().withUpdate(updateEntity));
}
@Override
protected void saveChildEntityBrnIfNotExist(ChildEntity e) {
String partitionKey = resourceHelper.getFQTrn(e.getRootTRN());
String rangeKey = resourceHelper.getFQBrn(e.getBRN());
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(partitionKey));
item.put(this.rangeKeyColumn, new AttributeValue(rangeKey));
item.put(this.jsonColumn, new AttributeValue(GsonCodec.encode(e)));
item.put(this.lastSeqColumn, new AttributeValue().withN(String.valueOf(e.getMeta().getLastSeq())));
Put createEntity = new Put()
.withTableName(this.tableName)
.withItem(item)
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
.withConditionExpression("attribute_not_exists(" + this.rangeKeyColumn + ")");
txContext.get().add(new TransactWriteItem().withPut(createEntity));
}
@Override
protected void saveChildEntityTrnIfNotExist(ChildEntity e) {
String partitionKey = resourceHelper.getFQTrn(e.getRootTRN());
String rangeKey = resourceHelper.getFQTrn(e.getTRN());
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(partitionKey));
item.put(this.rangeKeyColumn, new AttributeValue(rangeKey));
item.put(this.jsonColumn, new AttributeValue(GsonCodec.encode(e)));
item.put(this.lastSeqColumn, new AttributeValue().withN(String.valueOf(e.getMeta().getLastSeq())));
Put createEntity = new Put()
.withTableName(this.tableName)
.withItem(item)
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
.withConditionExpression("attribute_not_exists(" + this.rangeKeyColumn + ")");
txContext.get().add(new TransactWriteItem().withPut(createEntity));
}
@Override
protected void saveChildEntityBrnIfExist(long lastSeq,ChildEntity e) {
String partitionKey = resourceHelper.getFQTrn(e.getRootTRN());
String rangeKey = resourceHelper.getFQBrn(e.getBRN());
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(partitionKey));
item.put(this.rangeKeyColumn, new AttributeValue(rangeKey));
Map expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":lastSeq", new AttributeValue().withN(String.valueOf(e.getMeta().getLastSeq())));
expressionAttributeValues.put(":checkLastSeq", new AttributeValue().withN(String.valueOf(lastSeq)));
expressionAttributeValues.put(":json", new AttributeValue(GsonCodec.encode(e)));
Update updateEntity = new Update()
.withTableName(this.tableName)
.withKey(item)
.withUpdateExpression("SET json = :json , lastSeq = :lastSeq")
.withExpressionAttributeValues(expressionAttributeValues)
.withConditionExpression("lastSeq = :checkLastSeq and attribute_exists(" + this.rangeKeyColumn + ")")
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
txContext.get().add(new TransactWriteItem().withUpdate(updateEntity));
}
@Override
protected void saveChildEntityTrnIfExist(long lastSeq,ChildEntity e) {
String partitionKey = resourceHelper.getFQTrn(e.getRootTRN());
String rangeKey = resourceHelper.getFQTrn(e.getTRN());
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(partitionKey));
item.put(this.rangeKeyColumn, new AttributeValue(rangeKey));
Map expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":lastSeq", new AttributeValue().withN(String.valueOf(e.getMeta().getLastSeq())));
expressionAttributeValues.put(":checkLastSeq", new AttributeValue().withN(String.valueOf(lastSeq)));
expressionAttributeValues.put(":json", new AttributeValue(GsonCodec.encode(e)));
Update updateEntity = new Update()
.withTableName(this.tableName)
.withKey(item)
.withUpdateExpression("SET json = :json , lastSeq = :lastSeq")
.withExpressionAttributeValues(expressionAttributeValues)
.withConditionExpression("lastSeq = :checkLastSeq and attribute_exists(" + this.rangeKeyColumn + ")")
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
txContext.get().add(new TransactWriteItem().withUpdate(updateEntity));
}
@Override
protected void deleteRootEntityBrnById(RootEntity e,boolean deleteOnlyEntity) {
String rn = resourceHelper.getFQBrn(RootEntity.makeRN(e.getClass(), version, e.entityId(), e.getTenantId()));
String prefix = rn.substring(0, rn.lastIndexOf("/"));
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(prefix));
item.put(this.rangeKeyColumn, new AttributeValue(rn));
Delete delete = new Delete()
.withTableName(tableName)
.withKey(item);
txContext.get().add(new TransactWriteItem().withDelete(delete));
}
@Override
protected void deleteRootEntityTrnById(Class rootType, String id, String tenantId) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected > void deleteChildEntityBrnById(ChildEntity e,boolean deleteOnlyEntity) {
String partitionKey = resourceHelper.getFQTrn(RootEntity.makeTRN(rootType, version, e.id(),e.getTenantId()));
String rangeKey = resourceHelper.getFQBrn(ChildEntity.makeRN(rootType, version, e.rootId(), e.getClass(), e.id(), e.getTenantId()));
HashMap item = new HashMap<>();
item.put(this.partitionKeyColumn, new AttributeValue(partitionKey));
item.put(this.rangeKeyColumn, new AttributeValue(rangeKey));
Delete delete = new Delete()
.withTableName(tableName)
.withKey(item);
txContext.get().add(new TransactWriteItem().withDelete(delete));
}
@Override
protected > void deleteChildEntityTrnById(Class rootType, String id, Class childType, String childId, String tenantId) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
@Override
protected Optional _getCheckpoint(String rootTrn) {
String partitionKey = resourceHelper.getFQTrn(rootTrn);
Map expressionAttributesNames = new HashMap<>();
expressionAttributesNames.put("#partitionKey", "partitionKey");
expressionAttributesNames.put("#rangeKey", "rangeKey");
expressionAttributesNames.put("#json", "json");
Map expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":partitionKey", new AttributeValue().withS(partitionKey));
expressionAttributeValues.put(":rangeKey", new AttributeValue().withS(EntityCheckpoint.class.getSimpleName()));
QueryRequest queryRequest = new QueryRequest()
.withTableName(this.tableName)
//.withAttributesToGet("json")
.withKeyConditionExpression("partitionKey = :partitionKey AND rangeKey = :rangeKey")
// .withExpressionAttributeNames(expressionAttributesNames)
.withExpressionAttributeValues(expressionAttributeValues);
QueryResult queryResult = client.query(queryRequest);
List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy