Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add reference and custom operations #14

Merged
merged 2 commits into from
Nov 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/DynaMight/AtomicOperations/CustomOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;

namespace DynaMight.AtomicOperations;

/// <summary>
/// Creates a custom operation, that receives Func to execute the internal methods.
/// </summary>
public class CustomOperation : IAtomicOperation
{
private readonly string _getUpdateExpression;
private readonly (string, string) _getNameExpressionFunc;
private readonly (string, AttributeValue, DynamoDBEntry?) _getValueExpressionFunc;

/// <inheritdoc />
public string UpdateExpressionType {get; }

/// <inheritdoc />
public string GetUpdateExpression() => _getUpdateExpression;

/// <inheritdoc />
public (string key, string value) GetNameExpression() => _getNameExpressionFunc;

/// <inheritdoc />
public (string key, AttributeValue AttributeValue, DynamoDBEntry? dynamoDbEntry) GetValueExpression() =>
_getValueExpressionFunc;

/// <summary>
/// Creates a custom operation, that receives Func to execute the internal methods.
/// </summary>
/// <param name="updateExpressionType">Value for <see cref="UpdateExpressionType"/>.</param>
/// <param name="getUpdateExpression">Value for <see cref="GetUpdateExpression"/>.</param>
/// <param name="getNameExpressionFunc">Value for <see cref="GetNameExpression"/>.</param>
/// <param name="getValueExpressionFunc">Value for <see cref="GetValueExpression"/>.</param>
public CustomOperation(string updateExpressionType, string getUpdateExpression,
(string, string) getNameExpressionFunc, (string, AttributeValue, DynamoDBEntry?) getValueExpressionFunc)
{
UpdateExpressionType = updateExpressionType;
_getUpdateExpression = getUpdateExpression;
_getNameExpressionFunc = getNameExpressionFunc;
_getValueExpressionFunc = getValueExpressionFunc;
}
}
32 changes: 32 additions & 0 deletions src/DynaMight/AtomicOperations/ReferenceOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;

namespace DynaMight.AtomicOperations;

/// <summary>
/// Creates an reference entry for the atomic operation
/// </summary>
public class ReferenceOperation : IAtomicOperation
{
/// <inheritdoc />
public string UpdateExpressionType => string.Empty;
private string FieldName { get; }

/// <inheritdoc />
public string GetUpdateExpression() => string.Empty;

/// <inheritdoc />
public (string key, string value) GetNameExpression() => ($"#{FieldName}", FieldName);

/// <inheritdoc />
public (string key, AttributeValue AttributeValue, DynamoDBEntry? dynamoDbEntry) GetValueExpression() => (string.Empty, null!, null);

/// <summary>
/// Creates an reference entry in the AtomicBuilder. Should be used with the CustomOperation
/// </summary>
/// <param name="fieldName">The field's name that will be added. Recommended to use `nameof` function to avoid issues when renaming.</param>
public ReferenceOperation(string fieldName)
{
FieldName = fieldName;
}
}
3 changes: 3 additions & 0 deletions src/DynaMight/Builders/AtomicBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public IAtomicBuilder AddOperation(IAtomicOperation atomicOperation)

AddNameExpression(nameExpression);

if (string.IsNullOrEmpty(atomicOperation.UpdateExpressionType))
return this;

if (!_atomicOperations.ContainsKey(atomicOperation.UpdateExpressionType))
_atomicOperations.Add(atomicOperation.UpdateExpressionType, new List<IAtomicOperation>());

Expand Down
63 changes: 55 additions & 8 deletions tests/DynaMight.UnitTests/Builders/AtomicBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Amazon.DynamoDBv2.Model;
using DynaMight.AtomicOperations;
using DynaMight.Builders;
using DynaMight.Converters;
using DynaMight.Criteria;
using FluentAssertions;

Expand Down Expand Up @@ -137,7 +138,7 @@ public void SetTableName_WhenConfigNotEmpty()
var config = builder.Build();
config.TableName.Should().Be("22cans_DynaMightTestClass");
}

[Fact]
public void SetTableName_WhenConfigEmpty()
{
Expand All @@ -149,7 +150,7 @@ public void SetTableName_WhenConfigEmpty()
var config = builder.Build();
config.TableName.Should().Be("DynaMightTestClass");
}

[Fact]
public void Operation_WhenConditionTrue()
{
Expand All @@ -171,7 +172,8 @@ public void Operation_WhenConditionTrue()
});
}

[Fact] public void Operation_WhenConditionFalse()
[Fact]
public void Operation_WhenConditionFalse()
{
var builder = AtomicBuilder
.Create()
Expand Down Expand Up @@ -205,7 +207,7 @@ public void OneOperation()
{ $":{Field2Name}", new AttributeValue { N = Field2Value.ToString() } }
});
}

[Fact]
public void TwoOperationsSameKind()
{
Expand All @@ -229,7 +231,7 @@ public void TwoOperationsSameKind()
{ $":{Field3Name}", new AttributeValue { N = Field3Value.ToString() } }
});
}

[Fact]
public void TwoOperationsDifferentKind()
{
Expand All @@ -253,7 +255,7 @@ public void TwoOperationsDifferentKind()
{ $":{Field3Name}", new AttributeValue { N = Field3Value.ToString() } }
});
}

[Fact]
public void AddTwoOperationsForSameField_KeepsTheFirst()
{
Expand All @@ -274,7 +276,7 @@ public void AddTwoOperationsForSameField_KeepsTheFirst()
{ $":{Field2Name}", new AttributeValue { N = Field2Value.ToString() } }
});
}

[Fact]
public void AddTwoOperationsForSameField_KeepsTheFirst_AndCriteriaBefore()
{
Expand All @@ -296,7 +298,7 @@ public void AddTwoOperationsForSameField_KeepsTheFirst_AndCriteriaBefore()
{ $":{Field2Name}", new AttributeValue { N = Field2Value.ToString() } }
});
}

[Fact]
public void AddOperationWithEmptyKeyField_DontAdd()
{
Expand All @@ -317,4 +319,49 @@ public void AddOperationWithEmptyKeyField_DontAdd()
{ $":{Field2Name}", new AttributeValue { N = Field2Value.ToString() } }
});
}

[Fact]
public void AddReferenceOperation()
{
var builder = AtomicBuilder
.Create()
.AddOperation(new ReferenceOperation(Field1Name));

var config = builder.Build();
config.UpdateExpression.Should().BeEmpty();

config.ExpressionAttributeNames.Should().BeEquivalentTo(new Dictionary<string, string>
{
{ $"#{Field1Name}", Field1Name }
});
config.ExpressionAttributeValues.Should().BeEmpty();
}

[Fact]
public void AddCustomOperationWithReference()
{
var builder = AtomicBuilder
.Create()
.AddOperation(new CustomOperation(
"SET",
$"#{Field1Name} = :{Field1Name} + #{Field2Name}",
($"#{Field1Name}", Field1Name),
($":{Field1Name}", DynamoValueConverter.ToAttributeValue(Field1Value),
DynamoValueConverter.ToDynamoDbEntry(Field1Value))
))
.AddOperation(new ReferenceOperation(Field2Name));

var config = builder.Build();
config.UpdateExpression.Should().Be($"SET #{Field1Name} = :{Field1Name} + #{Field2Name}");

config.ExpressionAttributeNames.Should().BeEquivalentTo(new Dictionary<string, string>
{
{ $"#{Field1Name}", Field1Name },
{ $"#{Field2Name}", Field2Name }
});
config.ExpressionAttributeValues.Should().BeEquivalentTo(new Dictionary<string, AttributeValue>
{
{ $":{Field1Name}", new AttributeValue { S = Field1Value } }
});
}
}