/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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. */ using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; using Amazon.DynamoDBv2.Model; using Amazon.DynamoDBv2.Model.Internal.MarshallTransformations; using Amazon.Runtime.Internal.Transform; using Amazon.Util.Internal; #pragma warning disable 1591 namespace Amazon.DynamoDBv2.DocumentModel { /// /// An enumeration of all supported return value directives /// public enum ReturnValues { None, AllOldAttributes, AllNewAttributes, UpdatedOldAttributes, UpdatedNewAttributes } /// /// An enumeration of supported return value directives /// for scenarios where attributes are returned when the condition check fails. /// public enum ReturnValuesOnConditionCheckFailure { None, AllOldAttributes } /// /// An enumeration of all supported scan operator directives /// public enum ScanOperator { Equal, NotEqual, LessThanOrEqual, LessThan, GreaterThanOrEqual, GreaterThan, IsNotNull, IsNull, Contains, NotContains, BeginsWith, In, Between } /// /// An enumeration of all supported query operator directives /// public enum QueryOperator { Equal, LessThanOrEqual, LessThan, GreaterThanOrEqual, GreaterThan, BeginsWith, Between } /// /// An enumeration of all supported Select values for Query and Scan. /// Value of Count will force service to return the number of items, /// not the items themselves. /// public enum SelectValues { Count, SpecificAttributes, AllAttributes, AllProjectedAttributes } /// /// An enumeration of all supported ConditionalOperator values. /// Value of And for an operation will require that all conditions match. /// Value of Or for an operation will require that at least one of the conditons match. /// public enum ConditionalOperatorValues { /// /// And condition /// And, /// /// Or Condition /// Or } internal enum ProjectionTypeValues { All, Include, KeysOnly } internal enum ReturnConsumedCapacityValues { None, Total } internal enum ReturnItemCollectionMetricsValues { None, All } internal enum SearchType { Scan, Query } public class KeyDescription { public DynamoDBEntryType Type { get; internal set; } public bool IsHash { get; internal set; } } internal class Key : Dictionary { public Key(IDictionary values) : base(values) { } public Key() : base() { } } internal static class EnumMapper { public static string Convert(ReturnConsumedCapacityValues value) { switch(value) { case ReturnConsumedCapacityValues.None: return "NONE"; case ReturnConsumedCapacityValues.Total: return "TOTAL"; default: throw new ArgumentOutOfRangeException("value", "Invalid ReturnConsumedCapacityValues value"); } } public static string Convert(ReturnItemCollectionMetricsValues value) { switch (value) { case ReturnItemCollectionMetricsValues.None: return "NONE"; case ReturnItemCollectionMetricsValues.All: return "ALL"; default: throw new ArgumentOutOfRangeException("value", "Invalid ReturnItemCollectionMetricsValues value"); } } public static string Convert(ProjectionTypeValues value) { switch (value) { case ProjectionTypeValues.All: return "ALL"; case ProjectionTypeValues.Include: return "INCLUDE"; case ProjectionTypeValues.KeysOnly: return "KEYS_ONLY"; default: throw new ArgumentOutOfRangeException("value", "Invalid ProjectionTypeValues value"); } } public static string Convert(SelectValues value) { switch (value) { case SelectValues.Count: return "COUNT"; case SelectValues.AllAttributes: return "ALL_ATTRIBUTES"; case SelectValues.AllProjectedAttributes: return "ALL_PROJECTED_ATTRIBUTES"; case SelectValues.SpecificAttributes: return "SPECIFIC_ATTRIBUTES"; default: throw new ArgumentOutOfRangeException("value", "Invalid SelectValues value"); } } public static string Convert(ReturnValues value) { switch (value) { case ReturnValues.None: return "NONE"; case ReturnValues.AllOldAttributes: return "ALL_OLD"; case ReturnValues.AllNewAttributes: return "ALL_NEW"; case ReturnValues.UpdatedOldAttributes: return "UPDATED_OLD"; case ReturnValues.UpdatedNewAttributes: return "UPDATED_NEW"; default: throw new ArgumentOutOfRangeException("value", "Invalid ReturnValues value"); } } public static string Convert(ReturnValuesOnConditionCheckFailure value) { switch (value) { case ReturnValuesOnConditionCheckFailure.None: return "NONE"; case ReturnValuesOnConditionCheckFailure.AllOldAttributes: return "ALL_OLD"; default: throw new ArgumentOutOfRangeException("value", "Invalid ReturnValuesOnConditionCheckFailure value"); } } public static string Convert(ScanOperator value) { switch (value) { case ScanOperator.Equal: return "EQ"; case ScanOperator.NotEqual: return "NE"; case ScanOperator.LessThanOrEqual: return "LE"; case ScanOperator.LessThan: return "LT"; case ScanOperator.GreaterThanOrEqual: return "GE"; case ScanOperator.GreaterThan: return "GT"; case ScanOperator.IsNotNull: return "NOT_NULL"; case ScanOperator.IsNull: return "NULL"; case ScanOperator.Contains: return "CONTAINS"; case ScanOperator.NotContains: return "NOT_CONTAINS"; case ScanOperator.BeginsWith: return "BEGINS_WITH"; case ScanOperator.In: return "IN"; case ScanOperator.Between: return "BETWEEN"; default: throw new ArgumentOutOfRangeException("value", "Invalid ScanOperator value"); } } public static string Convert(QueryOperator value) { switch(value) { case QueryOperator.Equal: return "EQ"; case QueryOperator.LessThanOrEqual: return "LE"; case QueryOperator.LessThan: return "LT"; case QueryOperator.GreaterThanOrEqual: return "GE"; case QueryOperator.GreaterThan: return "GT"; case QueryOperator.BeginsWith: return "BEGINS_WITH"; case QueryOperator.Between: return "BETWEEN"; default: throw new ArgumentOutOfRangeException("value", "Invalid QueryOperator value"); } } public static string Convert(ConditionalOperatorValues value) { switch (value) { case ConditionalOperatorValues.And: return "AND"; case ConditionalOperatorValues.Or: return "OR"; default: throw new ArgumentOutOfRangeException("value", "Invalid ConditionalOperatorValues value"); } } } internal static class Common { private const string AwsVariablePrefix = "awsavar"; // Convert collection of AttributeValueUpdate to an update expression. This is needed when doing an update // with a conditional expression. public static void ConvertAttributeUpdatesToUpdateExpression(Dictionary attributesToUpdates, out string statement, out Dictionary expressionAttributeValues, out Dictionary expressionAttributes) { expressionAttributeValues = new Dictionary(StringComparer.Ordinal); expressionAttributes = new Dictionary(StringComparer.Ordinal); // Build an expression string with a SET clause for the added/modified attributes and // REMOVE clause for the attributes set to null. int attributeCount = 0; StringBuilder sets = new StringBuilder(); StringBuilder removes = new StringBuilder(); foreach (var kvp in attributesToUpdates) { var attribute = kvp.Key; var update = kvp.Value; string variableName = GetVariableName(ref attributeCount); var attributeReference = GetAttributeReference(variableName); var attributeValueReference = GetAttributeValueReference(variableName); if (update.Action == AttributeAction.DELETE) { if (removes.Length > 0) removes.Append(", "); removes.Append(attributeReference); } else { if (sets.Length > 0) sets.Append(", "); sets.AppendFormat("{0} = {1}", attributeReference, attributeValueReference); // Add the attribute value for the variable in the added in the expression expressionAttributeValues.Add(attributeValueReference, update.Value); } // Add the attribute name for the variable in the added in the expression expressionAttributes.Add(attributeReference, attribute); } // Combine the SET and REMOVE clause StringBuilder statementBuilder = new StringBuilder(); if (sets.Length > 0) { statementBuilder.AppendFormat(CultureInfo.InvariantCulture, "SET {0}", sets.ToString()); } if (removes.Length > 0) { if (sets.Length > 0) statementBuilder.Append(" "); statementBuilder.AppendFormat(CultureInfo.InvariantCulture, "REMOVE {0}", removes.ToString()); } statement = statementBuilder.ToString(); } public static void ConvertAttributesToGetToProjectionExpression(QueryRequest request) { if (request.IsSetAttributesToGet() && (request.IsSetExpressionAttributeNames() || request.IsSetExpressionAttributeValues() || request.IsSetFilterExpression())) { var attributesToGet = request.AttributesToGet; var attributeNames = request.ExpressionAttributeNames; request.ProjectionExpression = AttributesToGetAsProjectionExpression(attributesToGet, attributeNames); request.AttributesToGet = null; } } public static void ConvertAttributesToGetToProjectionExpression(ScanRequest request) { if (request.IsSetAttributesToGet() && (request.IsSetExpressionAttributeNames() || request.IsSetExpressionAttributeValues() || request.IsSetFilterExpression())) { var attributesToGet = request.AttributesToGet; var attributeNames = request.ExpressionAttributeNames; request.ProjectionExpression = AttributesToGetAsProjectionExpression(attributesToGet, attributeNames); request.AttributesToGet = null; } } private static string AttributesToGetAsProjectionExpression(List attributesToGet, Dictionary attributeNames) { int attributeCount = attributeNames.Count; var referencesToGet = new List(); foreach (var attribute in attributesToGet) { string attributeReference = GetAttributeReference(attribute, attributeNames, ref attributeCount); referencesToGet.Add(attributeReference); } var projectionExpression = string.Join(", ", referencesToGet.ToArray()); return projectionExpression; } private static string GetAttributeReference(string attribute, Dictionary attributeNames, ref int attributeCount) { string attributeReference; if (!InternalSDKUtils.TryFindByValue(attributeNames, attribute, StringComparer.Ordinal, out attributeReference)) { var variableName = GetVariableName(ref attributeCount); attributeReference = GetAttributeReference(variableName); attributeNames.Add(attributeReference, attribute); } return attributeReference; } public static string GetAttributeReference(string attributeName) { return string.Format(CultureInfo.InvariantCulture, "#{0}", attributeName); } public static string GetAttributeValueReference(string attributeName) { return string.Format(CultureInfo.InvariantCulture, ":{0}", attributeName); } public static string GetVariableName(ref int attributeCount) { attributeCount++; string variableName = AwsVariablePrefix + attributeCount; return variableName; } public static string GetVariableName(string suffix) { return AwsVariablePrefix + suffix; } public static Dictionary Combine( IDictionary items1, IDictionary items2, IEqualityComparer valueComparer) { if (valueComparer == null) valueComparer = EqualityComparer.Default; var result = new Dictionary(items1, StringComparer.Ordinal); foreach (var kvp in items2) { var key = kvp.Key; var value = kvp.Value; // check if the key is already present T existingValue; if (result.TryGetValue(key, out existingValue)) { if (!valueComparer.Equals(value, existingValue)) { // values are different, throw exception throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Key [{0}] exists in both collections, but has different values: [{1}] != [{2}]", key, value, existingValue)); } else { // key and identical value already present, no-op } } else result[key] = value; } return result; } public static string ToPaginationToken(Dictionary nextKey) { if (nextKey == null) return null; var clearToken = SerializeClearString(nextKey); return clearToken; } private static string SerializeClearString(Dictionary nextKey) { using (var writer = new StringWriter(CultureInfo.InvariantCulture)) { WriteNextKey(nextKey, writer); return writer.ToString(); } } public static Dictionary FromPaginationToken(string token) { if (token == null) return null; return DeserializeClearString(token); } private static Dictionary DeserializeClearString(string token) { var bytes = Encoding.UTF8.GetBytes(token); using (var stream = new MemoryStream(bytes)) { return ReadNextKey(stream); } } private static Dictionary ReadNextKey(Stream stream) { JsonUnmarshallerContext context = new JsonUnmarshallerContext(stream, false, null); var unmarshaller = new DictionaryUnmarshaller(StringUnmarshaller.Instance, AttributeValueUnmarshaller.Instance); var nextKey = unmarshaller.Unmarshall(context); return nextKey; } private static void WriteNextKey(Dictionary nextKey, TextWriter writer) { var marshaller = Amazon.DynamoDBv2.Model.Internal.MarshallTransformations.AttributeValueMarshaller.Instance; var jsonWriter = new ThirdParty.Json.LitJson.JsonWriter(writer); var context = new Runtime.Internal.Transform.JsonMarshallerContext(null, jsonWriter); context.Writer.WriteObjectStart(); foreach (var kvp in nextKey) { context.Writer.WritePropertyName(kvp.Key); context.Writer.WriteObjectStart(); { marshaller.Marshall(kvp.Value, context); } context.Writer.WriteObjectEnd(); } context.Writer.WriteObjectEnd(); } } }