Skip to content

Commit

Permalink
Added to prevent reposting cart and return conflict result
Browse files Browse the repository at this point in the history
  • Loading branch information
garywoodfine committed Nov 10, 2021
1 parent 45df905 commit 28dcb4c
Show file tree
Hide file tree
Showing 29 changed files with 86 additions and 65 deletions.
4 changes: 2 additions & 2 deletions State Management/daprstate/src/Post.http
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
## Post Cart Item
POST http://{{host-url}}/cart
accept: application/json
x-session-id: {{create-session-id}}
x-session-id: {{session-id}}
Content-Type: application/json-patch+json

[
{
"sku": "abc-100",
"sku": "abc-104",
"quantity": 2,
"amount": 9.99
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ShoppingCart.Activities.Sample.Get;
using Swashbuckle.AspNetCore.Annotations;

namespace ShoppingCart.Activities.Cart
namespace ShoppingCart.Activities.Cart.Get
{
[Route(Routes.Cart)]
public class Get : BaseAsyncEndpoint.WithRequest<Query>.WithResponse<Response>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
using MediatR;
using ShoppingCart.Content.Activities.Cart.Post;
using ShoppingCart.Content.Activities.Cart.Post.Models;
using ShoppingCart.Content.Exceptions;

namespace ShoppingCart.Activities.Sample.Get
namespace ShoppingCart.Activities.Cart.Get
{
public class Handler : IRequestHandler<Query, Response>
{
Expand All @@ -19,7 +20,8 @@ public Handler(IService<Item> cartStateService)
public async Task<Response> Handle(Query request, CancellationToken cancellationToken)
{

var items = await _cartStateService.Get(request.Session);
var items = await _cartStateService.Get(request.Session, cancellationToken);

/// Your Logic Goes here
// This is only to supply an example and you should do whatever you need to achieve here
return await Task.FromResult(new Response
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;

namespace ShoppingCart.Activities.Sample.Get
namespace ShoppingCart.Activities.Cart.Get
{
public class Query : IRequest<Response>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Collections.Generic;
using ShoppingCart.Content.Activities.Cart.Post.Models;

namespace ShoppingCart.Activities.Sample.Get
namespace ShoppingCart.Activities.Cart
{
public class Response
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
namespace ShoppingCart.Activities.Sample.Get
using System.Data;
using FluentValidation;

namespace ShoppingCart.Activities.Cart.Get
{
public class Validator
public class Validator : AbstractValidator<Query>
{

public Validator()
{
RuleFor(b => b.Session).NotNull().NotEmpty();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public Handler(IService<Item> cartStateService)

public async Task<Response> Handle(Command request, CancellationToken cancellationToken)
{
var items = await _cartStateService.Update(request.Session, request.Items);
var items = await _cartStateService.Update(request.Session, request.Items, cancellationToken);
return await Task.FromResult(new Response
{
Items = items
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ namespace ShoppingCart.Content.Activities.Cart.Post
{
public class Command : IRequest<Response>
{
[Required][FromHeader(Name = "x-session-id")] public string Session { get; set; }
[Required]
[FromHeader(Name = "x-session-id")]
public string Session { get; set; }

[FromBody] public List<Item> Items { get; set; }

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ public Handler(IService<Item> cartService)
}
public async Task<Response> Handle(Command request, CancellationToken cancellationToken)
{
await _cartService.Save(request.Session, request.Items);
return new Response { Items = request.Items };

return new Response
{
Exists = await _cartService.Save(request.Session, request.Items, cancellationToken),
Items = request.Items
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,28 @@ public Post(IMediator mediator)
{
_mediator = mediator;
}

[HttpPost]
[SwaggerOperation(
Summary = "Create a shopping cart for a user",
Description = "Create a shopping cart for a user",
OperationId = "4D730510-3399-4324-BB2D-0A6C7270F783",
Tags = new[] {Routes.Cart})
Tags = new[] { Routes.Cart })
]
[ProducesResponseType(StatusCodes.Status201Created)]
public async override Task<ActionResult> HandleAsync([FromRoute] Command request, CancellationToken cancellationToken = new CancellationToken())
[ProducesErrorResponseType(typeof(ConflictResult))]
public override async Task<ActionResult> HandleAsync([FromRoute] Command request,
CancellationToken cancellationToken = new CancellationToken())
{
var response= await _mediator.Send(request, cancellationToken);
return new CreatedResult(
new Uri($"/{Routes.Cart}/{request.Session}", UriKind.Relative), response);
var response = await _mediator.Send(request, cancellationToken);

return response.Exists
? new ConflictResult()
: new CreatedResult(
new Uri($"/{Routes.Cart}/{request.Session}", UriKind.Relative), response.Items);
}
}
}


//dapr run --app-id "article-service" --app-port "5001" --dapr-grpc-port "50010" --dapr-http-port "5010" --components-path "./components" -- dotnet run --project ./ShoppingCart.csproj --urls="http://+:5001"
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ namespace ShoppingCart.Content.Activities.Cart.Post
public class Response
{
public List<Item> Items { get; set; }
public bool Exists { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;

namespace ShoppingCart.Content.Exceptions
{
public class DaprStateException : Exception
{
public DaprStateException(string title, string message) : base(message) => Title = title;
public DaprStateException(string title, string message, IReadOnlyDictionary<string, string[]> errors) : base(message) => Title = title;
public string Title { get; set; }
public IReadOnlyDictionary<string, string[]> Errors { get; }
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using ShoppingCart.Resources;

namespace ShoppingCart.Content.Exceptions
{
[Serializable]
public class NotFoundException : DaprStateException
{
public NotFoundException(string title, string message) : base(ExceptionTitle.NotFound, message)
public NotFoundException( string message, IReadOnlyDictionary<string, string[]> errors) : base(ExceptionTitle.NotFound, message, errors)
{
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ namespace ShoppingCart.Content.Exceptions
{
public class ValidationException : DaprStateException
{
public ValidationException( string message, IReadOnlyDictionary<string, string[]> errors) : base(ExceptionTitle.Validation , message) =>
Errors = errors;

public IReadOnlyDictionary<string, string[]> Errors { get; }
public ValidationException(string message, IReadOnlyDictionary<string, string[]> errors) : base(
ExceptionTitle.Validation, message, errors)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ private static IReadOnlyDictionary<string, string[]> GetErrors(Exception excepti
{
IReadOnlyDictionary<string, string[]> errors = null;

if (exception is ValidationException validationException)
if (exception is DaprStateException baseException)
{
errors = validationException.Errors;
return baseException.Errors;
}



return errors;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,46 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Dapr.Client;
using Microsoft.AspNetCore.JsonPatch;
using ShoppingCart.Content.Activities.Cart.Post.Models;
using ShoppingCart.Content.Exceptions;

namespace ShoppingCart.Content.Activities.Cart.Post
{
public class CartStateService : IService<Item>
{
private const string DAPR_STORE_NAME = "cart";
private const string DAPR_STORE_NAME = "cart-service";
private readonly DaprClient _client;

public CartStateService(DaprClient client)
{
_client = client;
}

public async Task<List<Item>> Update(string session, JsonPatchDocument<List<Item>> items)
public async Task<List<Item>> Update(string session, JsonPatchDocument<List<Item>> items, CancellationToken cancellationToken)
{
var currentItems = await Get(session);
var (currentItems, etag) = await _client.GetStateAndETagAsync<List<Item>>(DAPR_STORE_NAME, session, cancellationToken: cancellationToken);
items.ApplyTo(currentItems);
await Save(session, currentItems);
await _client.TrySaveStateAsync(DAPR_STORE_NAME, session, currentItems, etag, cancellationToken: cancellationToken);
return currentItems;
}

public async Task Save(string session, List<Item> entity)
public async Task<bool> Save(string session, List<Item> entity, CancellationToken cancellationToken)
{
await _client.SaveStateAsync(DAPR_STORE_NAME, session , entity);
var stateEntry = await _client.GetStateEntryAsync<List<Item>>(DAPR_STORE_NAME, session, default, cancellationToken: cancellationToken);
if (stateEntry.Value is not null) return true;
await _client.SaveStateAsync(DAPR_STORE_NAME, session, entity, cancellationToken: cancellationToken);
return false;

}

public async Task<List<Item>>Get(string session)
public async Task<List<Item>>Get(string session, CancellationToken cancellationToken)
{
return await _client.GetStateAsync<List<Item>>(DAPR_STORE_NAME, session, ConsistencyMode.Strong);
return await _client.GetStateAsync<List<Item>>(DAPR_STORE_NAME, session, ConsistencyMode.Strong, cancellationToken: cancellationToken);

}


}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.JsonPatch;

namespace ShoppingCart.Content.Activities.Cart.Post
{
public interface IService<T> where T : class
{
Task<List<T>> Update(string session, JsonPatchDocument<List<T>> items);
Task Save(string session, List<T> entity);
Task<List<T>> Get(string session);
Task<List<T>> Update(string session, JsonPatchDocument<List<T>> items, CancellationToken cancellationToken);
Task<bool> Save(string session, List<T> entity, CancellationToken cancellationToken);
Task<List<T>> Get(string session, CancellationToken cancellationToken);
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: cart
name: cart-service
spec:
type: state.mongodb
version: v1
metadata:
- name: keyPrefix
value: appid
value: name
- name: host
value: localhost:27017
- name: username
value: dapr
- name: password
value: daprPassword
- name: databaseName
value: admin
value: admin
- name: collectionName
value: cart
scopes:
- cart-service
scopes:
- cart-service
Original file line number Diff line number Diff line change
@@ -1 +1 @@
52bde6a5a5838dc6c49cc744b5a919bb856714d2
d8886b9e2c6471a0dae77374cf1e75b015a8405d
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
16360564658755498
16365798909296117
5 changes: 2 additions & 3 deletions State Management/daprstate/src/http-client.env.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"dev": {
"host-url": "localhost:5001/",
"session-id": "9F520260-2CB4-462A-BA04-C2F7CFAB1EFE",
"create-session-id": "93416A7B-A2B6-4CC8-AE94-9CD6BE89AE32"
"host-url": "localhost:5001",
"session-id": "D80D2408-4946-4A66-879C-CED411CBD0CB"
}
}

0 comments on commit 28dcb4c

Please sign in to comment.