diff --git a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs index f703c2944..ccb218a0d 100644 --- a/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs +++ b/src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs @@ -1,72 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.eShopOnContainers.Services.Catalog.API.Model; - + namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers { - [Route("/")] + using Microsoft.AspNetCore.Mvc; + using Microsoft.EntityFrameworkCore; + using Microsoft.eShopOnContainers.Services.Catalog.API.Model; + using System; + using System.Linq; + using System.Threading.Tasks; + + [Route("api/v1/[controller]")] public class CatalogController : ControllerBase { - private CatalogContext _context; + private readonly CatalogContext _context; public CatalogController(CatalogContext context) { _context = context; } - // GET api/values - [HttpGet] - public IEnumerable Get() - { - return _context.CatalogItems.ToList(); - } + // GET api/v1/[controller]/all - // GET api/values/5 - [HttpGet("{id}")] - public IActionResult Get(Guid id) + [HttpGet] + [Route("[action]")] + public async Task All() { - var item = _context.CatalogItems.FirstOrDefault(x=> x.Id == id); - - if(item == null) - { - return NotFound(); - } + var items = await _context.CatalogItems + .ToListAsync(); - return new OkObjectResult(item); + return Ok(items); } - // POST api/values - [HttpPost] - public IActionResult Post([FromBody]CatalogItem item) - { - try - { - _context.CatalogItems.Add(item); - _context.SaveChanges(); - return Ok(); - } - catch - { - return StatusCode(500, "Unable to add new catalog item"); - } - } + // GET api/v1/[controller]/FindByName/samplename - // PUT api/values/5 - [HttpPut("{id}")] - public IActionResult Put(int id, [FromBody]CatalogItem item) + [HttpGet] + [Route("FindByName/{name:minlength(1)}")] + public async Task Find(string name) { - _context.CatalogItems.Update(item); - _context.SaveChanges(); - return Ok(); - } + var items = await _context.CatalogItems + .Where(c => c.Name.StartsWith(name, StringComparison.CurrentCultureIgnoreCase)) + .ToListAsync(); - // DELETE api/values/5 - [HttpDelete("{id}")] - public IActionResult Delete(Guid id) - { return Ok(); } } diff --git a/src/Services/Catalog/Catalog.API/Dockerfile b/src/Services/Catalog/Catalog.API/Dockerfile index 99490a7c3..3e3ef42e8 100644 --- a/src/Services/Catalog/Catalog.API/Dockerfile +++ b/src/Services/Catalog/Catalog.API/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore +FROM microsoft/aspnetcore:latest WORKDIR /app EXPOSE 80 ADD . /app diff --git a/src/Services/Catalog/Catalog.API/Model/CatalogContext.cs b/src/Services/Catalog/Catalog.API/Model/CatalogContext.cs index c8fa53cf6..dc78bce84 100644 --- a/src/Services/Catalog/Catalog.API/Model/CatalogContext.cs +++ b/src/Services/Catalog/Catalog.API/Model/CatalogContext.cs @@ -1,15 +1,12 @@ -using Microsoft.EntityFrameworkCore; -using Npgsql.EntityFrameworkCore.PostgreSQL; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model { + using EntityFrameworkCore.Metadata.Builders; + using Microsoft.EntityFrameworkCore; + using Npgsql.EntityFrameworkCore.PostgreSQL; + public class CatalogContext : DbContext { - public CatalogContext(DbContextOptions options): base(options) + public CatalogContext(DbContextOptions options) : base(options) { } @@ -17,7 +14,33 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model protected override void OnModelCreating(ModelBuilder builder) { + builder.HasSequence("idseq") + .StartsAt(1) + .IncrementsBy(1); + + builder.Entity(ConfigureCatalogItem); + builder.HasPostgresExtension("uuid-ossp"); } + + void ConfigureCatalogItem(EntityTypeBuilder builder) + { + builder.ForNpgsqlToTable("catalog"); + + builder.Property(ci => ci.Id) + .HasDefaultValueSql("nextval('idseq')") + .IsRequired(); + + builder.Property(ci => ci.Name) + .IsRequired(true) + .HasMaxLength(50); + + builder.Property(ci => ci.Price) + .IsRequired(true); + + builder.Property(ci => ci.PictureUri) + .IsRequired(false); + + } } } diff --git a/src/Services/Catalog/Catalog.API/Model/CatalogContextSeed.cs b/src/Services/Catalog/Catalog.API/Model/CatalogContextSeed.cs new file mode 100644 index 000000000..a941dc165 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/Model/CatalogContextSeed.cs @@ -0,0 +1,67 @@ +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model +{ + using Microsoft.AspNetCore.Builder; + using Model; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + public class CatalogContextSeed + { + public static async Task SeedAsync(IApplicationBuilder applicationBuilder) + { + var context = (CatalogContext)applicationBuilder + .ApplicationServices.GetService(typeof(CatalogContext)); + + using (context) + { + context.Database.EnsureCreated(); + + if (!context.CatalogItems.Any()) + { + context.CatalogItems.AddRange( + GetPreconfiguredItems()); + + await context.SaveChangesAsync(); + } + } + } + + static IEnumerable GetPreconfiguredItems() + { + return new List() + { + new CatalogItem() { Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" }, + new CatalogItem() { Description = "Cupt Black & White Mug", Name = "Cupt Black & White Mug", Price= 17, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=CuptBlack&WhiteMug" }, + new CatalogItem() { Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.PrismWhiteT-Shirt" }, + new CatalogItem() { Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.NETBotBlack" }, + new CatalogItem() { Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" }, + new CatalogItem() { Description = "Cupt Black & White Mug", Name = "Cupt Black & White Mug", Price= 17, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=CuptBlack&WhiteMug" }, + new CatalogItem() { Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.PrismWhiteT-Shirt" }, + new CatalogItem() { Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.NETBotBlack" }, + new CatalogItem() { Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" }, + new CatalogItem() { Description = "Cupt Black & White Mug", Name = "Cupt Black & White Mug", Price= 17, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=CuptBlack&WhiteMug" }, + new CatalogItem() { Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.PrismWhiteT-Shirt" }, + new CatalogItem() { Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.NETBotBlack" }, + new CatalogItem() { Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" }, + new CatalogItem() { Description = "Cupt Black & White Mug", Name = "Cupt Black & White Mug", Price= 17, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=CuptBlack&WhiteMug" }, + new CatalogItem() { Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.PrismWhiteT-Shirt" }, + new CatalogItem() { Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.NETBotBlack" }, + new CatalogItem() { Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" }, + new CatalogItem() { Description = "Cupt Black & White Mug", Name = "Cupt Black & White Mug", Price= 17, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=CuptBlack&WhiteMug" }, + new CatalogItem() { Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.PrismWhiteT-Shirt" }, + new CatalogItem() { Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.NETBotBlack" }, + new CatalogItem() { Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" }, + new CatalogItem() { Description = "Cupt Black & White Mug", Name = "Cupt Black & White Mug", Price= 17, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=CuptBlack&WhiteMug" }, + new CatalogItem() { Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.PrismWhiteT-Shirt" }, + new CatalogItem() { Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.NETBotBlack" }, + new CatalogItem() { Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" }, + new CatalogItem() { Description = "Cupt Black & White Mug", Name = "Cupt Black & White Mug", Price= 17, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=CuptBlack&WhiteMug" }, + new CatalogItem() { Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.PrismWhiteT-Shirt" }, + new CatalogItem() { Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.NETBotBlack" } + + }; + } + } +} diff --git a/src/Services/Catalog/Catalog.API/Model/CatalogItem.cs b/src/Services/Catalog/Catalog.API/Model/CatalogItem.cs index abc3f91af..b79f7bd1a 100644 --- a/src/Services/Catalog/Catalog.API/Model/CatalogItem.cs +++ b/src/Services/Catalog/Catalog.API/Model/CatalogItem.cs @@ -1,20 +1,19 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model +namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model { + using System; + public class CatalogItem { - public CatalogItem() - { - } + public int Id { get; set; } - public Guid Id { get; set; } public string Name { get; set; } + public string Description { get; set; } + public decimal Price { get; set; } - public int ImageCount { get; set; } + + public string PictureUri { get; set; } + + public CatalogItem() { } } } diff --git a/src/Services/Catalog/Catalog.API/Program.cs b/src/Services/Catalog/Catalog.API/Program.cs index 82ca9383e..1eaf35596 100644 --- a/src/Services/Catalog/Catalog.API/Program.cs +++ b/src/Services/Catalog/Catalog.API/Program.cs @@ -14,6 +14,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API { var host = new WebHostBuilder() .UseKestrel() + .UseUrls(Environment.GetEnvironmentVariable("ASPNETCORE_URLS") ?? String.Empty) .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() .Build(); diff --git a/src/Services/Catalog/Catalog.API/Startup.cs b/src/Services/Catalog/Catalog.API/Startup.cs index 80485de5d..08a0baafb 100644 --- a/src/Services/Catalog/Catalog.API/Startup.cs +++ b/src/Services/Catalog/Catalog.API/Startup.cs @@ -1,48 +1,81 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.eShopOnContainers.Services.Catalog.API.Model; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Microsoft.eShopOnContainers.Services.Catalog.API.Model; -using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json.Serialization; +using System.Threading.Tasks; namespace Microsoft.eShopOnContainers.Services.Catalog.API { public class Startup { + public IConfigurationRoot Configuration { get; } + public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) - .AddJsonFile("settings.json") + .AddJsonFile($"settings.{env.EnvironmentName}.json",optional:false) .AddEnvironmentVariables(); + + Configuration = builder.Build(); } - public IConfigurationRoot Configuration { get; } + - // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddDbContext(c => { + services.AddSingleton(Configuration); + + services.AddDbContext(c => + { c.UseNpgsql(Configuration["ConnectionString"]); + c.ConfigureWarnings(wb => + { + wb.Throw(RelationalEventId.QueryClientEvaluationWarning); + }); }); // Add framework services. + + services.AddCors(); + services.AddMvcCore() - .AddJsonFormatters(); + .AddJsonFormatters(settings=> + { + settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { + + //Configure logs + + if(env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); + //Seed Data + + CatalogContextSeed.SeedAsync(app) + .Wait(); + + // Use frameworks + app.UseCors(policyBuilder=>policyBuilder.AllowAnyOrigin()); + app.UseMvc(); } } diff --git a/src/Services/Catalog/Catalog.API/project.json b/src/Services/Catalog/Catalog.API/project.json index 563e4d136..020aad4a0 100644 --- a/src/Services/Catalog/Catalog.API/project.json +++ b/src/Services/Catalog/Catalog.API/project.json @@ -1,21 +1,25 @@ { "dependencies": { "Microsoft.NETCore.App": { - "version": "1.0.0", + "version": "1.0.1", "type": "platform" }, - "Microsoft.AspNetCore.Mvc": "1.0.0", - "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", - "Microsoft.EntityFrameworkCore": "1.0.0", + "Microsoft.AspNetCore.Mvc": "1.0.1", + "Microsoft.AspNetCore.Diagnostics": "1.0.0", + "Microsoft.AspNetCore.Diagnostics.Abstractions": "1.0.0", + "Microsoft.AspNetCore.Server.Kestrel": "1.0.1", + "Microsoft.EntityFrameworkCore": "1.0.1", "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0", "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", - "Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.1" + "Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.2" + }, + "tools": { + }, - "tools": {}, "frameworks": { "netcoreapp1.0": { "imports": [ @@ -39,7 +43,7 @@ "wwwroot", "Views", "Areas/**/Views", - "settings.json", + "settings.Production.json", "web.config", "project.json", "Dockerfile" diff --git a/src/Services/Catalog/Catalog.API/settings.json b/src/Services/Catalog/Catalog.API/settings.development.json similarity index 61% rename from src/Services/Catalog/Catalog.API/settings.json rename to src/Services/Catalog/Catalog.API/settings.development.json index e4f7496f5..3674eb6bc 100644 --- a/src/Services/Catalog/Catalog.API/settings.json +++ b/src/Services/Catalog/Catalog.API/settings.development.json @@ -1,5 +1,5 @@ { - "ConnectionString": "Server=127.0.0.1;Port=5432;Database=postgres;username=postgres", + "ConnectionString": "Server=127.0.0.1;Port=5432;Database=Catalog;username=postgres;password=postgres", "Logging": { "IncludeScopes": false, "LogLevel": { diff --git a/src/Services/Catalog/Catalog.API/settings.production.json b/src/Services/Catalog/Catalog.API/settings.production.json new file mode 100644 index 000000000..a562552b2 --- /dev/null +++ b/src/Services/Catalog/Catalog.API/settings.production.json @@ -0,0 +1,11 @@ +{ + "ConnectionString": "Server=127.0.0.1;Port=5432;Database=CatalogDB;username=postgres;password=postgres", + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +}