Carlos Cañizares Estévez 8 years ago
parent
commit
068e5daed3
19 changed files with 286 additions and 61 deletions
  1. +13
    -2
      build-images.ps1
  2. +3
    -1
      build-images.sh
  3. +12
    -1
      docker-compose.yml
  4. +2
    -0
      src/Services/Basket/Basket.API/.dockerignore
  5. +12
    -0
      src/Services/Basket/Basket.API/BasketSettings.cs
  6. +44
    -0
      src/Services/Basket/Basket.API/Controllers/BasketController.cs
  7. +0
    -44
      src/Services/Basket/Basket.API/Controllers/ValuesController.cs
  8. +6
    -0
      src/Services/Basket/Basket.API/Dockerfile
  9. +18
    -0
      src/Services/Basket/Basket.API/Model/Basket.cs
  10. +14
    -0
      src/Services/Basket/Basket.API/Model/BasketItem.cs
  11. +14
    -0
      src/Services/Basket/Basket.API/Model/IBasketRepository.cs
  12. +66
    -0
      src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs
  13. +4
    -0
      src/Services/Basket/Basket.API/Properties/launchSettings.json
  14. +19
    -0
      src/Services/Basket/Basket.API/Startup.cs
  15. +2
    -1
      src/Services/Basket/Basket.API/appsettings.json
  16. +17
    -0
      src/Services/Basket/Basket.API/docker-compose.dev.debug.yml
  17. +9
    -0
      src/Services/Basket/Basket.API/docker-compose.dev.release.yml
  18. +16
    -0
      src/Services/Basket/Basket.API/docker-compose.yml
  19. +15
    -12
      src/Services/Basket/Basket.API/project.json

+ 13
- 2
build-images.ps1 View File

@ -39,8 +39,19 @@ Write-Host "Restore Dependencies just in case as it is needed to run dotnet publ
dotnet restore $orderingPathToJson
dotnet build $orderingPathToJson
dotnet publish $orderingPathToJson -o $orderingPathToPub
#*** Basket service image ***
$basketPathToJson = $scriptPath + "\src\Services\Basket\Basket.API\project.json"
Write-Host "basketPathToJson is $orderingPathToJson" -ForegroundColor Yellow
$basketPathToPub = $scriptPath + "\pub\basket"
Write-Host "basketPathToPub is $basketPathToPub" -ForegroundColor Yellow
Write-Host "Restore Dependencies just in case as it is needed to run dotnet publish" -ForegroundColor Blue
dotnet restore $basketPathToJson
dotnet build $basketPathToPub
dotnet publish $basketPathToJson -o $basketPathToPub
docker build -t eshop/web $webPathToPub
docker build -t eshop/catalog.api $catalogPathToPub
docker build -t eshop/ordering.api $orderingPathToPub
docker build -t eshop/ordering.api $orderingPathToPub
docker build -t eshop/basket.api $basketPathToPub

+ 3
- 1
build-images.sh View File

@ -4,7 +4,9 @@ rm -rf ./pub
dotnet publish "$(pwd)/src/Web/WebMVC/project.json" -o "$(pwd)/pub/webMVC"
dotnet publish "$(pwd)/src/Services/Catalog/Catalog.API/project.json" -o "$(pwd)/pub/catalog"
dotnet publish "$(pwd)/src/Services/Ordering/Ordering.API/project.json" -o "$(pwd)/pub/ordering"
dotnet publish "$(pwd)/src/Services/Basket/Basket.API/project.json" -o "$(pwd)/pub/basket"
docker build -t eshop/web "$(pwd)/pub/webMVC"
docker build -t eshop/catalog.api "$(pwd)/pub/catalog"
docker build -t eshop/ordering.api "$(pwd)/pub/ordering"
docker build -t eshop/ordering.api "$(pwd)/pub/ordering"
docker build -t eshop/basket.api "$(pwd)/pub/basket"

+ 12
- 1
docker-compose.yml View File

@ -10,6 +10,7 @@ services:
- "800:80"
depends_on:
- catalog.api
- ordering.api
catalog.api:
image: eshop/catalog.api:latest
@ -41,4 +42,14 @@ services:
ordering.data:
image: eshop/ordering.data.sqlserver.linux
ports:
- "1433:1433"
- "1433:1433"
basket.api:
image: eshop/basket.api:latest
environment:
- ConnectionString=basket.data
depends_on:
- basket.data
basket.data:
image: redis

+ 2
- 0
src/Services/Basket/Basket.API/.dockerignore View File

@ -0,0 +1,2 @@
docker-compose.yml
Dockerfile

+ 12
- 0
src/Services/Basket/Basket.API/BasketSettings.cs View File

@ -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; }
}
}

+ 44
- 0
src/Services/Basket/Basket.API/Controllers/BasketController.cs View File

@ -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);
}
}
}

+ 0
- 44
src/Services/Basket/Basket.API/Controllers/ValuesController.cs View File

@ -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)
{
}
}
}

+ 6
- 0
src/Services/Basket/Basket.API/Dockerfile View File

@ -0,0 +1,6 @@
FROM microsoft/aspnetcore:1.0.1
ENTRYPOINT ["dotnet", "Basket.API.dll"]
ARG source=.
WORKDIR /app
EXPOSE 80
COPY $source .

+ 18
- 0
src/Services/Basket/Basket.API/Model/Basket.cs View File

@ -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;
}
}
}

+ 14
- 0
src/Services/Basket/Basket.API/Model/BasketItem.cs View File

@ -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; }
}
}

+ 14
- 0
src/Services/Basket/Basket.API/Model/IBasketRepository.cs View File

@ -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);
}
}

+ 66
- 0
src/Services/Basket/Basket.API/Model/RedisBasketRepository.cs View File

@ -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();
}
}
}

+ 4
- 0
src/Services/Basket/Basket.API/Properties/launchSettings.json View File

@ -23,6 +23,10 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"launchBrowser": true,
"launchUrl": "http://localhost:{ServicePort}/api/values"
}
}
}

+ 19
- 0
src/Services/Basket/Basket.API/Startup.cs View File

@ -7,6 +7,10 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using StackExchange.Redis;
using Microsoft.Extensions.Options;
using System.Net;
namespace Microsoft.eShopOnContainers.Services.Basket.API
{
@ -29,6 +33,21 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
{
// Add framework services.
services.AddMvc();
services.Configure<BasketSettings>(Configuration);
//By connection here we are making sure that our service
//cannot start until redis is ready. This might slow down startup,
//but given that it is there is a delay on resolving the ip address
//and then creating the connection it seems reasonable to move
//that cost to startup instead of having the first request pay the
//penalty.
services.AddSingleton<ConnectionMultiplexer>((sp) => {
var config = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
var ips = Dns.GetHostAddressesAsync(config.ConnectionString).Result;
return ConnectionMultiplexer.Connect(ips.First().ToString());
});
services.AddTransient<IBasketRepository, RedisBasketRepository>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.


+ 2
- 1
src/Services/Basket/Basket.API/appsettings.json View File

@ -6,5 +6,6 @@
"System": "Information",
"Microsoft": "Information"
}
}
},
"ConnectionString": "127.0.0.1"
}

+ 17
- 0
src/Services/Basket/Basket.API/docker-compose.dev.debug.yml View File

@ -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

+ 9
- 0
src/Services/Basket/Basket.API/docker-compose.dev.release.yml View File

@ -0,0 +1,9 @@
version: '2'
services:
basket.api:
labels:
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
volumes:
- ~/clrdbg:/clrdbg:ro
entrypoint: tail -f /dev/null

+ 16
- 0
src/Services/Basket/Basket.API/docker-compose.yml View File

@ -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

+ 15
- 12
src/Services/Basket/Basket.API/project.json View File

@ -1,9 +1,10 @@
{
{
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
},
"System.Threading": "4.0.11",
"Microsoft.AspNetCore.Mvc": "1.0.0",
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
@ -13,13 +14,13 @@
"Microsoft.Extensions.Logging": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Extensions.Logging.Debug": "1.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0"
"Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0",
"StackExchange.Redis": "1.1.608",
"Newtonsoft.Json": "9.0.1"
},
"tools": {
"Microsoft.AspNetCore.Server.IISIntegration.Tools": "1.0.0-preview2-final"
},
"frameworks": {
"netcoreapp1.0": {
"imports": [
@ -28,29 +29,31 @@
]
}
},
"buildOptions": {
"emitEntryPoint": true,
"preserveCompilationContext": true
"preserveCompilationContext": true,
"debugType": "portable"
},
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true
}
},
"publishOptions": {
"include": [
"wwwroot",
"Views",
"Areas/**/Views",
"appsettings.json",
"web.config"
"web.config",
"Dockerfile",
"docker-compose.yml",
".dockerignore"
]
},
"scripts": {
"postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
"postpublish": [
"dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%"
]
}
}
}

Loading…
Cancel
Save