Files
caliverse_server/ServerBase/Helper/DynamoDbItemRequestHelper.cs
2025-05-01 07:20:41 +09:00

618 lines
23 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Amazon.DynamoDBv2.Model;
using Amazon.DynamoDBv2;
using Amazon.Runtime.Internal;
using Amazon.DynamoDBv2.DocumentModel;
using NLog.Conditions;
using ServerCore; using ServerBase;
using DYNAMO_DB_TABLE_FULL_NAME = System.String;
namespace ServerBase;
public static class DynamoDbItemRequestHelper
{
public enum AttribValueAction
{
None = 0,
Increase, // Atomic 기반 증가
Decrease, // Atomic 기반 감소
Update // 값의 변경
}
public class AttribAction
{
public AttribValueAction ValueAction { get; set; } = AttribValueAction.None;
public AttributeValue Value { get; set; } = new();
}
public static (Result, DynamoDbItemRequestQueryContext?) makeUpdateItemRequestWithDoc<TAttrib>( DynamoDbDocBase targetDoc
, Dictionary<string, AttribAction> toChangeAttibValues
, DynamoDbQueryExceptionNotifier.ExceptionHandler? exceptionHandler = null )
where TAttrib : AttribBase
{
ConditionValidCheckHelper.throwIfFalseWithCondition(() => 0 < toChangeAttibValues.Count, () => $"Invalid toChangeAttibValues.Count !!! : 0 < {toChangeAttibValues.Count}");
var server_logic = ServerLogicApp.getServerLogicApp();
NullReferenceCheckHelper.throwIfNull(server_logic, () => $"server_logic client is null !!! ");
var db_connector = server_logic.getDynamoDbClient();
NullReferenceCheckHelper.throwIfNull(db_connector, () => $"db_connector client is null !!! ");
var result = new Result();
var primary_key = targetDoc.getPrimaryKey();
var query_builder = new DynamoDbItemRequestHelper.UpdateItemRequestBuilder(db_connector.getTableFullName(targetDoc.TableName));
query_builder.withKeys(primary_key.toKeyWithAttributeValue());
var attrib_path_json_string = targetDoc.toJsonStringOfAttribs();
var update_expression = "SET ";
var expression_idx = 0;
var expression_attribute_names = new Dictionary<string, string>();
var expression_attribute_values = new Dictionary<string, AttributeValue>();
var update_item_request = query_builder.getItemRequest();
foreach (var each in toChangeAttibValues)
{
var attrib_key = each.Key;
var attrib_value = each.Value;
var target_key = JsonHelper.getJsonPropertyName<TAttrib>(attrib_key);
(var is_success, var attribute_expression) = DynamoDbClientHelper.toAttributeExpressionFromJson(attrib_path_json_string, target_key);
if (false == is_success)
{
var err_msg = $"Failed to DynamoDbClientHelper.toAttributeExpressionFromJson() !!! : attribPath:{attrib_path_json_string}, targetKey:{target_key} - {primary_key.toBasicString()}";
result.setFail(ServerErrorCode.AttribPathMakeFailed, err_msg);
Log.getLogger().error(result.toBasicString());
continue;
}
var attribute_names = DynamoDbClientHelper.toExpressionAttributeNamesFromJson(attrib_path_json_string, target_key);
foreach (var name in attribute_names)
{
expression_attribute_names.TryAdd(name.Key, name.Value);
}
if (expression_idx > 0) update_expression += ", ";
var data = attrib_value.Value;
if (AttribValueAction.Increase == attrib_value.ValueAction)
{
var start_key = $":start_{expression_idx}";
var incr_key = $":incr_{expression_idx}";
update_expression += $"{attribute_expression} = if_not_exists({attribute_expression}, {start_key}) + {incr_key}";
expression_attribute_values.Add(incr_key, data);
expression_attribute_values.TryAdd(start_key, new AttributeValue { N = "0" });
}
else if(AttribValueAction.Decrease== attrib_value.ValueAction)
{
var start_key = $":start_{expression_idx}";
var decr_key = $":decr_{expression_idx}";
update_expression += $"{attribute_expression} = if_not_exists({attribute_expression}, {start_key}) - {decr_key}";
expression_attribute_values.Add(decr_key, data);
expression_attribute_values.TryAdd(start_key, new AttributeValue { N = "0" });
}
else if (AttribValueAction.Update == attrib_value.ValueAction)
{
var new_value_key = $":newValue_{expression_idx}";
update_expression += $"{attribute_expression} = {new_value_key}";
expression_attribute_values.Add(new_value_key, data);
}
expression_idx++;
}
query_builder.withExpressionAttributeNames(expression_attribute_names);
query_builder.withUpdateExpression(update_expression);
query_builder.withExpressionAttributeValues(expression_attribute_values);
query_builder.withReturnValues(ReturnValue.ALL_NEW);
(result, var builded_update_item_request) = query_builder.build();
if(result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(builded_update_item_request, () => $"builded_update_item_request is null !!! - {primary_key.toBasicString()}");
var exception_handler = new DynamoDbQueryExceptionNotifier.ExceptionHandler(DynamoDbQueryExceptionNotifier.ConditionalCheckFailed, ServerErrorCode.LackOfTotalCalium);
return (result, builded_update_item_request.createItemRequestQueryContext(QueryType.Update, exceptionHandler));
}
//=========================================================================================
// UpdateItemRequest Builder
//=========================================================================================
public class UpdateItemRequestBuilder
{
private readonly UpdateItemRequest m_request;
private Document? m_document;
private readonly bool m_is_upsert;
public UpdateItemRequestBuilder(DYNAMO_DB_TABLE_FULL_NAME tableFullName, bool isUpsert = false)
{
ConditionValidCheckHelper.throwIfFalseWithCondition(() => false == tableFullName.isNullOrWhiteSpace(), () => $"Invalid TableFullName !!!, Null or WhiteSpace !!!");
m_request = new UpdateItemRequest
{
TableName = tableFullName,
Key = new Dictionary<string, AttributeValue>(),
AttributeUpdates = new Dictionary<string, AttributeValueUpdate>(),
UpdateExpression = string.Empty,
};
m_is_upsert = isUpsert;
}
public UpdateItemRequest getItemRequest() => m_request;
public UpdateItemRequestBuilder withDocument(Document document)
{
m_document = document;
return this;
}
public UpdateItemRequestBuilder withReturnValues(ReturnValue returnValue)
{
m_request.ReturnValues = returnValue;
return this;
}
public UpdateItemRequestBuilder withConditionExpression(string conditionExpression)
{
m_request.ConditionExpression = conditionExpression;
return this;
}
public UpdateItemRequestBuilder withExpressionAttributeNames(Dictionary<string, string> expressionAttributeNames)
{
m_request.ExpressionAttributeNames = expressionAttributeNames;
return this;
}
public UpdateItemRequestBuilder withExpressionAttributeValues(Dictionary<string, AttributeValue> expressionAttributeValues)
{
m_request.ExpressionAttributeValues = expressionAttributeValues;
return this;
}
public UpdateItemRequestBuilder withKeys(Dictionary<string, AttributeValue> keyValue)
{
m_request.Key = keyValue;
return this;
}
public UpdateItemRequestBuilder addKey(string keyName, string keyValue)
{
m_request.Key[keyName] = new AttributeValue { S = keyValue };
return this;
}
public UpdateItemRequestBuilder addKey(string keyName, int keyValue)
{
m_request.Key[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public UpdateItemRequestBuilder addKey(string keyName, long keyValue)
{
m_request.Key[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public UpdateItemRequestBuilder addKey(string keyName, float keyValue)
{
m_request.Key[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public UpdateItemRequestBuilder addKey(string keyName, double keyValue)
{
m_request.Key[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public UpdateItemRequestBuilder addKey(string keyName, bool keyValue)
{
m_request.Key[keyName] = new AttributeValue { BOOL = keyValue };
return this;
}
public UpdateItemRequestBuilder addKey(string keyName, AttributeValue keyValue)
{
m_request.Key[keyName] = keyValue;
return this;
}
public UpdateItemRequestBuilder withReturnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity)
{
m_request.ReturnConsumedCapacity = returnConsumedCapacity;
return this;
}
public UpdateItemRequestBuilder withReturnItemCollectionMetrics(ReturnItemCollectionMetrics returnItemCollectionMetrics)
{
m_request.ReturnItemCollectionMetrics = returnItemCollectionMetrics;
return this;
}
public UpdateItemRequestBuilder withUpdateExpression(string updateExpression)
{
m_request.UpdateExpression = updateExpression;
return this;
}
public UpdateItemRequestBuilder withConditionCheck(string conditionExpression)
{
m_request.ConditionExpression = conditionExpression;
return this;
}
public (Result, UpdateItemRequest?) build()
{
var result = new Result();
if(null != m_document)
{
result = m_document.tryFillupUpdateItemRequest(m_request, m_is_upsert);
if(result.isFail())
{
return (result, null);
}
}
else
{
if ( false == m_is_upsert
&& null != m_request.ConditionExpression
&& m_request.ConditionExpression.isNullOrWhiteSpace() )
{
m_request.ConditionExpression = $"attribute_exists({PrimaryKey.PK_Define}) AND attribute_exists({PrimaryKey.SK_Define})";
}
}
return (result, m_request);
}
}
//=========================================================================================
// GetItemRequest Builder
//=========================================================================================
public class GetItemRequestBuilder
{
private readonly GetItemRequest m_request;
public GetItemRequestBuilder(string tableName)
{
m_request = new GetItemRequest
{
TableName = tableName,
Key = new Dictionary<string, AttributeValue>(),
ExpressionAttributeNames = new Dictionary<string, string>()
};
}
public GetItemRequestBuilder withKeys(Dictionary<string, AttributeValue> keyValue)
{
m_request.Key = keyValue;
return this;
}
public GetItemRequestBuilder addKey(string keyName, string keyValue)
{
m_request.Key[keyName] = new AttributeValue { S = keyValue };
return this;
}
public GetItemRequestBuilder addKey(string keyName, int keyValue)
{
m_request.Key[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public GetItemRequestBuilder addKey(string keyName, long keyValue)
{
m_request.Key[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public GetItemRequestBuilder addKey(string keyName, double keyValue)
{
m_request.Key[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public GetItemRequestBuilder addKey(string keyName, float keyValue)
{
m_request.Key[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public GetItemRequestBuilder addKey(string keyName, bool keyValue)
{
m_request.Key[keyName] = new AttributeValue { BOOL = keyValue };
return this;
}
public GetItemRequestBuilder withConsistentRead(bool isConsistentRead)
{
m_request.ConsistentRead = isConsistentRead;
return this;
}
public GetItemRequestBuilder withProjectionExpression(string projectExpression)
{
m_request.ProjectionExpression = projectExpression;
return this;
}
public GetItemRequestBuilder withExpressionAttributeNames(Dictionary<string, string> expressionAttributeNames)
{
m_request.ExpressionAttributeNames = expressionAttributeNames;
return this;
}
public GetItemRequestBuilder withReturnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity)
{
m_request.ReturnConsumedCapacity = returnConsumedCapacity;
return this;
}
public GetItemRequest build()
{
return m_request;
}
}
//=========================================================================================
// PutItemRequest Builder
//=========================================================================================
public class PutItemRequestBuilder
{
private readonly PutItemRequest m_request;
public PutItemRequestBuilder(string tableName)
{
m_request = new PutItemRequest
{
TableName = tableName,
Item = new Dictionary<string, AttributeValue>(),
ExpressionAttributeNames = new Dictionary<string, string>(),
ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
};
}
public PutItemRequestBuilder withItem(Dictionary<string, AttributeValue> attributeValues)
{
m_request.Item = attributeValues;
return this;
}
public PutItemRequestBuilder addItem(string keyName, string keyValue)
{
m_request.Item[keyName] = new AttributeValue { S = keyValue };
return this;
}
public PutItemRequestBuilder addItem(string keyName, int keyValue)
{
m_request.Item[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public PutItemRequestBuilder addItem(string keyName, long keyValue)
{
m_request.Item[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public PutItemRequestBuilder addItem(string keyName, double keyValue)
{
m_request.Item[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public PutItemRequestBuilder addItem(string keyName, float keyValue)
{
m_request.Item[keyName] = new AttributeValue { N = keyValue.ToString() };
return this;
}
public PutItemRequestBuilder addItem(string keyName, bool keyValue)
{
m_request.Item[keyName] = new AttributeValue { BOOL = keyValue };
return this;
}
public PutItemRequestBuilder withConditionalOperator(ConditionalOperator conditionalOperator)
{
m_request.ConditionalOperator = conditionalOperator;
return this;
}
public PutItemRequestBuilder withConditionalCapacity(ReturnConsumedCapacity returnConsumedCapacity)
{
m_request.ReturnConsumedCapacity = returnConsumedCapacity;
return this;
}
public PutItemRequestBuilder withConditionExpression(string conditionExpression)
{
m_request.ConditionExpression = conditionExpression;
return this;
}
public PutItemRequestBuilder withExpressionAttributeNames(Dictionary<string, string> expressAttributeNames)
{
m_request.ExpressionAttributeNames = expressAttributeNames;
return this;
}
public PutItemRequestBuilder withExpressionAttributeValues(Dictionary<string, AttributeValue> expressAttributeValues)
{
m_request.ExpressionAttributeValues = expressAttributeValues;
return this;
}
public PutItemRequestBuilder withReturnConsumedCapacity(ReturnConsumedCapacity retrunConsumedCapacity)
{
m_request.ReturnConsumedCapacity = retrunConsumedCapacity;
return this;
}
public PutItemRequestBuilder withReturnItemCollectionMetrics(ReturnItemCollectionMetrics returnItemCollectionMetrics)
{
m_request.ReturnItemCollectionMetrics = returnItemCollectionMetrics;
return this;
}
public PutItemRequest build()
{
return m_request;
}
}
//=========================================================================================
// DeleteItemRequest Builder
//=========================================================================================
public class DeleteItemRequestBuilder
{
private readonly DeleteItemRequest m_request;
public DeleteItemRequestBuilder(string tableName)
{
m_request = new DeleteItemRequest
{
TableName = tableName,
Key = new Dictionary<string, AttributeValue>(),
ExpressionAttributeNames = new Dictionary<string, string>(),
ExpressionAttributeValues = new Dictionary<string, AttributeValue>()
};
}
public DeleteItemRequestBuilder withKeys(Dictionary<string, AttributeValue> keys)
{
m_request.Key = keys;
return this;
}
public DeleteItemRequestBuilder addKey(string attributeName, string attributeValue)
{
m_request.Key[attributeName] = new AttributeValue { S = attributeValue };
return this;
}
public DeleteItemRequestBuilder addKey(string attributeName, int attributeValue)
{
m_request.Key[attributeName] = new AttributeValue { N = attributeValue.ToString() };
return this;
}
public DeleteItemRequestBuilder addKey(string attributeName, long attributeValue)
{
m_request.Key[attributeName] = new AttributeValue { N = attributeValue.ToString() };
return this;
}
public DeleteItemRequestBuilder addKey(string attributeName, double attributeValue)
{
m_request.Key[attributeName] = new AttributeValue { N = attributeValue.ToString() };
return this;
}
public DeleteItemRequestBuilder addKey(string attributeName, float attributeValue)
{
m_request.Key[attributeName] = new AttributeValue { N = attributeValue.ToString() };
return this;
}
public DeleteItemRequestBuilder addKey(string attributeName, bool attributeValue)
{
m_request.Key[attributeName] = new AttributeValue { BOOL = attributeValue };
return this;
}
public DeleteItemRequestBuilder withConditionalOperator(ConditionalOperator conditionalOperator)
{
m_request.ConditionalOperator = conditionalOperator;
return this;
}
public DeleteItemRequestBuilder withReturnValues(ReturnValue returnValue)
{
m_request.ReturnValues = returnValue;
return this;
}
public DeleteItemRequestBuilder withConditionExpression(string conditionExpression)
{
m_request.ConditionExpression = conditionExpression;
return this;
}
public DeleteItemRequestBuilder withExpressionAttributeNames(Dictionary<string, string> expressionAttributeNames)
{
m_request.ExpressionAttributeNames = expressionAttributeNames;
return this;
}
public DeleteItemRequestBuilder withExpressionAttributeValues(Dictionary<string, AttributeValue> expressionAttributeValues)
{
m_request.ExpressionAttributeValues = expressionAttributeValues;
return this;
}
public DeleteItemRequestBuilder withReturnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity)
{
m_request.ReturnConsumedCapacity = returnConsumedCapacity;
return this;
}
public DeleteItemRequestBuilder withReturnItemCollectionMetrics(ReturnItemCollectionMetrics returnItemCollectionMetrics)
{
m_request.ReturnItemCollectionMetrics = returnItemCollectionMetrics;
return this;
}
public DeleteItemRequest build()
{
return m_request;
}
}
public static DynamoDbItemRequestQueryContext createItemRequestQueryContext( this AmazonDynamoDBRequest itemRequest
, QueryType queryType
, DynamoDbQueryExceptionNotifier.ExceptionHandler? exceptionHandler = null)
{
return new DynamoDbItemRequestQueryContext(itemRequest, queryType, exceptionHandler);
}
public static bool isValid(this AmazonDynamoDBRequest itemRequest)
{
//if (document.getPK() == string.Empty || document.getSK() == string.Empty || document.getDocType() == string.Empty)
//{
// Log.getLogger().error($"PK or SK or DocType is empty !!! : {document.toBasicString()}");
// return false;
//}
return true;
}
}