Merge branch 'Dev' into WebMvc

This commit is contained in:
Carlos Cañizares Estévez 2016-11-03 17:25:34 +01:00
commit 45bc1f8929
15 changed files with 415 additions and 109 deletions

View File

@ -1,73 +1,121 @@
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 namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
{ {
[Route("/")] using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure;
using System;
using System.Linq;
using System.Threading.Tasks;
using ViewModel;
[Route("api/v1/[controller]")]
public class CatalogController : ControllerBase public class CatalogController : ControllerBase
{ {
private CatalogContext _context; private readonly CatalogContext _context;
public CatalogController(CatalogContext context) public CatalogController(CatalogContext context)
{ {
_context = context; _context = context;
} }
// GET api/values // GET api/v1/[controller]/items/[?pageSize=3&pageIndex=10]
[HttpGet] [HttpGet]
public IEnumerable<CatalogItem> Get() [Route("[action]")]
public async Task<IActionResult> Items(int pageSize = 10, int pageIndex = 0)
{ {
return _context.CatalogItems.ToList(); var totalItems = await _context.CatalogItems
.LongCountAsync();
var itemsOnPage = await _context.CatalogItems
.Skip(pageSize * pageIndex)
.Take(pageSize)
.ToListAsync();
var model = new PaginatedItemsViewModel<CatalogItem>(
pageIndex, pageSize, totalItems, itemsOnPage);
return Ok(model);
} }
// GET api/values/5 // GET api/v1/[controller]/items/withname/samplename
[HttpGet("{id}")]
public IActionResult Get(Guid id)
{
var item = _context.CatalogItems.FirstOrDefault(x=> x.Id == id);
if(item == null) [HttpGet]
[Route("[action]/withname/{name:minlength(1)}")]
public async Task<IActionResult> Items(string name, int pageSize = 10, int pageIndex = 0)
{ {
return NotFound();
var totalItems = await _context.CatalogItems
.Where(c => c.Name.StartsWith(name))
.LongCountAsync();
var itemsOnPage = await _context.CatalogItems
.Where(c => c.Name.StartsWith(name))
.Skip(pageSize * pageIndex)
.Take(pageSize)
.ToListAsync();
var model = new PaginatedItemsViewModel<CatalogItem>(
pageIndex, pageSize, totalItems, itemsOnPage);
return Ok(model);
} }
return new OkObjectResult(item); // GET api/v1/[controller]/items/type/1/brand/null
[HttpGet]
[Route("[action]/type/{catalogTypeId}/brand/{catalogBrandId}")]
public async Task<IActionResult> Items(int? catalogTypeId, int? catalogBrandId, int pageSize = 10, int pageIndex = 0)
{
var root = (IQueryable<CatalogItem>)_context.CatalogItems;
if (catalogTypeId.HasValue)
{
root = root.Where(ci => ci.CatalogTypeId == catalogTypeId);
} }
// POST api/values if (catalogBrandId.HasValue)
[HttpPost]
public IActionResult Post([FromBody]CatalogItem item)
{ {
try root = root.Where(ci => ci.CatalogBrandId == catalogBrandId);
{
_context.CatalogItems.Add(item);
_context.SaveChanges();
return Ok();
}
catch
{
return StatusCode(500, "Unable to add new catalog item");
}
} }
// PUT api/values/5 var totalItems = await root
[HttpPut("{id}")] .LongCountAsync();
public IActionResult Put(int id, [FromBody]CatalogItem item)
{ var itemsOnPage = await root
_context.CatalogItems.Update(item); .Skip(pageSize * pageIndex)
_context.SaveChanges(); .Take(pageSize)
return Ok(); .ToListAsync();
var model = new PaginatedItemsViewModel<CatalogItem>(
pageIndex, pageSize, totalItems, itemsOnPage);
return Ok(model);
} }
// DELETE api/values/5 // GET api/v1/[controller]/CatalogTypes
[HttpDelete("{id}")]
public IActionResult Delete(Guid id) [HttpGet]
[Route("[action]")]
public async Task<IActionResult> CatalogTypes()
{ {
return Ok(); var items = await _context.CatalogTypes
.ToListAsync();
return Ok(items);
}
// GET api/v1/[controller]/CatalogBrands
[HttpGet]
[Route("[action]")]
public async Task<IActionResult> CatalogBrands()
{
var items = await _context.CatalogBrands
.ToListAsync();
return Ok(items);
} }
} }
} }

View File

@ -1,4 +1,4 @@
FROM microsoft/aspnetcore FROM microsoft/aspnetcore:latest
WORKDIR /app WORKDIR /app
EXPOSE 80 EXPOSE 80
ADD . /app ADD . /app

View File

@ -0,0 +1,15 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class CatalogBrand
{
public int Id { get; set; }
public string Brand { get; set; }
}
}

View File

@ -0,0 +1,95 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure
{
using EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore;
using Npgsql.EntityFrameworkCore.PostgreSQL;
public class CatalogContext : DbContext
{
public CatalogContext(DbContextOptions options) : base(options)
{
}
public DbSet<CatalogItem> CatalogItems { get; set; }
public DbSet<CatalogBrand> CatalogBrands { get; set; }
public DbSet<CatalogType> CatalogTypes { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.HasSequence("idseqcatalog")
.StartsAt(1)
.IncrementsBy(1);
builder.HasSequence("idseqcatalogbrand")
.StartsAt(1)
.IncrementsBy(1);
builder.HasSequence("idseqcatalogtype")
.StartsAt(1)
.IncrementsBy(1);
builder.Entity<CatalogBrand>(ConfigureCatalogBrand);
builder.Entity<CatalogType>(ConfigureCatalogType);
builder.Entity<CatalogItem>(ConfigureCatalogItem);
builder.HasPostgresExtension("uuid-ossp");
}
void ConfigureCatalogItem(EntityTypeBuilder<CatalogItem> builder)
{
builder.ForNpgsqlToTable("catalog");
builder.Property(ci => ci.Id)
.HasDefaultValueSql("nextval('idseqcatalog')")
.IsRequired();
builder.Property(ci => ci.Name)
.IsRequired(true)
.HasMaxLength(50);
builder.Property(ci => ci.Price)
.IsRequired(true);
builder.Property(ci => ci.PictureUri)
.IsRequired(false);
builder.HasOne(ci => ci.CatalogBrand)
.WithMany()
.HasForeignKey(ci => ci.CatalogBrandId);
builder.HasOne(ci => ci.CatalogType)
.WithMany()
.HasForeignKey(ci => ci.CatalogTypeId);
}
void ConfigureCatalogBrand(EntityTypeBuilder<CatalogBrand> builder)
{
builder.ForNpgsqlToTable("catalogbrand");
builder.Property(cb => cb.Id)
.HasDefaultValueSql("nextval('idseqcatalogbrand')")
.IsRequired();
builder.Property(cb => cb.Brand)
.IsRequired()
.HasMaxLength(100);
}
void ConfigureCatalogType(EntityTypeBuilder<CatalogType> builder)
{
builder.ForNpgsqlToTable("catalogtype");
builder.Property(cb => cb.Id)
.HasDefaultValueSql("nextval('idseqcatalogtype')")
.IsRequired();
builder.Property(cb => cb.Type)
.IsRequired()
.HasMaxLength(100);
}
}
}

View File

@ -0,0 +1,77 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure
{
using Microsoft.AspNetCore.Builder;
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.EnsureDeleted();
context.Database.EnsureCreated();
if (!context.CatalogBrands.Any())
{
context.CatalogBrands.AddRange(
GetPreconfiguredCatalogBrands());
await context.SaveChangesAsync();
}
if (!context.CatalogTypes.Any())
{
context.CatalogTypes.AddRange(
GetPreconfiguredCatalogTypes());
await context.SaveChangesAsync();
}
if (!context.CatalogItems.Any())
{
context.CatalogItems.AddRange(
GetPreconfiguredItems());
await context.SaveChangesAsync();
}
}
}
static IEnumerable<CatalogBrand> GetPreconfiguredCatalogBrands()
{
return new List<CatalogBrand>()
{
new CatalogBrand() { Brand="Azure"},
new CatalogBrand() { Brand = "Visual Studio" }
};
}
static IEnumerable<CatalogType> GetPreconfiguredCatalogTypes()
{
return new List<CatalogType>()
{
new CatalogType() { Type="Mug"},
new CatalogType() { Type = "T-Shirt" }
};
}
static IEnumerable<CatalogItem> GetPreconfiguredItems()
{
return new List<CatalogItem>()
{
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=RoslynRedT-Shirt" },
new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = "Cupt Black & White Mug", Name = "Cupt Black & White Mug", Price= 17, PictureUri = "https://fakeimg.pl/370x240/EEEEEE/000/?text=CuptBlack&WhiteMug" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.PrismWhiteT-Shirt" },
new CatalogItem() { CatalogTypeId=2,CatalogBrandId=1, Description = ".NET Bot Black Sweatshirt", Name = ".NET Bot Black Sweatshirt", Price = 19.5M, PictureUri = "http://fakeimg.pl/370x240/EEEEEE/000/?text=.NETBotBlack" },
};
}
}
}

View File

@ -0,0 +1,27 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure
{
using System;
public class CatalogItem
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string PictureUri { get; set; }
public int CatalogTypeId { get; set; }
public CatalogType CatalogType { get; set; }
public int CatalogBrandId { get; set; }
public CatalogBrand CatalogBrand { get; set; }
public CatalogItem() { }
}
}

View File

@ -0,0 +1,14 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Infrastructure
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
public class CatalogType
{
public int Id { get; set; }
public string Type { get; set; }
}
}

View File

@ -1,23 +0,0 @@
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
{
public class CatalogContext : DbContext
{
public CatalogContext(DbContextOptions options): base(options)
{
}
public DbSet<CatalogItem> CatalogItems { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.HasPostgresExtension("uuid-ossp");
}
}
}

View File

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Catalog.API.Model
{
public class CatalogItem
{
public CatalogItem()
{
}
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; }
}
}

View File

@ -14,6 +14,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API
{ {
var host = new WebHostBuilder() var host = new WebHostBuilder()
.UseKestrel() .UseKestrel()
.UseUrls(Environment.GetEnvironmentVariable("ASPNETCORE_URLS") ?? String.Empty)
.UseContentRoot(Directory.GetCurrentDirectory()) .UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>() .UseStartup<Startup>()
.Build(); .Build();

View File

@ -1,48 +1,81 @@
using System; using Microsoft.AspNetCore.Builder;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; 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.Infrastructure;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.eShopOnContainers.Services.Catalog.API.Model; using Newtonsoft.Json.Serialization;
using Microsoft.EntityFrameworkCore; using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Catalog.API namespace Microsoft.eShopOnContainers.Services.Catalog.API
{ {
public class Startup public class Startup
{ {
public IConfigurationRoot Configuration { get; }
public Startup(IHostingEnvironment env) public Startup(IHostingEnvironment env)
{ {
var builder = new ConfigurationBuilder() var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath) .SetBasePath(env.ContentRootPath)
.AddJsonFile("settings.json") .AddJsonFile($"settings.{env.EnvironmentName}.json",optional:false)
.AddEnvironmentVariables(); .AddEnvironmentVariables();
Configuration = builder.Build(); 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) public void ConfigureServices(IServiceCollection services)
{ {
services.AddDbContext<CatalogContext>(c => { services.AddSingleton<IConfiguration>(Configuration);
services.AddDbContext<CatalogContext>(c =>
{
c.UseNpgsql(Configuration["ConnectionString"]); c.UseNpgsql(Configuration["ConnectionString"]);
c.ConfigureWarnings(wb =>
{
wb.Throw(RelationalEventId.QueryClientEvaluationWarning);
});
}); });
// Add framework services. // Add framework services.
services.AddCors();
services.AddMvcCore() 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. // 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) public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{ {
//Configure logs
if(env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug(); loggerFactory.AddDebug();
//Seed Data
CatalogContextSeed.SeedAsync(app)
.Wait();
// Use frameworks
app.UseCors(policyBuilder=>policyBuilder.AllowAnyOrigin());
app.UseMvc(); app.UseMvc();
} }
} }

View File

@ -0,0 +1,24 @@
namespace Microsoft.eShopOnContainers.Services.Catalog.API.ViewModel
{
using System.Collections.Generic;
public class PaginatedItemsViewModel<TEntity> where TEntity : class
{
public int PageIndex { get; private set; }
public int PageSize { get; private set; }
public long Count { get; private set; }
public IEnumerable<TEntity> Data { get; private set; }
public PaginatedItemsViewModel(int pageIndex, int pageSize, long count, IEnumerable<TEntity> data)
{
this.PageIndex = pageIndex;
this.PageSize = pageSize;
this.Count = count;
this.Data = data;
}
}
}

View File

@ -1,21 +1,25 @@
{ {
"dependencies": { "dependencies": {
"Microsoft.NETCore.App": { "Microsoft.NETCore.App": {
"version": "1.0.0", "version": "1.0.1",
"type": "platform" "type": "platform"
}, },
"Microsoft.AspNetCore.Mvc": "1.0.0", "Microsoft.AspNetCore.Mvc": "1.0.1",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0", "Microsoft.AspNetCore.Diagnostics": "1.0.0",
"Microsoft.EntityFrameworkCore": "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.EnvironmentVariables": "1.0.0",
"Microsoft.Extensions.Configuration.Json": "1.0.0", "Microsoft.Extensions.Configuration.Json": "1.0.0",
"Microsoft.Extensions.Logging": "1.0.0", "Microsoft.Extensions.Logging": "1.0.0",
"Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0",
"Microsoft.Extensions.Logging.Debug": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0",
"Microsoft.Extensions.Options.ConfigurationExtensions": "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": { "frameworks": {
"netcoreapp1.0": { "netcoreapp1.0": {
"imports": [ "imports": [
@ -39,7 +43,7 @@
"wwwroot", "wwwroot",
"Views", "Views",
"Areas/**/Views", "Areas/**/Views",
"settings.json", "settings.Production.json",
"web.config", "web.config",
"project.json", "project.json",
"Dockerfile" "Dockerfile"

View File

@ -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=CatalogDB;username=postgres;password=postgres",
"Logging": { "Logging": {
"IncludeScopes": false, "IncludeScopes": false,
"LogLevel": { "LogLevel": {

View File

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