@ -0,0 +1,2 @@ | |||
docker-compose.yml | |||
Dockerfile |
@ -0,0 +1,12 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Services.Basket.API | |||
{ | |||
public class BasketSettings | |||
{ | |||
public string ConnectionString { get; set; } | |||
} | |||
} |
@ -0,0 +1,44 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Mvc; | |||
using Microsoft.eShopOnContainers.Services.Basket.API.Model; | |||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers | |||
{ | |||
//NOTE: Right now this is a very chunky API, as the app evolves it is possible we would | |||
//want to make the actions more fine graned, add basket item as an action for example. | |||
//If this is the case we should also investigate changing the serialization format used for Redis, | |||
//using a HashSet instead of a simple string. | |||
[Route("/")] | |||
public class BasketController : Controller | |||
{ | |||
private IBasketRepository _repository; | |||
public BasketController(IBasketRepository repository) | |||
{ | |||
_repository = repository; | |||
} | |||
// GET api/values/5 | |||
[HttpGet("{id}")] | |||
public async Task<CustomerBasket> Get(Guid id) | |||
{ | |||
return await _repository.GetBasket(id); | |||
} | |||
// POST api/values | |||
[HttpPost] | |||
public void Post([FromBody]CustomerBasket value) | |||
{ | |||
_repository.UpdateBasket(value); | |||
} | |||
// DELETE api/values/5 | |||
[HttpDelete("{id}")] | |||
public void Delete(Guid id) | |||
{ | |||
_repository.DeleteBasket(id); | |||
} | |||
} | |||
} |
@ -1,44 +0,0 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using Microsoft.AspNetCore.Mvc; | |||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Controllers | |||
{ | |||
[Route("api/[controller]")] | |||
public class ValuesController : Controller | |||
{ | |||
// GET api/values | |||
[HttpGet] | |||
public IEnumerable<string> Get() | |||
{ | |||
return new string[] { "value1", "value2" }; | |||
} | |||
// GET api/values/5 | |||
[HttpGet("{id}")] | |||
public string Get(int id) | |||
{ | |||
return "value"; | |||
} | |||
// POST api/values | |||
[HttpPost] | |||
public void Post([FromBody]string value) | |||
{ | |||
} | |||
// PUT api/values/5 | |||
[HttpPut("{id}")] | |||
public void Put(int id, [FromBody]string value) | |||
{ | |||
} | |||
// DELETE api/values/5 | |||
[HttpDelete("{id}")] | |||
public void Delete(int id) | |||
{ | |||
} | |||
} | |||
} |
@ -0,0 +1,6 @@ | |||
FROM microsoft/aspnetcore:1.0.1 | |||
ENTRYPOINT ["dotnet", "Basket.API.dll"] | |||
ARG source=. | |||
WORKDIR /app | |||
EXPOSE 80 | |||
COPY $source . |
@ -0,0 +1,18 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Model | |||
{ | |||
public class CustomerBasket | |||
{ | |||
public Guid CustomerId { get; private set; } | |||
public IList<BasketItem> BasketItems => new List<BasketItem>(); | |||
public CustomerBasket(Guid customerId) | |||
{ | |||
CustomerId = customerId; | |||
} | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Model | |||
{ | |||
public class BasketItem | |||
{ | |||
public Guid Id { get; set; } | |||
public decimal Price { get; set; } | |||
public int Count { get; set; } | |||
} | |||
} |
@ -0,0 +1,14 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Model | |||
{ | |||
public interface IBasketRepository | |||
{ | |||
Task<CustomerBasket> GetBasket(Guid customerId); | |||
Task<bool> UpdateBasket(CustomerBasket basket); | |||
Task<bool> DeleteBasket(Guid id); | |||
} | |||
} |
@ -0,0 +1,66 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading.Tasks; | |||
using StackExchange.Redis; | |||
using Microsoft.Extensions.Options; | |||
using Newtonsoft.Json; | |||
using Microsoft.Extensions.Logging; | |||
using System.Net; | |||
namespace Microsoft.eShopOnContainers.Services.Basket.API.Model | |||
{ | |||
public class RedisBasketRepository : IBasketRepository | |||
{ | |||
private ILogger<RedisBasketRepository> _logger; | |||
private BasketSettings _settings; | |||
private ConnectionMultiplexer _redis; | |||
public RedisBasketRepository(IOptions<BasketSettings> options, ILoggerFactory loggerFactory) | |||
{ | |||
_settings = options.Value; | |||
_logger = loggerFactory.CreateLogger<RedisBasketRepository>(); | |||
} | |||
public async Task<bool> DeleteBasket(Guid id) | |||
{ | |||
var database = await GetDatabase(); | |||
return await database.KeyDeleteAsync(id.ToString()); | |||
} | |||
public async Task<CustomerBasket> GetBasket(Guid customerId) | |||
{ | |||
var database = await GetDatabase(); | |||
var data = await database.StringGetAsync(customerId.ToString()); | |||
if (data.IsNullOrEmpty) | |||
{ | |||
return null; | |||
} | |||
return JsonConvert.DeserializeObject<CustomerBasket>(data); | |||
} | |||
public async Task<bool> UpdateBasket(CustomerBasket basket) | |||
{ | |||
var database = await GetDatabase(); | |||
return await database.StringSetAsync(basket.CustomerId.ToString(), JsonConvert.SerializeObject(basket)); | |||
} | |||
private async Task<IDatabase> GetDatabase() | |||
{ | |||
if (_redis == null) | |||
{ | |||
//TODO: Need to make this more robust. Also want to understand why the static connection method cannot accept dns names. | |||
var ips = await Dns.GetHostAddressesAsync(_settings.ConnectionString); | |||
_logger.LogInformation($"Connecting to database {_settings.ConnectionString} at IP {ips.First().ToString()}"); | |||
_redis = await ConnectionMultiplexer.ConnectAsync(ips.First().ToString()); | |||
} | |||
return _redis.GetDatabase(); | |||
} | |||
} | |||
} |
@ -0,0 +1,17 @@ | |||
version: '2' | |||
services: | |||
basket.api: | |||
build: | |||
args: | |||
source: obj/Docker/empty/ | |||
labels: | |||
- "com.microsoft.visualstudio.targetoperatingsystem=linux" | |||
environment: | |||
- ASPNETCORE_ENVIRONMENT=Development | |||
- DOTNET_USE_POLLING_FILE_WATCHER=1 | |||
volumes: | |||
- .:/app | |||
- ~/.nuget/packages:/root/.nuget/packages:ro | |||
- ~/clrdbg:/clrdbg:ro | |||
entrypoint: tail -f /dev/null |
@ -0,0 +1,9 @@ | |||
version: '2' | |||
services: | |||
basket.api: | |||
labels: | |||
- "com.microsoft.visualstudio.targetoperatingsystem=linux" | |||
volumes: | |||
- ~/clrdbg:/clrdbg:ro | |||
entrypoint: tail -f /dev/null |
@ -0,0 +1,16 @@ | |||
version: '2' | |||
services: | |||
basket.api: | |||
image: user/basket.api${TAG} | |||
environment: | |||
- ConnectionString=basket.data | |||
build: | |||
context: . | |||
dockerfile: Dockerfile | |||
ports: | |||
- "32783:80" | |||
depends_on: | |||
- basket.data | |||
basket.data: | |||
image: redis |