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

1369 lines
54 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.WebSockets;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Amazon.S3.Model;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.DynamoDBv2;
using Amazon.Runtime.Internal.Transform;
using Amazon.DynamoDBv2.DataModel;
using Microsoft.AspNetCore.Components.Web;
using ServerCore; using ServerBase;
using DYNAMO_DB_TABLE_NAME = System.String;
using DYNAMO_DB_TABLE_FULL_NAME = System.String;
using DB_TIMESTAMP = System.String;
namespace ServerBase;
public static class DynamoDBDocBaseHelper
{
public static async Task<(Result, PrimaryKey?)> makePrimaryKey<TDoc>(string PK, string SK = DynamoDbClient.SK_EMPTY)
where TDoc : DynamoDbDocBase, new()
{
await Task.CompletedTask;
var result = new Result();
var err_msg = string.Empty;
var doc = new TDoc();
doc.setCombinationKeyForPKSK(PK, SK);
var error_code = doc.onApplyPKSK();
if (error_code.isFail())
{
err_msg = $"Failed to onApplyPKSK() !!! : {doc.getPrimaryKey().toBasicString()}, Params(PK:{PK}, SK:{SK}), Doc:{typeof(TDoc).Name}";
result.setFail(error_code, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
return (result, doc.getPrimaryKey());
}
public static Dictionary<string, AttributeValue> toKeyWithAttributeValue(this PrimaryKey primaryKey)
{
var key = new Dictionary<string, AttributeValue>
{
{
PrimaryKey.PK_Define
, new AttributeValue { S = primaryKey.PK }
}
,
{
PrimaryKey.SK_Define
, new AttributeValue { S = primaryKey.SK }
}
};
return key;
}
public static Primitive toPartitionKey(this PrimaryKey primaryKey)
{
ConditionValidCheckHelper.throwIfFalseWithCondition( () => false == primaryKey.PK.isNullOrWhiteSpace()
, () => $"Invalid PK, Null or WhiteSpace !!!" );
return new Primitive(primaryKey.PK);
}
public static Primitive? toSortKey(this PrimaryKey primaryKey)
{
if (true == primaryKey.SK.isNullOrWhiteSpace()) return null;
return new Primitive(primaryKey.SK);
}
public static Result tryFillupUpdateItemRequest( this Amazon.DynamoDBv2.DocumentModel.Document document
, UpdateItemRequest updateItemRequest
, bool isUpsert = false )
{
var result = new Result();
var err_msg = string.Empty;
if (false == document.isValid())
{
err_msg = $"DynamoDbDocument invalid !!! : {document.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbDocumentIsInvalid, err_msg);
return result;
}
var update_expression = "SET ";
var expression_attribute_names = new Dictionary<string, string>();
var expression_attribute_values = new Dictionary<string, AttributeValue>();
var expression_idx = 0;
foreach ( var each in document.ToAttributeMap() )
{
if( each.Key == PrimaryKey.PK_Define
|| each.Key == PrimaryKey.SK_Define )
{
continue;
}
if (expression_idx > 0)
{
update_expression += ", ";
}
var placeholder_key = $"#{each.Key}";
var placeholder_value = $":{each.Key}";
update_expression += $"{placeholder_key} = {placeholder_value}";
expression_idx++;
expression_attribute_names.Add(placeholder_key, each.Key);
expression_attribute_values.Add(placeholder_value, each.Value);
}
updateItemRequest.UpdateExpression = update_expression;
updateItemRequest.ExpressionAttributeNames = expression_attribute_names;
updateItemRequest.ExpressionAttributeValues = expression_attribute_values;
updateItemRequest.ReturnValues = "ALL_NEW";
if( false == isUpsert
&& null != updateItemRequest.ConditionExpression
&& true == updateItemRequest.ConditionExpression.isNullOrWhiteSpace() )
{
updateItemRequest.ConditionExpression = $"attribute_exists({PrimaryKey.PK_Define}) AND attribute_exists({PrimaryKey.SK_Define})";
}
return result;
}
public static async Task<Result> tryFillupTimestampsForUpsert( this Document document
, DynamoDbClient dynamoDbClient, DYNAMO_DB_TABLE_NAME tableName)
{
var result = new Result();
(result, bool is_found) = await dynamoDbClient.checkIfDocumentExists(tableName, document);
if(result.isFail())
{
return result;
}
// 데이터가 존재하는 경우
if(true == is_found)
{
result = document.tryFillupTimestamps(QueryType.Update);
if (result.isFail())
{
return result;
}
}
// 데이터가 없는 경우
else
{
result = document.tryFillupTimestamps(QueryType.Insert);
if (result.isFail())
{
return result;
}
}
return result;
}
public static void fillupTimestamps(this Amazon.DynamoDBv2.Model.Put put)
{
var attribute_values = put.Item;
NullReferenceCheckHelper.throwIfNull(attribute_values, () => $"attributes is null !!!");
fillupAttributeValueWithTimestampsForInsert(attribute_values, DateTimeHelper.Current);
}
public static Result tryFillupTimestamps(this Amazon.DynamoDBv2.Model.Update update, bool isUpsert = false)
{
var result = new Result();
var err_msg = string.Empty;
var statement = update.UpdateExpression;
NullReferenceCheckHelper.throwIfNull(statement, () => $"attribute_names is null !!!");
var attribute_names = update.ExpressionAttributeNames;
NullReferenceCheckHelper.throwIfNull(attribute_names, () => $"attribute_names is null !!!");
var attribute_values = update.ExpressionAttributeValues;
NullReferenceCheckHelper.throwIfNull(attribute_values, () => $"attribute_values is null !!!");
result = tryFillupTimestamps( ref statement
, attribute_names, attribute_values
, DateTimeHelper.Current
, isUpsert );
if (result.isFail())
{
return result;
}
update.UpdateExpression = statement;
return result;
}
public static Result tryFillupTimestamps(this Amazon.DynamoDBv2.DocumentModel.Expression expression, bool isUpsert = false)
{
var result = new Result();
var err_msg = string.Empty;
var statement = expression.ExpressionStatement;
NullReferenceCheckHelper.throwIfNull(statement, () => $"statement is null !!!");
var attribute_names = expression.ExpressionAttributeNames;
NullReferenceCheckHelper.throwIfNull(attribute_names, () => $"attribute_names is null !!!");
var attribute_values = expression.ExpressionAttributeValues;
NullReferenceCheckHelper.throwIfNull(attribute_values, () => $"attribute_values is null !!!");
result = tryFillupTimestamps( ref statement
, attribute_names, attribute_values
, DateTimeHelper.Current
, isUpsert );
if (result.isFail())
{
return result;
}
expression.ExpressionStatement = statement;
return result;
}
public static void fillupTimestamps(this PutItemRequest putItemRequest)
{
var attributes = putItemRequest.Item;
NullReferenceCheckHelper.throwIfNull(attributes, () => $"attributes is null !!!");
fillupAttributeValueWithTimestampsForInsert(attributes, DateTimeHelper.Current);
}
public static Result tryFillupTimestamps(this UpdateItemRequest updateItemRequest, bool isUpsert = false)
{
var result = new Result();
var err_msg = string.Empty;
var statement = updateItemRequest.UpdateExpression;
NullReferenceCheckHelper.throwIfNull(statement, () => $"statement is null !!! - {updateItemRequest.toBasicString()}");
var attribute_names = updateItemRequest.ExpressionAttributeNames;
NullReferenceCheckHelper.throwIfNull(attribute_names, () => $"attribute_names is null !!! - {updateItemRequest.toBasicString()}");
var attribute_values = updateItemRequest.ExpressionAttributeValues;
NullReferenceCheckHelper.throwIfNull(attribute_values, () => $"attribute_values is null !!! - {updateItemRequest.toBasicString()}");
result = tryFillupTimestamps( ref statement
, attribute_names, attribute_values
, DateTimeHelper.Current
, isUpsert );
if(result.isFail())
{
return result;
}
updateItemRequest.UpdateExpression = statement;
return result;
}
private static Result tryFillupTimestamps( ref string statement
, Dictionary<string, string> attributeNames, Dictionary<string, AttributeValue> attributeValues
, DateTime currentTime
, bool isUpsert = false )
{
var result = new Result();
var err_msg = string.Empty;
result = fillupStatementForTimestamps(ref statement, isUpsert);
if (result.isFail())
{
return result;
}
fillupAttributeNameForTimestamps(attributeNames, isUpsert);
fillupAttributeValueWithTimestamps(attributeValues, currentTime, isUpsert);
return result;
}
private static Result tryFillupTimestamps( ref string statement
, Dictionary<string, string> attributeNames, Dictionary<string, DynamoDBEntry> attributeValues
, DateTime currentTime
, bool isUpsert = false )
{
var result = new Result();
var err_msg = string.Empty;
result = fillupStatementForTimestamps(ref statement, isUpsert);
if (result.isFail())
{
return result;
}
fillupAttributeNameForTimestamps(attributeNames, isUpsert);
fillupDynamoDBEntryWithTimestamps(attributeValues, currentTime, isUpsert);
return result;
}
private static Result fillupStatementForTimestamps(ref string statement, bool isUpsert = false)
{
var result = new Result();
var err_msg = string.Empty;
if (statement.isNullOrWhiteSpace())
{
return result;
}
if (false == statement.Contains("SET"))
{
err_msg = $"Not found SET keyword in ExpressionStatement !!!";
result.setFail(ServerErrorCode.DynamoDbExpressionError, err_msg);
Log.getLogger().error(err_msg);
return result;
}
// Expression 설정: UpdatedTime을 설정하고, 항상 UpdatedTime 갱신 한다.
if (false == statement.Contains($"#{DynamoDbDocBase.UpdatedDateTime}"))
{
if (true == statement.Contains("#") || true == statement.Contains(":"))
{
statement += ",";
}
statement += $" #{DynamoDbDocBase.UpdatedDateTime} = :{DynamoDbDocBase.UpdatedDateTime}";
}
if (true == isUpsert)
{
// Expression 설정: 아이템이 없을 때 CreatedTime을 설정하고, 항상 UpdatedTime 갱신 한다.
if (false == statement.Contains($"#{DynamoDbDocBase.CreatedDateTime}"))
{
statement += $", #{DynamoDbDocBase.CreatedDateTime} = if_not_exists(#{DynamoDbDocBase.CreatedDateTime}, :{DynamoDbDocBase.CreatedDateTime})";
}
}
return result;
}
private static void fillupAttributeValueWithTimestampsForInsert( Dictionary<string, AttributeValue> attributeValues, DateTime currentTime )
{
// 현재 시간 설정
var current_time = currentTime.toStringWithUtcIso8601();
attributeValues[DynamoDbDocBase.CreatedDateTime] = new AttributeValue { S = current_time };
attributeValues[DynamoDbDocBase.UpdatedDateTime] = new AttributeValue { S = current_time };
}
private static void fillupAttributeNameForTimestamps( Dictionary<string, string> attributeNames
, bool isUpsert = false )
{
attributeNames[$"#{DynamoDbDocBase.UpdatedDateTime}"] = $"{DynamoDbDocBase.UpdatedDateTime}";
if (true == isUpsert)
{
attributeNames[$"#{DynamoDbDocBase.CreatedDateTime}"] = $"{DynamoDbDocBase.CreatedDateTime}";
}
}
private static void fillupAttributeValueWithTimestamps( Dictionary<string, AttributeValue> attributeValues
, DateTime currentTime
, bool isUpsert = false )
{
// 현재 시간 설정
var current_time = currentTime.toStringWithUtcIso8601();
attributeValues[$":{DynamoDbDocBase.UpdatedDateTime}"] = new AttributeValue { S = current_time };
if (true == isUpsert)
{
attributeValues[$":{DynamoDbDocBase.CreatedDateTime}"] = new AttributeValue { S = current_time };
}
}
private static void fillupDynamoDBEntryWithTimestamps( Dictionary<string, DynamoDBEntry> attributeValues
, DateTime currentTime
, bool isUpsert = false )
{
attributeValues[$":{DynamoDbDocBase.CreatedDateTime}"] = currentTime;
if (true == isUpsert)
{
attributeValues[$":{DynamoDbDocBase.CreatedDateTime}"] = currentTime;
}
}
public static async Task<(Result, bool)> checkIfDocumentExists( this DynamoDbClient dbClient
, DYNAMO_DB_TABLE_NAME tableName
, Document toCheckDocument
, bool isConsistentRead = false)
{
ArgumentNullReferenceCheckHelper.throwIfNull(toCheckDocument, () => $"toCheckDocument is null !!!");
var db_connector = dbClient.getDbClient();
NullReferenceCheckHelper.throwIfNull(db_connector, () => $"db_connector is null !!! - {toCheckDocument.toBasicString()}, tableName:{tableName}");
var result = new Result();
var err_msg = string.Empty;
var projection_field = DynamoDbDocBase.CreatedDateTime;
(result, var primary_key) = toCheckDocument.toPrimaryKey();
if(result.isFail())
{
return (result, false);
}
NullReferenceCheckHelper.throwIfNull(primary_key, () => $"primary_key is null !!! - {toCheckDocument.toBasicString()}, tableName:{tableName}");
// GetItemRequest 설정
var get_request = new GetItemRequest
{
TableName = tableName,
Key = primary_key.toKeyWithAttributeValue(),
ProjectionExpression = projection_field,
ConsistentRead = isConsistentRead // 일관된 읽기를 사용하지 않음 (성능 최적화)
};
try
{
var response = await db_connector.GetItemAsync(get_request);
if(false == response.IsItemSet)
{
err_msg = $"Not found PrimaryKey in DynamoDb !!! - DbTable:{tableName}, {primary_key.toBasicString()}";
Log.getLogger().warn(err_msg);
return (result, false);
}
if (true == response.Item.TryGetValue(projection_field, out var found_date_time))
{
toCheckDocument[projection_field] = found_date_time.S;
}
}
catch (Exception e)
{
err_msg = $"Exception !!!, Failed to perform in checkIfPrimaryKeyExists() !!! : exception:{e} - {primary_key.toBasicString()}, tableName:{tableName}";
result.setFail(ServerErrorCode.DynamoDbException, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, false);
}
return (result, true);
}
public static DynamoDbDocumentQueryContext createDocumentQueryContext( this Amazon.DynamoDBv2.DocumentModel.Document document
, DYNAMO_DB_TABLE_FULL_NAME tableFullName
, QueryType queryType
, DynamoDbQueryExceptionNotifier.ExceptionHandler? exceptionHandler = null )
{
return new DynamoDbDocumentQueryContext(tableFullName, document, queryType, exceptionHandler);
}
public static Result tryFillupTimestamps(this Amazon.DynamoDBv2.DocumentModel.Document document, QueryType queryType)
{
var result = new Result();
var err_msg = string.Empty;
var currenct_time = DateTimeHelper.Current;
if (QueryType.Insert == queryType)
{
document[DynamoDbDocBase.CreatedDateTime] = currenct_time;
document[DynamoDbDocBase.UpdatedDateTime] = currenct_time;
}
else if(QueryType.Update == queryType)
{
document[DynamoDbDocBase.UpdatedDateTime] = currenct_time;
}
else
{
err_msg = $"Invalid DynamoDbDocumentQueryContext.QueryType !!! : queryType:{queryType} - {document.toPKSK()}";
result.setFail(ServerErrorCode.DynamoDbDocumentQueryContextTypeInvalid, err_msg);
return result;
}
return result;
}
public static string getPK(this Amazon.DynamoDBv2.DocumentModel.Document document)
{
var doc_type = "Empty !!!";
if (false == document.TryGetValue(PrimaryKey.PK_Define, out var db_entry))
{
Log.getLogger().fatal($"Not found PrimaryKey.PK_Define !!! : {document.toBasicString()}");
return doc_type;
}
return db_entry.AsString();
}
public static string getSK(this Amazon.DynamoDBv2.DocumentModel.Document document)
{
var doc_type = "Empty !!!";
if (false == document.TryGetValue(PrimaryKey.SK_Define, out var db_entry))
{
Log.getLogger().fatal($"Not found PrimaryKey.SK_Define !!! : {document.toBasicString()}");
return doc_type;
}
return db_entry.AsString();
}
public static string toPKSK(this Amazon.DynamoDBv2.DocumentModel.Document document)
{
return $"PK:{document.getPK()}, SK:{document.getSK()}";
}
public static string getDocType(this Amazon.DynamoDBv2.DocumentModel.Document document)
{
var doc_type = "Empty !!!";
if(false == document.TryGetValue("DocType", out var db_entry))
{
Log.getLogger().fatal($"Not found DocType !!! : {document.toBasicString()}");
return doc_type;
}
return db_entry.AsString(); }
public static bool isValid(this Amazon.DynamoDBv2.DocumentModel.Document document)
{
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;
}
public static bool fillupAttribObject<TAttrib>(this Amazon.DynamoDBv2.DocumentModel.Document document, out TAttrib? fillupAttrib)
where TAttrib : AttribBase, new()
{
fillupAttrib = null;
var attrib_type_name = typeof(TAttrib).Name;
try
{
var dynamo_db_entry = document.findAttribObjectWithCaseInsensitive<TAttrib>();
if (null == dynamo_db_entry)
{
Log.getLogger().fatal($"Failed to findAttribObjectWithCaseInsensitive() !!!, Not found AttribType in Document of DynamoDbTable !!! - AttribType:{attrib_type_name}");
return false;
}
TAttrib? attrib_object = null;
if (dynamo_db_entry.isJsonEncoded())
{
attrib_object = JsonConvert.DeserializeObject<TAttrib>(dynamo_db_entry.AsString());
if (null == attrib_object)
{
Log.getLogger().fatal($"Failed to JsonConvert.DeserializeObject<TAttrib> in DynamoDBEntry of DynamoDbTable !!! - AttribType:{attrib_type_name}");
return false;
}
}
else
{
attrib_object = DynamoDbClientHelper.dynamoDbDocumentToClass<TAttrib>(dynamo_db_entry);
if (null == attrib_object)
{
Log.getLogger().fatal($"Failed to DynamoDbClientHelper.dynamoDbDocumentToClass<TAttrib> in DynamoDBEntry of DynamoDbTable !!! - AttribType:{attrib_type_name}");
return false;
}
}
fillupAttrib = attrib_object;
return true;
}
catch (Exception e)
{
var error_code = ServerErrorCode.TryCatchException;
Log.getLogger().fatal($"Exception !!!, Failed to get AttribBase in DynamoDBEntry of DynamoDbTable !!! : errorCode:{error_code}, exception:{e} - AttribType:{attrib_type_name}");
return false;
}
}
public static Amazon.DynamoDBv2.DocumentModel.DynamoDBEntry? findAttribObjectWithCaseInsensitive<TAttrib>(this Amazon.DynamoDBv2.DocumentModel.Document document)
where TAttrib : AttribBase
{
var attrib_type_name = typeof(TAttrib).Name;
var attribute_map = document.ToAttributeMap();
foreach(var each in attribute_map)
{
if(true == each.Key.Equals(attrib_type_name, StringComparison.OrdinalIgnoreCase))
{
return document[each.Key];
}
}
return null;
}
public static bool setAttribObject<TAttrib>(this Amazon.DynamoDBv2.DocumentModel.Document document, TAttrib fillupAttrib)
where TAttrib : AttribBase
{
var attrib_type_name = typeof(TAttrib).Name;
var is_save_to_json_in_db = fillupAttrib.isSaveToJsonInDb();
try
{
var dynamo_db_entry = document.findAttribObjectWithCaseInsensitive<TAttrib>();
if (null == dynamo_db_entry)
{
if(true == is_save_to_json_in_db)
{
document[attrib_type_name] = fillupAttrib.toJsonString();
}
else
{
document[attrib_type_name] = fillupAttrib.toDocument();
}
}
else
{
if (true == is_save_to_json_in_db)
{
dynamo_db_entry = fillupAttrib.toJsonString();
}
else
{
dynamo_db_entry = fillupAttrib.toDocument();
}
}
return true;
}
catch (Exception e)
{
var error_code = ServerErrorCode.TryCatchException;
Log.getLogger().fatal($"Exception !!!, Failed to set AttribBase in DynamoDBEntry of DynamoDbTable !!! : errorCode:{error_code}, exception:{e} - AttribType:{attrib_type_name}");
return false;
}
}
public static bool getTimestampByDB_TIMESTAMP(this Amazon.DynamoDBv2.DocumentModel.Document document, DB_TIMESTAMP timestampName, out DateTime createdDateTime)
{
createdDateTime = DateTime.MinValue;
try
{
if(false == document.TryGetValue(timestampName, out var datetime))
{
return false;
}
createdDateTime = datetime.AsString().toUtcTime();
return true;
}
catch (Exception e)
{
var error_code = ServerErrorCode.TryCatchException;
Log.getLogger().fatal($"Exception !!!, Failed to get document[{timestampName}] in DynamoDbTable !!! : errorCode:{error_code}, exception:{e} - {document.toBasicString()}");
}
return false;
}
public static async Task<(Result, TDoc?)> simpleQueryDocTypeWithQueryOperationConfig<TDoc>( this DynamoDbClient dbClient
, QueryOperationConfig queryOperationConfig
, bool isFailOnEmptyDoc = true
, string eventTid = "" )
where TDoc : DynamoDbDocBase, new()
{
var result = new Result();
var err_msg = string.Empty;
var to_copy_doc = new TDoc();
var table = dbClient.getTableByName(to_copy_doc.TableName);
var search = table.queryWithStopwatch(queryOperationConfig, eventTid);
if (search == null)
{
err_msg = $"Failed to Table.Query !!! : Doc:{typeof(TDoc).Name} - {table.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbQueryFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
var next_documents = await search.GetNextSetAsync();
if ( null == next_documents
|| 0 >= next_documents.Count)
{
if(true == isFailOnEmptyDoc)
{
err_msg = $"No match Doc !!! : Doc:{typeof(TDoc).Name} - {table.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbQueryNoMatchAttribute, err_msg);
Log.getLogger().error(result.toBasicString());
}
return (result, null);
}
var source_document = next_documents[0];
result = await to_copy_doc.onCopyFromDocument(source_document);
if (result.isFail())
{
err_msg = $"Failed to onCopyFromDocument() !!! : {result.toBasicString()}, TgtDoc:{typeof(TDoc).Name} <= SrcDoc:{source_document.toBasicString()} - {table.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbDocumentCopyFailedToDoc, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
return (result, to_copy_doc);
}
public static async Task<(Result, List<TDoc>)> simpleQueryDocTypesWithQueryOperationConfig<TDoc>( this DynamoDbClient dbClient
, QueryOperationConfig queryOperationConfig
, bool isFailOnEmptyDoc = false
, string eventTid = "" )
where TDoc : DynamoDbDocBase, new()
{
var result = new Result();
var err_msg = string.Empty;
var table = dbClient.getTableByDoc<TDoc>();
var doc_bases = new List<TDoc>();
var search = table.queryWithStopwatch(queryOperationConfig, eventTid);
while (search.IsDone == false)
{
var next_documents = await search.GetNextSetAsync();
if(null == next_documents)
{
break;
}
foreach (var document in next_documents)
{
var doc = new TDoc();
result = await doc.onCopyFromDocument(document);
if (result.isFail())
{
err_msg = $"Failed to onCopyFromDocument() !!! : {result.toBasicString()}, TgtDoc:{typeof(TDoc).Name} <= SrcDoc:{document.toBasicString()} - {table.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbDocumentCopyFailedToDoc, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, doc_bases);
}
doc_bases.Add(doc);
}
}
if( 0 >= doc_bases.Count
&& true == isFailOnEmptyDoc)
{
err_msg = $"No match Doc !!! : Doc:{typeof(TDoc).Name} - {table.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbQueryNoMatchAttribute, err_msg);
Log.getLogger().error(result.toBasicString());
}
return (result, doc_bases);
}
public static async Task<(Result, TDoc?)> simpleQueryDocTypeWithScanOperationConfig<TDoc>( this DynamoDbClient dbClient
, ScanOperationConfig scanOperationConfig)
where TDoc : DynamoDbDocBase, new()
{
var result = new Result();
var err_msg = string.Empty;
var table = dbClient.getTableByDoc<TDoc>();
var search = table.Scan(scanOperationConfig);
if (search == null)
{
err_msg = $"Failed to Table.Query !!! : Doc:{typeof(TDoc).Name} - {table.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbQueryFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
var next_documents = await search.GetNextSetAsync();
if ( null == next_documents
|| 0 >= next_documents.Count)
{
err_msg = $"No match Doc !!! : Doc:{typeof(TDoc).Name} - {table.TableName}";
result.setFail(ServerErrorCode.DynamoDbQueryNoMatchAttribute, err_msg);
Log.getLogger().warn(result.toBasicString());
return (result, null);
}
var source_document = next_documents[0];
var to_copy_doc = new TDoc();
result = await to_copy_doc.onCopyFromDocument(source_document);
if (result.isFail())
{
err_msg = $"Failed to onCopyFromDocument !!! : {result.toBasicString()}, TgtDoc:{typeof(TDoc).Name} <= SrcDoc:{source_document.toBasicString()} - {table.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbDocumentCopyFailedToDoc, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
return (result, to_copy_doc);
}
public static async Task<(Result, List<TDoc>)> simpleQueryDocTypesWithScanOperationConfig<TDoc>( this DynamoDbClient dbClient
, ScanOperationConfig scanOperationConfig
, bool isFailOnCriticalError = false )
where TDoc : DynamoDbDocBase, new()
{
var result = new Result();
var err_msg = string.Empty;
var table = dbClient.getTableByDoc<TDoc>();
var doc_bases = new List<TDoc>();
var search = table.Scan(scanOperationConfig);
while (search.IsDone == false)
{
var next_documents = await search.GetNextSetAsync();
if(null == next_documents)
{
return (result, doc_bases);
}
foreach (var document in next_documents)
{
var doc = new TDoc();
result = await doc.onCopyFromDocument(document);
if (result.isFail())
{
if (true == isFailOnCriticalError)
{
err_msg = $"Failed to onCopyFromDocument() !!!, by Critical Error !!! : {result.toBasicString()}, TgtDoc:{typeof(TDoc).Name} <= SrcDoc:{document.toBasicString()} - {table.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, doc_bases);
}
else
{
err_msg = $"Failed to onCopyFromDocument() !!! : {result.toBasicString()}, TgtDoc:{typeof(TDoc).Name} <= SrcDoc:{document.toBasicString()} - {table.toBasicString()}";
Log.getLogger().warn(err_msg);
}
continue;
}
doc_bases.Add(doc);
}
}
return (result, doc_bases);
}
public static async Task<(Result, TDoc?)> simpleQueryTransactReadDocWithItemRequest<TDoc>( this DynamoDbClient dbClient
, TransactGetItem toReadTransactQuery
, bool isFailOnEmptyData = false
, UInt16 retryCount = 3 )
where TDoc : DynamoDbDocBase, new()
{
var result = new Result();
var err_msg = string.Empty;
var get_items = new List<TransactGetItem>() { toReadTransactQuery };
(result, var read_documents) = await dbClient.simpleTransactReadOfItemRequestWithItemRequest(get_items, isFailOnEmptyData, retryCount);
if(result.isFail())
{
return (result, null);
}
foreach (var each in read_documents)
{
var table_name = each.Key;
var documents = each.Value;
foreach (var document in documents)
{
var doc = new TDoc();
result = await doc.onCopyFromDocument(document);
if (result.isFail())
{
err_msg = $"Failed to onCopyFromDocument() !!! : {result.toBasicString()}, TgtDoc:{typeof(TDoc).Name} <= SrcDoc:{document.toBasicString()} - {table_name}";
result.setFail(ServerErrorCode.DynamoDbDocumentCopyFailedToDoc, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
return (result, doc);
}
}
return (result, null);
}
public static async Task<(Result, Dictionary<DYNAMO_DB_TABLE_NAME, List<TDoc>>)> simpleQueryTransactReadDocsWithItemRequest<TDoc>( this DynamoDbClient dbClient
, List<TransactGetItem> toReadTransactQueries
, bool isFailOnEmptyData = false
, UInt16 retryCount = 3 )
where TDoc : DynamoDbDocBase, new()
{
var result = new Result();
var err_msg = string.Empty;
var doc_bases = new Dictionary<DYNAMO_DB_TABLE_NAME, List<TDoc>>();
(result, var read_documents) = await dbClient.simpleTransactReadOfItemRequestWithItemRequest(toReadTransactQueries, isFailOnEmptyData, retryCount);
if(result.isFail())
{
return (result, doc_bases);
}
foreach (var each in read_documents)
{
var table_name = each.Key;
var documents = each.Value;
if (false == read_documents.TryGetValue(table_name, out var has_documents))
{
has_documents = new List<Document>();
read_documents.Add(table_name, has_documents);
}
foreach (var document in documents)
{
var doc = new TDoc();
result = await doc.onCopyFromDocument(document);
if (result.isFail())
{
err_msg = $"Failed to onCopyFromDocument() !!! : {result.toBasicString()}, TgtDoc:{typeof(TDoc).Name} <= SrcDoc:{document.toBasicString()} - {table_name}";
result.setFail(ServerErrorCode.DynamoDbDocumentCopyFailedToDoc, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, doc_bases);
}
has_documents.Add(document);
}
}
return (result, doc_bases);
}
public static async Task<Result> simpleInsertDocumentWithDocType<TDoc>( this DynamoDbClient dynamoDbClient, TDoc docBase
, string eventTid = "")
where TDoc : DynamoDbDocBase
{
var result = new Result();
var err_msg = string.Empty;
var table = dynamoDbClient.getTableByName(docBase.TableName);
if (QueryType.Insert != docBase.getQueryType())
{
result = await docBase.newDoc4Query();
if (result.isFail())
{
err_msg = $"Failed to newDoc4Query() !!! : {result.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
}
(result, var copied_doc) = await docBase.onCopyToDocument();
if (result.isFail())
{
err_msg = $"Failed to onCopyToDocument() !!! : {result.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
result = await table.simpleInsertDocument(copied_doc, eventTid);
if (result.isFail())
{
err_msg = $"Failed to simpleInsertDocument() !!! : {result.toBasicString()}, {copied_doc.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(result.toBasicString());
return result;
}
Log.getLogger().debug($"Db insert from simpleInsertDocument() !!! : {copied_doc.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}");
return result;
}
public static async Task<Result> simpleUpsertDocumentWithDocType<TDoc>( this DynamoDbClient dynamoDbClient, TDoc docBase
, string eventTid = "")
where TDoc : DynamoDbDocBase
{
var result = new Result();
var err_msg = string.Empty;
var table = dynamoDbClient.getTableByName(docBase.TableName);
if (QueryType.Upsert != docBase.getQueryType())
{
result = await docBase.upsertDoc4Query();
if (result.isFail())
{
err_msg = $"Failed to upsertDoc4Query() !!! : {result.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
}
(result, var copied_doc) = await docBase.onCopyToDocument();
if (result.isFail())
{
err_msg = $"Failed to onCopyToDocument() !!! : {result.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
result = await copied_doc.tryFillupTimestampsForUpsert(dynamoDbClient, table.TableName);
if(result.isFail())
{
err_msg = $"Failed to tryFillupTimestampsForUpsert() !!! : {result.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
(result, _) = await table.simpleUpsertDocument(copied_doc, eventTid);
if (result.isFail())
{
err_msg = $"Failed to simpleUpsertDocument() !!! : {result.toBasicString()}, {copied_doc.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(result.toBasicString());
return result;
}
Log.getLogger().debug($"Db upsert from simpleUpsertDocument() !!! : {copied_doc.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}");
return result;
}
public static async Task<Result> simpleUpdateDocumentWithDocType<TDoc>( this DynamoDbClient dynamoDbClient, TDoc docBase
, string eventTid = "" )
where TDoc : DynamoDbDocBase
{
var result = new Result();
var err_msg = string.Empty;
var table = dynamoDbClient.getTableByName(docBase.TableName);
if (QueryType.Update != docBase.getQueryType())
{
result = await docBase.updateDoc4Query();
if (result.isFail())
{
err_msg = $"Failed to updateDoc4Query() !!! : {result.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
}
(result, var copied_doc) = await docBase.onCopyToDocument();
if (result.isFail())
{
err_msg = $"Failed to onCopyToDocument() !!! : {result.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
(result, _) = await table.simpleUpdateDocument(copied_doc, eventTid);
if (result.isFail())
{
err_msg = $"Failed to simpleUpdateDocument() !!! : {result.toBasicString()}, {copied_doc.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}";
Log.getLogger().error(err_msg);
return result;
}
Log.getLogger().debug($"Db update from simpleUpdateDocument() !!! : {copied_doc.toBasicString()} - {typeof(TDoc).Name}, {table.toBasicString()}");
return result;
}
public static async Task<Result> simpleDeleteDocumentWithDocType<TDoc>( this DynamoDbClient dynamoDbClient, TDoc docBase
, string eventTid = "" )
where TDoc : DynamoDbDocBase
{
var result = new Result();
var err_msg = string.Empty;
var table = dynamoDbClient.getTableByName(docBase.TableName);
if (QueryType.Delete != docBase.getQueryType())
{
result = await docBase.deleteDoc4Query();
if (result.isFail())
{
err_msg = $"Failed to deleteDoc4Query() !!! : {result.toBasicString()} - {typeof(TDoc).Name}";
Log.getLogger().error(err_msg);
return result;
}
}
(result, var copied_doc) = await docBase.onCopyToDocument();
if (result.isFail())
{
err_msg = $"Failed to onCopyToDocument() !!! : {result.toBasicString()} - {typeof(TDoc).Name}";
Log.getLogger().error(err_msg);
return result;
}
result = await table.simpleDeleteDocument(copied_doc, eventTid);
if (result.isFail())
{
err_msg = $"Failed to simpleDeleteDocument() !!! : {result.toBasicString()}, {copied_doc.toBasicString()} - {typeof(TDoc).Name}";
Log.getLogger().error(err_msg);
return result;
}
Log.getLogger().debug($"Db delete from simpleDeleteDocument() !!! : {copied_doc.toBasicString()} - {typeof(TDoc).Name}");
return result;
}
public static async Task<(Result, DynamoDbDocumentQueryContext?)> toDocumentQueryContext<TDoc>( this TDoc dbBaseDoc )
where TDoc : DynamoDbDocBase
{
var result = new Result();
var err_msg = string.Empty;
var server_logic = ServerLogicApp.getServerLogicApp();
var dynamo_db_connector = server_logic.getDynamoDbClient();
(result, var copied_doc) = await dbBaseDoc.onCopyToDocument();
if (result.isFail())
{
err_msg = $"Failed to onCopyToDocument() from TDoc !!! : {result.toBasicString()} - {typeof(TDoc).Name}";
Log.getLogger().error(err_msg);
return (result, null);
}
return (result, new DynamoDbDocumentQueryContext(dynamo_db_connector.getTableFullName(dbBaseDoc.TableName), copied_doc, dbBaseDoc.getQueryType(), dbBaseDoc.getExceptionHandler()));
}
public static async Task<(Result, TAttrib?)> simpleQueryDocTypeToAttrib<TDoc, TAttrib>( this DynamoDbClient dynamoDbClient
, string pk, string sk = DynamoDbClient.SK_EMPTY
, string eventTid = "")
where TDoc : DynamoDbDocBase, new()
where TAttrib : AttribBase
{
var result = new Result();
var err_msg = string.Empty;
(result, var make_primary_key) = await makePrimaryKey<TDoc>(pk, sk);
if (result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!!");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK);
(result, var found_base_doc) = await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig<TDoc>(query_config, eventTid:eventTid);
if (result.isFail())
{
err_msg = $"Failed to simpleQueryDocTypeWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(found_base_doc, () => $"found_base_doc is null !!! - {make_primary_key.toBasicString()}");
var found_attrib = found_base_doc.getAttrib<TAttrib>();
NullReferenceCheckHelper.throwIfNull(found_attrib, () => $"found_attrib is null !!! - {make_primary_key.toBasicString()}");
return (result, found_attrib);
}
public static async Task<(Result, List<TAttrib>?)> simpleQueryDocTypesToAttrib<TDoc, TAttrib>( this DynamoDbClient dynamoDbClient
, string pk, string sk = DynamoDbClient.SK_EMPTY
, string eventTid = "")
where TDoc : DynamoDbDocBase, new()
where TAttrib : AttribBase
{
var result = new Result();
var err_msg = string.Empty;
(result, var make_primary_key) = await makePrimaryKey<TDoc>(pk, sk);
if (result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!!");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK);
(result, var found_base_doc_list) = await dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig<TDoc>(query_config, eventTid:eventTid);
if (result.isFail())
{
err_msg = $"Failed to simpleQueryDocTypesWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, null);
}
var found_attrib_list = new List<TAttrib>();
foreach(var found_base_doc in found_base_doc_list)
{
var found_attrib = found_base_doc.getAttrib<TAttrib>();
NullReferenceCheckHelper.throwIfNull(found_attrib, () => $"found_attrib is null !!! - {make_primary_key.toBasicString()}");
found_attrib_list.Add(found_attrib);
}
return (result, found_attrib_list);
}
public static (Result, PrimaryKey?) toPrimaryKey(this Amazon.DynamoDBv2.DocumentModel.Document document)
{
var result = new Result();
var err_msg = string.Empty;
if(false == document.TryGetValue(PrimaryKey.PK_Define, out var found_pk))
{
err_msg = $"Not found PK in Document !!! : {document.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbPrimaryKeyNotFound, err_msg);
return (result, null);
}
var pk = found_pk.AsString();
if (false == document.TryGetValue(PrimaryKey.SK_Define, out var found_sk))
{
err_msg = $"Not found SK in Document !!! : {document.toBasicString()}";
result.setFail(ServerErrorCode.DynamoDbPrimaryKeyNotFound, err_msg);
return (result, null);
}
var sk = found_sk.AsString();
return (result, new PrimaryKey(pk, sk));
}
public static async Task<(Result, TDoc?)> simpleQueryDocTypesWithUpdateItemRequest<TDoc>( this DynamoDbClient dbClient
, UpdateItemRequest updateItemRequest
, string eventTid = "")
where TDoc : DynamoDbDocBase, new()
{
var result = new Result();
var err_msg = string.Empty;
(result, var updated_doc) = await dbClient.simpleQueryWithUpdateItemRequest(updateItemRequest, false, eventTid);
if(result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(updated_doc, () => $"updated_doc is null !!!");
var doc = new TDoc();
result = await doc.onCopyFromDocument(updated_doc);
if (result.isFail())
{
err_msg = $"Failed to onCopyFromDocument() !!! : {result.toBasicString()}, TgtDoc:{typeof(TDoc).Name} <= SrcDoc:{updated_doc.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, null);
}
return (result, doc);
}
public static async Task<Result> createIfNotExist<TDoc>( this DynamoDbClient dbClient
, TDoc toInsertDoc, Action<TDoc> initFunc)
where TDoc : DynamoDbDocBase, new()
{
var result = new Result();
var make_primary_key = toInsertDoc.getPrimaryKey();
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!!");
(result, var is_exist) = await dbClient.checkIfAttributeExists(dbClient.getTableFullName(toInsertDoc.TableName), make_primary_key);
if(result.isFail())
{
return result;
}
if(false == is_exist)
{
if(null != initFunc)
{
initFunc(toInsertDoc);
}
return await dbClient.simpleInsertDocumentWithDocType<TDoc>(toInsertDoc);
}
return result;
}
public static async Task<(Result, bool)> checkIfAttributeExists( this DynamoDbClient dbClient
, DYNAMO_DB_TABLE_FULL_NAME tableFullName
, PrimaryKey targetPrimaryKey
, bool isConsistentRead = false)
{
var db_connector = dbClient.getDbClient();
NullReferenceCheckHelper.throwIfNull(db_connector, () => $"db_connector is null !!! - {targetPrimaryKey.toBasicString()}");
ConditionValidCheckHelper.throwIfFalseWithCondition(() => false == tableFullName.isNullOrWhiteSpace(), () => $"Invalid TableFullName !!! - {targetPrimaryKey.toBasicString()}");
var result = new Result();
var err_msg = string.Empty;
var projection_field = DynamoDbDocBase.CreatedDateTime;
// GetItemRequest 설정
var get_request = new GetItemRequest
{
TableName = tableFullName,
Key = targetPrimaryKey.toKeyWithAttributeValue(),
ProjectionExpression = projection_field,
ConsistentRead = isConsistentRead // 일관된 읽기를 사용하지 않음 (성능 최적화)
};
try
{
var response = await db_connector.GetItemAsync(get_request);
if(false == response.IsItemSet)
{
err_msg = $"Not found PrimaryKey in DynamoDb !!! : {get_request.toBasicString()}";
Log.getLogger().warn(err_msg);
return (result, false);
}
}
catch (Exception e)
{
var error_code = ServerErrorCode.DynamoDbException;
err_msg = $"Exception !!!, Failed to perform in checkIfPrimaryKeyExists() !!! : errorCode:{error_code}, exception:{e}, {get_request.toBasicString()}";
result.setFail(error_code, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, false);
}
return (result, true);
}
public static bool setAttribObjectWithJsonString<TAttrib>(this JObject jobject, TAttrib fillupAttrib)
where TAttrib : AttribBase
{
var attrib_type_name = typeof(TAttrib).Name;
try
{
jobject[attrib_type_name] = JObject.Parse(fillupAttrib.toJsonString());
return true;
}
catch (Exception e)
{
var error_code = ServerErrorCode.TryCatchException;
Log.getLogger().fatal($"Exception !!!, Failed to perform in setAttribObjectWithJsonString<{attrib_type_name}>() !!! : errorCode:{error_code}, exception:{e} - AttribType:{attrib_type_name}");
return false;
}
}
}