-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ef98642
commit af572c7
Showing
5 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
name: .NET Build, Test, and Publish Nuget Package | ||
|
||
on: | ||
push: | ||
branches: | ||
- "**" | ||
tags: | ||
- "v[0-9]+.[0-9]+.[0-9]+" | ||
pull_request: | ||
branches: | ||
- "**" | ||
env: | ||
VERSION: 0.0.0 | ||
|
||
defaults: | ||
run: | ||
working-directory: ./ | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Set Version Variable | ||
if: ${{ github.ref_type == 'tag' }} | ||
env: | ||
TAG: ${{ github.ref_name }} | ||
run: echo "VERSION=${TAG#v}" >> $GITHUB_ENV | ||
- name: Setup .NET | ||
uses: actions/setup-dotnet@v2 | ||
with: | ||
dotnet-version: 6.0.x | ||
- name: Restore dependencies | ||
run: dotnet restore | ||
- name: Build | ||
run: dotnet build --no-restore /p:Version=$VERSION | ||
- name: Test | ||
run: dotnet test --no-build --verbosity normal | ||
- name: pack nuget packages | ||
run: dotnet pack --output nupkgs --no-restore --no-build /p:PackageVersion=$VERSION | ||
- name: upload nuget package | ||
if: github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') | ||
run: dotnet nuget push nupkgs/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
# Dynatello | ||
|
||
## What does is do? | ||
A DynamoDB source generator that does the heavy lifting when it comes to using the low-level client in DynamoDB. | ||
|
||
## Features | ||
|
||
Builder patterns to create request builders through the source generated code. | ||
|
||
## Installation | ||
|
||
* Nuget | ||
* [![DynamoDBGenerator][1]][2] | ||
|
||
[1]: https://img.shields.io/nuget/v/Dynatello.svg?label=Dynatello | ||
[2]: https://www.nuget.org/packages/Dynatello | ||
|
||
## Example | ||
|
||
```csharp | ||
using System.Net; | ||
using Amazon.DynamoDBv2; | ||
using Amazon.DynamoDBv2.DataModel; | ||
using Amazon.DynamoDBv2.Model; | ||
using DynamoDBGenerator.Attributes; | ||
using Dynatello; | ||
using Dynatello.Builders; | ||
using Dynatello.Builders.Types; | ||
|
||
ProductRepository productRepository = new ProductRepository("MY_TABLE", new AmazonDynamoDBClient()); | ||
|
||
public class ProductRepository | ||
{ | ||
private readonly IAmazonDynamoDB _amazonDynamoDb; | ||
private readonly GetRequestBuilder<string> _getProductByTable; | ||
private readonly UpdateRequestBuilder<(string Id, decimal NewPrice, DateTime TimeStamp)> _updatePrice; | ||
private readonly PutRequestBuilder<Product> _createProduct; | ||
private readonly QueryRequestBuilder<decimal> _queryByPrice; | ||
|
||
public ProductRepository(string tableName, IAmazonDynamoDB amazonDynamoDb) | ||
{ | ||
_amazonDynamoDb = amazonDynamoDb; | ||
|
||
_getProductByTable = Product.GetById | ||
.OnTable(tableName) | ||
.ToGetRequestBuilder(arg => arg); // Since the ArgumentType is set to string, we don't need to select a property. | ||
_updatePrice = Product.UpdatePrice | ||
.OnTable(tableName) | ||
.WithUpdateExpression((db, arg) => $"SET {db.Price} = {arg.NewPrice}, {db.Metadata.ModifiedAt} = {arg.TimeStamp}") // Specify the update operation | ||
.ToUpdateItemRequestBuilder((marshaller, arg) => marshaller.PartitionKey(arg.Id)); | ||
|
||
_createProduct = Product.Put | ||
.OnTable(tableName) | ||
.WithConditionExpression((db, arg) => $"{db.Id} <> {arg.Id}") // Ensure we don't have an existing Product in DynamoDB | ||
.ToPutRequestBuilder(); | ||
|
||
_queryByPrice = Product.QueryByPrice | ||
.OnTable(tableName) | ||
.WithKeyConditionExpression((db, arg) => $"{db.Price} = {arg}") | ||
.ToQueryRequestBuilder() | ||
with | ||
{ | ||
IndexName = Product.PriceIndex | ||
}; | ||
} | ||
|
||
public async Task<IReadOnlyList<Product>> SearchByPrice(decimal price) | ||
{ | ||
QueryRequest request = _queryByPrice.Build(price); | ||
QueryResponse? response = await _amazonDynamoDb.QueryAsync(request); | ||
|
||
if (response.HttpStatusCode is not HttpStatusCode.OK) | ||
throw new Exception("..."); | ||
|
||
return response.Items | ||
.Select(x => Product.QueryByPrice.Unmarshall(x)) | ||
.ToArray(); | ||
} | ||
|
||
public async Task Create(Product product) | ||
{ | ||
PutItemRequest request = _createProduct.Build(product); | ||
PutItemResponse response = await _amazonDynamoDb.PutItemAsync(request); | ||
|
||
if (response.HttpStatusCode is not HttpStatusCode.OK) | ||
throw new Exception("..."); | ||
} | ||
|
||
public async Task<Product?> GetById(string id) | ||
{ | ||
GetItemRequest request = _getProductByTable.Build(id); | ||
GetItemResponse response = await _amazonDynamoDb.GetItemAsync(request); | ||
|
||
if (response.HttpStatusCode is HttpStatusCode.NotFound) | ||
return null; | ||
|
||
if (response.HttpStatusCode is not HttpStatusCode.OK) | ||
throw new Exception("..."); | ||
|
||
Product product = Product.GetById.Unmarshall(response.Item); | ||
|
||
return product; | ||
} | ||
|
||
public async Task<Product?> UpdatePrice(string id, decimal price) | ||
{ | ||
UpdateItemRequest request = _updatePrice.Build((id, price, DateTime.UtcNow)); | ||
UpdateItemResponse response = await _amazonDynamoDb.UpdateItemAsync(request); | ||
|
||
if (response.HttpStatusCode is not HttpStatusCode.OK) | ||
return null; | ||
|
||
Product product = Product.UpdatePrice.Unmarshall(response.Attributes); | ||
|
||
return product; | ||
} | ||
} | ||
|
||
// These attributes is what makes the source generator kick in. Make sure to have the class 'partial' as well. | ||
[DynamoDBMarshaller(typeof(Product), PropertyName = "Put")] | ||
[DynamoDBMarshaller(typeof(Product), ArgumentType = typeof(string), PropertyName = "GetById")] | ||
[DynamoDBMarshaller(typeof(Product), ArgumentType = typeof((string Id, decimal NewPrice, DateTime TimeStamp)), PropertyName = "UpdatePrice")] | ||
[DynamoDBMarshaller(typeof(Product), ArgumentType = typeof(decimal), PropertyName = "QueryByPrice")] | ||
public partial record Product( | ||
[property: DynamoDBHashKey, DynamoDBGlobalSecondaryIndexRangeKey(Product.PriceIndex)] string Id, | ||
[property: DynamoDBGlobalSecondaryIndexHashKey(Product.PriceIndex)] decimal Price, | ||
string Description, | ||
Product.MetadataEntity Metadata | ||
) | ||
{ | ||
public const string PriceIndex = "PriceIndex"; | ||
|
||
public record MetadataEntity(DateTime CreatedAt, DateTime ModifiedAt); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
using System.Net; | ||
using Amazon.DynamoDBv2; | ||
using Amazon.DynamoDBv2.DataModel; | ||
using Amazon.DynamoDBv2.Model; | ||
using DynamoDBGenerator.Attributes; | ||
using Dynatello; | ||
using Dynatello.Builders; | ||
using Dynatello.Builders.Types; | ||
|
||
ProductRepository productRepository = new ProductRepository("MY_TABLE", new AmazonDynamoDBClient()); | ||
|
||
public class ProductRepository | ||
{ | ||
private readonly IAmazonDynamoDB _amazonDynamoDb; | ||
private readonly GetRequestBuilder<string> _getProductByTable; | ||
private readonly UpdateRequestBuilder<(string Id, decimal NewPrice, DateTime TimeStamp)> _updatePrice; | ||
private readonly PutRequestBuilder<Product> _createProduct; | ||
private readonly QueryRequestBuilder<decimal> _queryByPrice; | ||
|
||
public ProductRepository(string tableName, IAmazonDynamoDB amazonDynamoDb) | ||
{ | ||
_amazonDynamoDb = amazonDynamoDb; | ||
|
||
_getProductByTable = Product.GetById | ||
.OnTable(tableName) | ||
.ToGetRequestBuilder(arg => arg); // Since the ArgumentType is set to string, we don't need to select a property. | ||
|
||
_updatePrice = Product.UpdatePrice | ||
.OnTable(tableName) | ||
.WithUpdateExpression((db, arg) => $"SET {db.Price} = {arg.NewPrice}, {db.Metadata.ModifiedAt} = {arg.TimeStamp}") // Specify the update operation | ||
.ToUpdateItemRequestBuilder((marshaller, arg) => marshaller.PartitionKey(arg.Id)); | ||
|
||
_createProduct = Product.Put | ||
.OnTable(tableName) | ||
.WithConditionExpression((db, arg) => $"{db.Id} <> {arg.Id}") // Ensure we don't have an existing Product in DynamoDB | ||
.ToPutRequestBuilder(); | ||
|
||
_queryByPrice = Product.QueryByPrice | ||
.OnTable(tableName) | ||
.WithKeyConditionExpression((db, arg) => $"{db.Price} = {arg}") | ||
.ToQueryRequestBuilder() | ||
with | ||
{ | ||
IndexName = Product.PriceIndex | ||
}; | ||
} | ||
|
||
public async Task<IReadOnlyList<Product>> SearchByPrice(decimal price) | ||
{ | ||
QueryRequest request = _queryByPrice.Build(price); | ||
QueryResponse? response = await _amazonDynamoDb.QueryAsync(request); | ||
|
||
if (response.HttpStatusCode is not HttpStatusCode.OK) | ||
throw new Exception("..."); | ||
|
||
return response.Items | ||
.Select(x => Product.QueryByPrice.Unmarshall(x)) | ||
.ToArray(); | ||
} | ||
|
||
public async Task Create(Product product) | ||
{ | ||
PutItemRequest request = _createProduct.Build(product); | ||
PutItemResponse response = await _amazonDynamoDb.PutItemAsync(request); | ||
|
||
if (response.HttpStatusCode is not HttpStatusCode.OK) | ||
throw new Exception("..."); | ||
} | ||
|
||
public async Task<Product?> GetById(string id) | ||
{ | ||
GetItemRequest request = _getProductByTable.Build(id); | ||
GetItemResponse response = await _amazonDynamoDb.GetItemAsync(request); | ||
|
||
if (response.HttpStatusCode is HttpStatusCode.NotFound) | ||
return null; | ||
|
||
if (response.HttpStatusCode is not HttpStatusCode.OK) | ||
throw new Exception("..."); | ||
|
||
Product product = Product.GetById.Unmarshall(response.Item); | ||
|
||
return product; | ||
} | ||
|
||
public async Task<Product?> UpdatePrice(string id, decimal price) | ||
{ | ||
UpdateItemRequest request = _updatePrice.Build((id, price, DateTime.UtcNow)); | ||
UpdateItemResponse response = await _amazonDynamoDb.UpdateItemAsync(request); | ||
|
||
if (response.HttpStatusCode is not HttpStatusCode.OK) | ||
return null; | ||
|
||
Product product = Product.UpdatePrice.Unmarshall(response.Attributes); | ||
|
||
return product; | ||
} | ||
} | ||
|
||
// These attributes is what makes the source generator kick in. Make sure to have the class 'partial' as well. | ||
[DynamoDBMarshaller(typeof(Product), PropertyName = "Put")] | ||
[DynamoDBMarshaller(typeof(Product), ArgumentType = typeof(string), PropertyName = "GetById")] | ||
[DynamoDBMarshaller(typeof(Product), ArgumentType = typeof((string Id, decimal NewPrice, DateTime TimeStamp)), PropertyName = "UpdatePrice")] | ||
[DynamoDBMarshaller(typeof(Product), ArgumentType = typeof(decimal), PropertyName = "QueryByPrice")] | ||
public partial record Product( | ||
[property: DynamoDBHashKey, DynamoDBGlobalSecondaryIndexRangeKey(Product.PriceIndex)] string Id, | ||
[property: DynamoDBGlobalSecondaryIndexHashKey(Product.PriceIndex)] decimal Price, | ||
string Description, | ||
Product.MetadataEntity Metadata | ||
) | ||
{ | ||
public const string PriceIndex = "PriceIndex"; | ||
|
||
public record MetadataEntity(DateTime CreatedAt, DateTime ModifiedAt); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<IsPackable>false</IsPackable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Dynatello\Dynatello.csproj" /> | ||
</ItemGroup> | ||
|
||
|
||
</Project> |