From 1ac1dae9d36398f27f7bb61c8eec1ea70b6e412b Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Thu, 1 Jun 2017 10:10:00 +0200 Subject: [PATCH 01/23] Add Marketing.API project --- ...irewall-rules-for-sts-auth-thru-docker.ps1 | 4 +- .../Marketing/Marketing.API/.dockerignore | 3 + .../Controllers/CampaignsController.cs | 36 +++++ .../Controllers/HomeController.cs | 13 ++ .../Marketing/Marketing.API/Dockerfile | 6 + .../Infrastructure/MarketingContext.cs | 25 ++++ .../Infrastructure/MarketingContextSeed.cs | 20 +++ .../Marketing.API/Marketing.API.csproj | 56 ++++++++ .../Marketing.API/MarketingSettings.cs | 7 + .../Marketing/Marketing.API/Model/Campaing.cs | 7 + .../Marketing/Marketing.API/Program.cs | 21 +++ .../Properties/launchSettings.json | 29 ++++ .../Marketing/Marketing.API/Startup.cs | 125 ++++++++++++++++++ .../appsettings.Development.json | 10 ++ .../Marketing/Marketing.API/appsettings.json | 10 ++ .../FunctionalTests/FunctionalTests.csproj | 4 + .../IntegrationTests/IntegrationTests.csproj | 4 + test/Services/UnitTest/UnitTest.csproj | 4 + 18 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 src/Services/Marketing/Marketing.API/.dockerignore create mode 100644 src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs create mode 100644 src/Services/Marketing/Marketing.API/Controllers/HomeController.cs create mode 100644 src/Services/Marketing/Marketing.API/Dockerfile create mode 100644 src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs create mode 100644 src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs create mode 100644 src/Services/Marketing/Marketing.API/Marketing.API.csproj create mode 100644 src/Services/Marketing/Marketing.API/MarketingSettings.cs create mode 100644 src/Services/Marketing/Marketing.API/Model/Campaing.cs create mode 100644 src/Services/Marketing/Marketing.API/Program.cs create mode 100644 src/Services/Marketing/Marketing.API/Properties/launchSettings.json create mode 100644 src/Services/Marketing/Marketing.API/Startup.cs create mode 100644 src/Services/Marketing/Marketing.API/appsettings.Development.json create mode 100644 src/Services/Marketing/Marketing.API/appsettings.json diff --git a/cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 b/cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 index be63d8a25..e3545c584 100644 --- a/cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 +++ b/cli-windows/add-firewall-rules-for-sts-auth-thru-docker.ps1 @@ -21,6 +21,6 @@ try { Write-Host "Rule found" } catch [Exception] { - New-NetFirewallRule -DisplayName eShopOnContainers-Inbound -Confirm -Description "eShopOnContainers Inbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5105 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Inbound - New-NetFirewallRule -DisplayName eShopOnContainers-Outbound -Confirm -Description "eShopOnContainers Outbound Rule for port range 5100-5105" -LocalAddress Any -LocalPort 5100-5105 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Outbound + New-NetFirewallRule -DisplayName eShopOnContainers-Inbound -Confirm -Description "eShopOnContainers Inbound Rule for port range 5100-5110" -LocalAddress Any -LocalPort 5100-5110 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Inbound + New-NetFirewallRule -DisplayName eShopOnContainers-Outbound -Confirm -Description "eShopOnContainers Outbound Rule for port range 5100-5110" -LocalAddress Any -LocalPort 5100-5110 -Protocol tcp -RemoteAddress Any -RemotePort Any -Direction Outbound } \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/.dockerignore b/src/Services/Marketing/Marketing.API/.dockerignore new file mode 100644 index 000000000..d8f8175f6 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/.dockerignore @@ -0,0 +1,3 @@ +* +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs new file mode 100644 index 000000000..e5914b3ca --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -0,0 +1,36 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Controllers +{ + using System.Collections.Generic; + using Microsoft.AspNetCore.Mvc; + + [Route("api/[controller]")] + public class CampaignsController : Controller + { + [HttpGet] + public IEnumerable Get() + { + return new string[] { "value1", "value2" }; + } + + [HttpGet("{id:int}")] + public string Get(int id) + { + return "value"; + } + + [HttpPost] + public void Post([FromBody]string value) + { + } + + [HttpPut("{id:int}")] + public void Put(int id, [FromBody]string value) + { + } + + [HttpDelete("{id:int}")] + public void Delete(int id) + { + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Controllers/HomeController.cs b/src/Services/Marketing/Marketing.API/Controllers/HomeController.cs new file mode 100644 index 000000000..d37386b9a --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Controllers/HomeController.cs @@ -0,0 +1,13 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Controllers +{ + using Microsoft.AspNetCore.Mvc; + + // GET: // + public class HomeController : Controller + { + public IActionResult Index() + { + return new RedirectResult("~/swagger"); + } + } +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dockerfile b/src/Services/Marketing/Marketing.API/Dockerfile new file mode 100644 index 000000000..ea0590a9f --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Dockerfile @@ -0,0 +1,6 @@ +FROM microsoft/aspnetcore:1.1 +ARG source +WORKDIR /app +EXPOSE 80 +COPY ${source:-obj/Docker/publish} . +ENTRYPOINT ["dotnet", "Marketing.API.dll"] diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs new file mode 100644 index 000000000..dc3a80564 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs @@ -0,0 +1,25 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure +{ + using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore.Metadata.Builders; + using Microsoft.eShopOnContainers.Services.Marketing.API.Model; + + public class MarketingContext : DbContext + { + public MarketingContext(DbContextOptions options) : base(options) + { + } + + public DbSet Campaings { get; set; } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity(ConfigureCampaings); + } + + void ConfigureCampaings(EntityTypeBuilder builder) + { + + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs new file mode 100644 index 000000000..46d82e3be --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs @@ -0,0 +1,20 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure +{ + using Microsoft.AspNetCore.Builder; + using Microsoft.EntityFrameworkCore; + using Microsoft.Extensions.Logging; + using System.Threading.Tasks; + + public class MarketingContextSeed + { + public static async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, int? retry = 0) + { + var context = (MarketingContext)applicationBuilder + .ApplicationServices.GetService(typeof(MarketingContext)); + + context.Database.Migrate(); + + //TODO: add model seeding + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Marketing.API.csproj b/src/Services/Marketing/Marketing.API/Marketing.API.csproj new file mode 100644 index 000000000..1e6ec2416 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Marketing.API.csproj @@ -0,0 +1,56 @@ + + + + netcoreapp1.1 + ..\..\..\..\docker-compose.dcproj + Microsoft.eShopOnContainers.Services.Marketing.API + portable-net45+win8 + aspnet-TestApp-ce941b30-19cf-4972-b34f-d03f2e7976ed + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Services/Marketing/Marketing.API/MarketingSettings.cs b/src/Services/Marketing/Marketing.API/MarketingSettings.cs new file mode 100644 index 000000000..c6e1dfc40 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/MarketingSettings.cs @@ -0,0 +1,7 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API +{ + public class MarketingSettings + { + public string ConnectionString { get; set; } + } +} diff --git a/src/Services/Marketing/Marketing.API/Model/Campaing.cs b/src/Services/Marketing/Marketing.API/Model/Campaing.cs new file mode 100644 index 000000000..62dca8483 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Model/Campaing.cs @@ -0,0 +1,7 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model +{ + public class Campaing + { + //TODO: Add Campaing properties + } +} diff --git a/src/Services/Marketing/Marketing.API/Program.cs b/src/Services/Marketing/Marketing.API/Program.cs new file mode 100644 index 000000000..cbda3e538 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Program.cs @@ -0,0 +1,21 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API +{ + using System.IO; + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + + public class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseStartup() + .UseApplicationInsights() + .Build(); + + host.Run(); + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Properties/launchSettings.json b/src/Services/Marketing/Marketing.API/Properties/launchSettings.json new file mode 100644 index 000000000..4adb2bd6a --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:52058/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Marketing.API": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:52059" + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Startup.cs b/src/Services/Marketing/Marketing.API/Startup.cs new file mode 100644 index 000000000..91afead63 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Startup.cs @@ -0,0 +1,125 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API +{ + using Microsoft.AspNetCore.Builder; + using Microsoft.AspNetCore.Hosting; + using Microsoft.EntityFrameworkCore.Infrastructure; + using Microsoft.EntityFrameworkCore; + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; + using Microsoft.Extensions.Configuration; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; + using System.Reflection; + using System; + + public class Startup + { + public Startup(IHostingEnvironment env) + { + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + + if (env.IsDevelopment()) + { + builder.AddUserSecrets(typeof(Startup).GetTypeInfo().Assembly); + } + + builder.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.AddHealthChecks(checks => + //{ + // var minutes = 1; + // if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) + // { + // minutes = minutesParsed; + // } + // checks.AddSqlCheck("MarketingDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); + //}); + + // Add framework services. + services.AddMvc(); + + services.AddDbContext(options => + { + options.UseSqlServer(Configuration["ConnectionString"], + sqlServerOptionsAction: sqlOptions => + { + sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); + //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency + sqlOptions.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null); + }); + + // Changing default behavior when client evaluation occurs to throw. + // Default in EF Core would be to log a warning when client evaluation is performed. + options.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning)); + //Check Client vs. Server evaluation: https://docs.microsoft.com/en-us/ef/core/querying/client-eval + }); + + // Add framework services. + services.AddSwaggerGen(options => + { + options.DescribeAllEnumsAsStrings(); + options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info + { + Title = "Marketing HTTP API", + Version = "v1", + Description = "The Marketing Service HTTP API", + TermsOfService = "Terms Of Service" + }); + }); + + services.AddCors(options => + { + options.AddPolicy("CorsPolicy", + builder => builder.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader() + .AllowCredentials()); + }); + } + + // 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) + { + loggerFactory.AddConsole(Configuration.GetSection("Logging")); + loggerFactory.AddDebug(); + + app.UseCors("CorsPolicy"); + + ConfigureAuth(app); + + app.UseMvcWithDefaultRoute(); + + app.UseSwagger() + .UseSwaggerUI(c => + { + c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); + }); + + //MarketingContextSeed.SeedAsync(app, loggerFactory) + // .Wait(); + } + + + protected virtual void ConfigureAuth(IApplicationBuilder app) + { + var identityUrl = Configuration.GetValue("IdentityUrl"); + app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions + { + Authority = identityUrl.ToString(), + ApiName = "marketing", + RequireHttpsMetadata = false + }); + } + } +} diff --git a/src/Services/Marketing/Marketing.API/appsettings.Development.json b/src/Services/Marketing/Marketing.API/appsettings.Development.json new file mode 100644 index 000000000..fa8ce71a9 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/appsettings.Development.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/Services/Marketing/Marketing.API/appsettings.json b/src/Services/Marketing/Marketing.API/appsettings.json new file mode 100644 index 000000000..36bdcad2c --- /dev/null +++ b/src/Services/Marketing/Marketing.API/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Warning" + } + }, + "ConnectionString": "127.0.0.1", + "IdentityUrl": "http://localhost:5105" +} diff --git a/test/Services/FunctionalTests/FunctionalTests.csproj b/test/Services/FunctionalTests/FunctionalTests.csproj index 2b8ee1f44..54a74beda 100644 --- a/test/Services/FunctionalTests/FunctionalTests.csproj +++ b/test/Services/FunctionalTests/FunctionalTests.csproj @@ -38,4 +38,8 @@ + + + + \ No newline at end of file diff --git a/test/Services/IntegrationTests/IntegrationTests.csproj b/test/Services/IntegrationTests/IntegrationTests.csproj index b71e1d4c4..60911dd4d 100644 --- a/test/Services/IntegrationTests/IntegrationTests.csproj +++ b/test/Services/IntegrationTests/IntegrationTests.csproj @@ -46,4 +46,8 @@ + + + + diff --git a/test/Services/UnitTest/UnitTest.csproj b/test/Services/UnitTest/UnitTest.csproj index 3a80e594b..8955e4a45 100644 --- a/test/Services/UnitTest/UnitTest.csproj +++ b/test/Services/UnitTest/UnitTest.csproj @@ -28,4 +28,8 @@ + + + + From 6082359f080c856005012e750fe09222549c2943 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Thu, 1 Jun 2017 10:10:20 +0200 Subject: [PATCH 02/23] Add docker support --- docker-compose.override.yml | 9 ++++ docker-compose.prod.yml | 11 ++++- docker-compose.vs.debug.yml | 15 +++++++ docker-compose.vs.release.yml | 10 +++++ docker-compose.yml | 11 ++++- eShopOnContainers-ServicesAndWebApps.sln | 56 +++++++++++++++++++++++- 6 files changed, 109 insertions(+), 3 deletions(-) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 04d1c4d9c..079c86768 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -95,3 +95,12 @@ services: - spa=http://webspa/hc ports: - "5107:80" + + marketing.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + ports: + - "5110:80" \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index c5d8839ea..b1dc89754 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -99,4 +99,13 @@ services: - spa=http://webspa/hc ports: - - "5107:80" \ No newline at end of file + - "5107:80" + + marketing.api: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + ports: + - "5110:80" \ No newline at end of file diff --git a/docker-compose.vs.debug.yml b/docker-compose.vs.debug.yml index 2e7145637..ef2ff662b 100644 --- a/docker-compose.vs.debug.yml +++ b/docker-compose.vs.debug.yml @@ -105,3 +105,18 @@ services: entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" + + marketing.api: + image: eshop/marketing.api:dev + build: + args: + source: ${DOCKER_BUILD_SOURCE} + environment: + - DOTNET_USE_POLLING_FILE_WATCHER=1 + volumes: + - ./src/Marketing/Marketing.API:/app + - ~/.nuget/packages:/root/.nuget/packages:ro + - ~/clrdbg:/clrdbg:ro + entrypoint: tail -f /dev/null + labels: + - "com.microsoft.visualstudio.targetoperatingsystem=linux" diff --git a/docker-compose.vs.release.yml b/docker-compose.vs.release.yml index d1ca5b2c6..8be29c095 100644 --- a/docker-compose.vs.release.yml +++ b/docker-compose.vs.release.yml @@ -70,3 +70,13 @@ services: entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" + + marketing.api: + build: + args: + source: ${DOCKER_BUILD_SOURCE} + volumes: + - ~/clrdbg:/clrdbg:ro + entrypoint: tail -f /dev/null + labels: + - "com.microsoft.visualstudio.targetoperatingsystem=linux" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 21f3972f2..9612b38fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,6 +36,15 @@ services: depends_on: - sql.data + marketing.api: + image: eshop/marketing.api + build: + context: ./src/Services/Marketing/Marketing.API + dockerfile: Dockerfile + depends_on: + - sql.data + - identity.api + webspa: image: eshop/webspa build: @@ -74,4 +83,4 @@ services: build: context: ./src/Web/WebStatus dockerfile: Dockerfile - \ No newline at end of file + diff --git a/eShopOnContainers-ServicesAndWebApps.sln b/eShopOnContainers-ServicesAndWebApps.sln index eb0e83e02..b0734add6 100644 --- a/eShopOnContainers-ServicesAndWebApps.sln +++ b/eShopOnContainers-ServicesAndWebApps.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.3 +VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932D8224-11F6-4D07-B109-DA28AD288A63}" EndProject @@ -76,6 +76,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Health EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventBus.Tests", "src\BuildingBlocks\EventBus\EventBus.Tests\EventBus.Tests.csproj", "{89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Marketing", "Marketing", "{A5260DE0-1FDD-467E-9CC1-A028AB081CEE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Marketing.API", "src\Services\Marketing\Marketing.API\Marketing.API.csproj", "{DF395F85-B010-465D-857A-7EBCC512C0C2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Ad-Hoc|Any CPU = Ad-Hoc|Any CPU @@ -1002,6 +1006,54 @@ Global {89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x64.Build.0 = Release|Any CPU {89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.ActiveCfg = Release|Any CPU {89D80DF1-32E1-4AAF-970F-DA0AA6881F9D}.Release|x86.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|ARM.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhone.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x64.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x64.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x86.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.AppStore|x86.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|ARM.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|ARM.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhone.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x64.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x86.ActiveCfg = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Debug|x86.Build.0 = Debug|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|Any CPU.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|ARM.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|ARM.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhone.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhone.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x64.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x64.Build.0 = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x86.ActiveCfg = Release|Any CPU + {DF395F85-B010-465D-857A-7EBCC512C0C2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1038,5 +1090,7 @@ Global {22A0F9C1-2D4A-4107-95B7-8459E6688BC5} = {A81ECBC2-6B00-4DCD-8388-469174033379} {4BD76717-3102-4969-8C2C-BAAA3F0263B6} = {A81ECBC2-6B00-4DCD-8388-469174033379} {89D80DF1-32E1-4AAF-970F-DA0AA6881F9D} = {807BB76E-B2BB-47A2-A57B-3D1B20FF5E7F} + {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} = {91CF7717-08AB-4E65-B10E-0B426F01E2E8} + {DF395F85-B010-465D-857A-7EBCC512C0C2} = {A5260DE0-1FDD-467E-9CC1-A028AB081CEE} EndGlobalSection EndGlobal From 665733c40e00574cf93917c5b8210625838a7ae2 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Thu, 1 Jun 2017 10:10:37 +0200 Subject: [PATCH 03/23] Add marketing resource to identity --- src/Services/Identity/Identity.API/Configuration/Config.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Services/Identity/Identity.API/Configuration/Config.cs b/src/Services/Identity/Identity.API/Configuration/Config.cs index 260989da4..7de28772e 100644 --- a/src/Services/Identity/Identity.API/Configuration/Config.cs +++ b/src/Services/Identity/Identity.API/Configuration/Config.cs @@ -12,7 +12,8 @@ namespace Identity.API.Configuration return new List { new ApiResource("orders", "Orders Service"), - new ApiResource("basket", "Basket Service") + new ApiResource("basket", "Basket Service"), + new ApiResource("marketing", "Marketing Service") }; } From 32247c503d0783375a4e06ff8326451b141f36e0 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Thu, 1 Jun 2017 20:15:21 +0200 Subject: [PATCH 04/23] Add marketing api --- docker-compose.override.yml | 20 ++-- docker-compose.prod.yml | 20 ++-- docker-compose.vs.debug.yml | 26 +++-- docker-compose.vs.release.yml | 10 +- .../Controllers/CampaignsController.cs | 68 +++++++++-- .../Marketing/Marketing.API/Dockerfile | 2 +- .../Infrastructure/MarketingContext.cs | 69 ++++++++++- .../Infrastructure/MarketingContextSeed.cs | 49 +++++++- .../Marketing.API/Marketing.API.csproj | 30 ++--- .../20170601175200_Initial.Designer.cs | 108 ++++++++++++++++++ .../Migrations/20170601175200_Initial.cs | 71 ++++++++++++ .../MarketingContextModelSnapshot.cs | 107 +++++++++++++++++ .../Marketing/Marketing.API/Model/Campaing.cs | 19 ++- .../Marketing/Marketing.API/Model/Location.cs | 9 ++ .../Marketing/Marketing.API/Model/Rule.cs | 29 +++++ .../Marketing/Marketing.API/Program.cs | 1 - .../Properties/launchSettings.json | 6 +- .../Marketing/Marketing.API/Startup.cs | 14 +-- .../appsettings.Development.json | 4 +- 19 files changed, 566 insertions(+), 96 deletions(-) create mode 100644 src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.Designer.cs create mode 100644 src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.cs create mode 100644 src/Services/Marketing/Marketing.API/Migrations/MarketingContextModelSnapshot.cs create mode 100644 src/Services/Marketing/Marketing.API/Model/Location.cs create mode 100644 src/Services/Marketing/Marketing.API/Model/Rule.cs diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 079c86768..1348a6e9b 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -49,6 +49,15 @@ services: ports: - "5102:80" + marketing.api: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + ports: + - "5110:80" + webspa: environment: - ASPNETCORE_ENVIRONMENT=Development @@ -94,13 +103,4 @@ services: - mvc=http://webmvc/hc - spa=http://webspa/hc ports: - - "5107:80" - - marketing.api: - environment: - - ASPNETCORE_ENVIRONMENT=Development - - ASPNETCORE_URLS=http://0.0.0.0:80 - - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word - - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - ports: - - "5110:80" \ No newline at end of file + - "5107:80" \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index b1dc89754..b9c46b4c2 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -54,6 +54,15 @@ services: ports: - "5102:80" + marketing.api: + environment: + - ASPNETCORE_ENVIRONMENT=Production + - ASPNETCORE_URLS=http://0.0.0.0:80 + - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word + - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. + ports: + - "5110:80" + webspa: environment: - ASPNETCORE_ENVIRONMENT=Production @@ -99,13 +108,4 @@ services: - spa=http://webspa/hc ports: - - "5107:80" - - marketing.api: - environment: - - ASPNETCORE_ENVIRONMENT=Production - - ASPNETCORE_URLS=http://0.0.0.0:80 - - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word - - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - ports: - - "5110:80" \ No newline at end of file + - "5107:80" \ No newline at end of file diff --git a/docker-compose.vs.debug.yml b/docker-compose.vs.debug.yml index ef2ff662b..85195a493 100644 --- a/docker-compose.vs.debug.yml +++ b/docker-compose.vs.debug.yml @@ -61,62 +61,64 @@ services: labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" - webspa: - image: eshop/webspa:dev + marketing.api: + image: eshop/marketing.api:dev build: args: source: ${DOCKER_BUILD_SOURCE} environment: - DOTNET_USE_POLLING_FILE_WATCHER=1 volumes: - - ./src/Web/WebSPA:/app + - ./src/Services/Marketing/Marketing.API:/app - ~/.nuget/packages:/root/.nuget/packages:ro - ~/clrdbg:/clrdbg:ro entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" - webmvc: - image: eshop/webmvc:dev + + webspa: + image: eshop/webspa:dev build: args: source: ${DOCKER_BUILD_SOURCE} environment: - DOTNET_USE_POLLING_FILE_WATCHER=1 volumes: - - ./src/Web/WebMVC:/app + - ./src/Web/WebSPA:/app - ~/.nuget/packages:/root/.nuget/packages:ro - ~/clrdbg:/clrdbg:ro entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" - webstatus: - image: eshop/webstatus:dev + webmvc: + image: eshop/webmvc:dev build: args: source: ${DOCKER_BUILD_SOURCE} environment: - DOTNET_USE_POLLING_FILE_WATCHER=1 volumes: - - ./src/Web/WebStatus:/app + - ./src/Web/WebMVC:/app - ~/.nuget/packages:/root/.nuget/packages:ro - ~/clrdbg:/clrdbg:ro entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" - marketing.api: - image: eshop/marketing.api:dev + webstatus: + image: eshop/webstatus:dev build: args: source: ${DOCKER_BUILD_SOURCE} environment: - DOTNET_USE_POLLING_FILE_WATCHER=1 volumes: - - ./src/Marketing/Marketing.API:/app + - ./src/Web/WebStatus:/app - ~/.nuget/packages:/root/.nuget/packages:ro - ~/clrdbg:/clrdbg:ro entrypoint: tail -f /dev/null labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" + diff --git a/docker-compose.vs.release.yml b/docker-compose.vs.release.yml index 8be29c095..f01b21eff 100644 --- a/docker-compose.vs.release.yml +++ b/docker-compose.vs.release.yml @@ -41,7 +41,7 @@ services: labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" - webspa: + marketing.api: build: args: source: ${DOCKER_BUILD_SOURCE} @@ -51,7 +51,7 @@ services: labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" - webmvc: + webspa: build: args: source: ${DOCKER_BUILD_SOURCE} @@ -61,7 +61,7 @@ services: labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" - webstatus: + webmvc: build: args: source: ${DOCKER_BUILD_SOURCE} @@ -71,7 +71,7 @@ services: labels: - "com.microsoft.visualstudio.targetoperatingsystem=linux" - marketing.api: + webstatus: build: args: source: ${DOCKER_BUILD_SOURCE} @@ -79,4 +79,4 @@ services: - ~/clrdbg:/clrdbg:ro entrypoint: tail -f /dev/null labels: - - "com.microsoft.visualstudio.targetoperatingsystem=linux" \ No newline at end of file + - "com.microsoft.visualstudio.targetoperatingsystem=linux" diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index e5914b3ca..b029bfa51 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -1,36 +1,86 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Controllers { - using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; + using System.Threading.Tasks; + using Microsoft.eShopOnContainers.Services.Marketing.API.Model; + using Microsoft.EntityFrameworkCore; [Route("api/[controller]")] public class CampaignsController : Controller { + private readonly MarketingContext _context; + + public CampaignsController(MarketingContext context) + { + _context = context; + } + [HttpGet] - public IEnumerable Get() + public async Task GetAllCampaigns() { - return new string[] { "value1", "value2" }; + var campaignList = await _context.Campaigns + .Include(c => c.Rules) + .ToListAsync(); + + return Ok(campaignList); } [HttpGet("{id:int}")] - public string Get(int id) + public async Task GetCampaignById(int id) { - return "value"; + var campaign = await _context.Campaigns + .Include(c => c.Rules) + .SingleAsync(c => c.Id == id); + + if (campaign is null) + { + return NotFound(); + } + + return Ok(campaign); } [HttpPost] - public void Post([FromBody]string value) + public async Task CreateCampaign([FromBody] Campaign campaign) { + await _context.Campaigns.AddAsync(campaign); + await _context.SaveChangesAsync(); + + return CreatedAtAction(nameof(GetCampaignById), new { id = campaign.Id }, null); } [HttpPut("{id:int}")] - public void Put(int id, [FromBody]string value) + public async Task UpdateCampaign(int id, [FromBody]Campaign campaign) { + var campaignToUpdate = await _context.Campaigns.FindAsync(id); + if (campaign is null) + { + return NotFound(); + } + + campaignToUpdate.Description = campaign.Description; + campaignToUpdate.From = campaign.From; + campaignToUpdate.To = campaign.To; + + await _context.SaveChangesAsync(); + + return CreatedAtAction(nameof(GetCampaignById), new { id = campaignToUpdate.Id }, null); } [HttpDelete("{id:int}")] - public void Delete(int id) + public async Task Delete(int id) { + var campaign = await _context.Campaigns.FindAsync(id); + if (campaign is null) + { + return NotFound(); + } + + _context.Campaigns.Remove(campaign); + await _context.SaveChangesAsync(); + + return NoContent(); } } -} +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dockerfile b/src/Services/Marketing/Marketing.API/Dockerfile index ea0590a9f..54a79a5e4 100644 --- a/src/Services/Marketing/Marketing.API/Dockerfile +++ b/src/Services/Marketing/Marketing.API/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore:1.1 +FROM microsoft/aspnetcore:1.1.2 ARG source WORKDIR /app EXPOSE 80 diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs index dc3a80564..798d8d152 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs @@ -6,20 +6,77 @@ public class MarketingContext : DbContext { - public MarketingContext(DbContextOptions options) : base(options) - { + public MarketingContext(DbContextOptions options) : base(options) + { } - public DbSet Campaings { get; set; } + public DbSet Campaigns { get; set; } + + public DbSet Rules { get; set; } + public DbSet UserProfileRules { get; set; } + public DbSet PurchaseHistoryRules { get; set; } + public DbSet UserLocationRules { get; set; } protected override void OnModelCreating(ModelBuilder builder) { - builder.Entity(ConfigureCampaings); + builder.Entity(ConfigureCampaigns); + builder.Entity(ConfigureRules); + builder.Entity(ConfigureUserLocationRules); } - void ConfigureCampaings(EntityTypeBuilder builder) + void ConfigureCampaigns(EntityTypeBuilder builder) { + builder.ToTable("Campaign"); + + builder.HasKey(ci => ci.Id); + + builder.Property(ci => ci.Id) + .ForSqlServerUseSequenceHiLo("campaign_hilo") + .IsRequired(); + + builder.Property(m => m.Description) + .HasColumnName("Description") + .IsRequired(); + builder.Property(m => m.From) + .HasColumnName("From") + .IsRequired(); + + builder.Property(m => m.To) + .HasColumnName("To") + .IsRequired(); + + builder.Property(m => m.Description) + .HasColumnName("Description") + .IsRequired(); + + builder.HasMany(m => m.Rules) + .WithOne(r => r.Campaign) + .HasForeignKey(m => m.CampaignId) + .IsRequired(); + } + + void ConfigureRules(EntityTypeBuilder builder) + { + builder.ToTable("Rules"); + + builder.HasKey(r => r.Id); + + builder.HasDiscriminator("RuleTypeId") + .HasValue(1) + .HasValue(2) + .HasValue(3); + + builder.Property(r => r.Description) + .HasColumnName("Description") + .IsRequired(); + } + + void ConfigureUserLocationRules(EntityTypeBuilder builder) + { + builder.Property(r => r.LocationId) + .HasColumnName("LocationId") + .IsRequired(); } } -} +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs index 46d82e3be..9d2bb0157 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs @@ -2,7 +2,11 @@ { using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; + using Microsoft.eShopOnContainers.Services.Marketing.API.Model; using Microsoft.Extensions.Logging; + using System; + using System.Collections.Generic; + using System.Linq; using System.Threading.Tasks; public class MarketingContextSeed @@ -14,7 +18,50 @@ context.Database.Migrate(); - //TODO: add model seeding + if (!context.Campaigns.Any()) + { + context.Campaigns.AddRange( + GetPreconfiguredMarketings()); + + await context.SaveChangesAsync(); + } + } + + static List GetPreconfiguredMarketings() + { + return new List + { + new Campaign + { + Description = "Campaign1", + From = DateTime.Now, + To = DateTime.Now.AddDays(7), + Url = "http://CampaignUrl.test/12f09ed3cef54187123f500ad", + Rules = new List + { + new UserLocationRule + { + Description = "UserLocationRule1", + LocationId = 1 + } + } + }, + new Campaign + { + Description = "Campaign2", + From = DateTime.Now.AddDays(7), + To = DateTime.Now.AddDays(14), + Url = "http://CampaignUrl.test/02a59eda65f241871239000ff", + Rules = new List + { + new UserLocationRule + { + Description = "UserLocationRule2", + LocationId = 1 + } + } + } + }; } } } diff --git a/src/Services/Marketing/Marketing.API/Marketing.API.csproj b/src/Services/Marketing/Marketing.API/Marketing.API.csproj index 1e6ec2416..f65be06bb 100644 --- a/src/Services/Marketing/Marketing.API/Marketing.API.csproj +++ b/src/Services/Marketing/Marketing.API/Marketing.API.csproj @@ -2,20 +2,20 @@ netcoreapp1.1 + 1.1.2 + Exe ..\..\..\..\docker-compose.dcproj Microsoft.eShopOnContainers.Services.Marketing.API portable-net45+win8 - aspnet-TestApp-ce941b30-19cf-4972-b34f-d03f2e7976ed + aspnet-Marketing.API-20161122013619 - - - + @@ -24,23 +24,14 @@ + + + + - - - - - - - - - - - - - - - + + @@ -52,5 +43,4 @@ - diff --git a/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.Designer.cs b/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.Designer.cs new file mode 100644 index 000000000..b0305f171 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.Designer.cs @@ -0,0 +1,108 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; + +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations +{ + [DbContext(typeof(MarketingContext))] + [Migration("20170601175200_Initial")] + partial class Initial + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.2") + .HasAnnotation("SqlServer:Sequence:.campaign_hilo", "'campaign_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "campaign_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Description") + .IsRequired() + .HasColumnName("Description"); + + b.Property("From") + .HasColumnName("From"); + + b.Property("To") + .HasColumnName("To"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("Campaign"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CampaignId"); + + b.Property("Description") + .IsRequired() + .HasColumnName("Description"); + + b.Property("RuleTypeId"); + + b.HasKey("Id"); + + b.HasIndex("CampaignId"); + + b.ToTable("Rules"); + + b.HasDiscriminator("RuleTypeId"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.PurchaseHistoryRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + + b.ToTable("PurchaseHistoryRule"); + + b.HasDiscriminator().HasValue(2); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserLocationRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + b.Property("LocationId") + .HasColumnName("LocationId"); + + b.ToTable("UserLocationRule"); + + b.HasDiscriminator().HasValue(3); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserProfileRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + + b.ToTable("UserProfileRule"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => + { + b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", "Campaign") + .WithMany("Rules") + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.cs b/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.cs new file mode 100644 index 000000000..dee3aa12f --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Metadata; + +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations +{ + public partial class Initial : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateSequence( + name: "campaign_hilo", + incrementBy: 10); + + migrationBuilder.CreateTable( + name: "Campaign", + columns: table => new + { + Id = table.Column(nullable: false), + Description = table.Column(nullable: false), + From = table.Column(nullable: false), + To = table.Column(nullable: false), + Url = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Campaign", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Rules", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + CampaignId = table.Column(nullable: false), + Description = table.Column(nullable: false), + RuleTypeId = table.Column(nullable: false), + LocationId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Rules", x => x.Id); + table.ForeignKey( + name: "FK_Rules_Campaign_CampaignId", + column: x => x.CampaignId, + principalTable: "Campaign", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Rules_CampaignId", + table: "Rules", + column: "CampaignId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Rules"); + + migrationBuilder.DropTable( + name: "Campaign"); + + migrationBuilder.DropSequence( + name: "campaign_hilo"); + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Migrations/MarketingContextModelSnapshot.cs b/src/Services/Marketing/Marketing.API/Migrations/MarketingContextModelSnapshot.cs new file mode 100644 index 000000000..3d954edcc --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Migrations/MarketingContextModelSnapshot.cs @@ -0,0 +1,107 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; + +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations +{ + [DbContext(typeof(MarketingContext))] + partial class MarketingContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.2") + .HasAnnotation("SqlServer:Sequence:.campaign_hilo", "'campaign_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "campaign_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Description") + .IsRequired() + .HasColumnName("Description"); + + b.Property("From") + .HasColumnName("From"); + + b.Property("To") + .HasColumnName("To"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("Campaign"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CampaignId"); + + b.Property("Description") + .IsRequired() + .HasColumnName("Description"); + + b.Property("RuleTypeId"); + + b.HasKey("Id"); + + b.HasIndex("CampaignId"); + + b.ToTable("Rules"); + + b.HasDiscriminator("RuleTypeId"); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.PurchaseHistoryRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + + b.ToTable("PurchaseHistoryRule"); + + b.HasDiscriminator().HasValue(2); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserLocationRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + b.Property("LocationId") + .HasColumnName("LocationId"); + + b.ToTable("UserLocationRule"); + + b.HasDiscriminator().HasValue(3); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.UserProfileRule", b => + { + b.HasBaseType("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule"); + + + b.ToTable("UserProfileRule"); + + b.HasDiscriminator().HasValue(1); + }); + + modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => + { + b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", "Campaign") + .WithMany("Rules") + .HasForeignKey("CampaignId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/Services/Marketing/Marketing.API/Model/Campaing.cs b/src/Services/Marketing/Marketing.API/Model/Campaing.cs index 62dca8483..bbe794b1a 100644 --- a/src/Services/Marketing/Marketing.API/Model/Campaing.cs +++ b/src/Services/Marketing/Marketing.API/Model/Campaing.cs @@ -1,7 +1,20 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model { - public class Campaing + using System; + using System.Collections.Generic; + + public class Campaign { - //TODO: Add Campaing properties + public int Id { get; set; } + + public string Description { get; set; } + + public DateTime From { get; set; } + + public DateTime To { get; set; } + + public string Url { get; set; } + + public ICollection Rules { get; set; } } -} +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Model/Location.cs b/src/Services/Marketing/Marketing.API/Model/Location.cs new file mode 100644 index 000000000..e3973f425 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Model/Location.cs @@ -0,0 +1,9 @@ +using System; +using System.Collections.Generic; +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model +{ + public class Location + { + public int Id { get; set; } + } +} diff --git a/src/Services/Marketing/Marketing.API/Model/Rule.cs b/src/Services/Marketing/Marketing.API/Model/Rule.cs new file mode 100644 index 000000000..38a47d3eb --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Model/Rule.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model +{ + public abstract class Rule + { + public int Id { get; set; } + + public int RuleTypeId { get; set; } + + public int CampaignId { get; set; } + + public Campaign Campaign { get; set; } + + public string Description { get; set; } + } + + + public class UserProfileRule : Rule + { } + + public class PurchaseHistoryRule : Rule + { } + + public class UserLocationRule : Rule + { + public int LocationId { get; set; } + } +} diff --git a/src/Services/Marketing/Marketing.API/Program.cs b/src/Services/Marketing/Marketing.API/Program.cs index cbda3e538..981e797c1 100644 --- a/src/Services/Marketing/Marketing.API/Program.cs +++ b/src/Services/Marketing/Marketing.API/Program.cs @@ -12,7 +12,6 @@ .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup() - .UseApplicationInsights() .Build(); host.Run(); diff --git a/src/Services/Marketing/Marketing.API/Properties/launchSettings.json b/src/Services/Marketing/Marketing.API/Properties/launchSettings.json index 4adb2bd6a..ec47d57ce 100644 --- a/src/Services/Marketing/Marketing.API/Properties/launchSettings.json +++ b/src/Services/Marketing/Marketing.API/Properties/launchSettings.json @@ -1,9 +1,9 @@ -{ +{ "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:52058/", + "applicationUrl": "http://localhost:5110", "sslPort": 0 } }, @@ -26,4 +26,4 @@ "applicationUrl": "http://localhost:52059" } } -} +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Startup.cs b/src/Services/Marketing/Marketing.API/Startup.cs index 91afead63..378ba214c 100644 --- a/src/Services/Marketing/Marketing.API/Startup.cs +++ b/src/Services/Marketing/Marketing.API/Startup.cs @@ -36,16 +36,6 @@ // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - //services.AddHealthChecks(checks => - //{ - // var minutes = 1; - // if (int.TryParse(Configuration["HealthCheck:Timeout"], out var minutesParsed)) - // { - // minutes = minutesParsed; - // } - // checks.AddSqlCheck("MarketingDb", Configuration["ConnectionString"], TimeSpan.FromMinutes(minutes)); - //}); - // Add framework services. services.AddMvc(); @@ -106,8 +96,8 @@ c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); }); - //MarketingContextSeed.SeedAsync(app, loggerFactory) - // .Wait(); + MarketingContextSeed.SeedAsync(app, loggerFactory) + .Wait(); } diff --git a/src/Services/Marketing/Marketing.API/appsettings.Development.json b/src/Services/Marketing/Marketing.API/appsettings.Development.json index fa8ce71a9..5fff67bac 100644 --- a/src/Services/Marketing/Marketing.API/appsettings.Development.json +++ b/src/Services/Marketing/Marketing.API/appsettings.Development.json @@ -2,9 +2,7 @@ "Logging": { "IncludeScopes": false, "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" + "Default": "Warning" } } } From f993c8c33dd7201678d417d0f513dfecc8a22ceb Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Thu, 1 Jun 2017 20:26:10 +0200 Subject: [PATCH 05/23] Remove unused class --- src/Services/Marketing/Marketing.API/Model/Location.cs | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/Services/Marketing/Marketing.API/Model/Location.cs diff --git a/src/Services/Marketing/Marketing.API/Model/Location.cs b/src/Services/Marketing/Marketing.API/Model/Location.cs deleted file mode 100644 index e3973f425..000000000 --- a/src/Services/Marketing/Marketing.API/Model/Location.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; -using System.Collections.Generic; -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model -{ - public class Location - { - public int Id { get; set; } - } -} From 83b229f4a1c274dedc92dd321d24dfa9ade4c9b2 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Thu, 1 Jun 2017 22:13:19 +0200 Subject: [PATCH 06/23] rename file name --- .../Marketing/Marketing.API/Model/{Campaing.cs => Campaign.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Services/Marketing/Marketing.API/Model/{Campaing.cs => Campaign.cs} (100%) diff --git a/src/Services/Marketing/Marketing.API/Model/Campaing.cs b/src/Services/Marketing/Marketing.API/Model/Campaign.cs similarity index 100% rename from src/Services/Marketing/Marketing.API/Model/Campaing.cs rename to src/Services/Marketing/Marketing.API/Model/Campaign.cs From 8494e776872cca3f66ab634fa5e93129484c1841 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Thu, 1 Jun 2017 22:19:07 +0200 Subject: [PATCH 07/23] add version 1 to Campaigns web api --- .../Marketing/Marketing.API/Controllers/CampaignsController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index b029bfa51..a70bc5823 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -6,7 +6,7 @@ using Microsoft.eShopOnContainers.Services.Marketing.API.Model; using Microsoft.EntityFrameworkCore; - [Route("api/[controller]")] + [Route("api/v1/[controller]")] public class CampaignsController : Controller { private readonly MarketingContext _context; From a5aea54cc8e58fb13fb193f08529d7a4d2a92764 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:30:39 +0200 Subject: [PATCH 08/23] Add dtos --- .../Controllers/CampaignsController.cs | 120 ++++++++++++++++-- .../Marketing.API/Dto/CampaignDTO.cs | 25 ++++ .../Marketing/Marketing.API/Dto/RuleDTO.cs | 19 +++ 3 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs create mode 100644 src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index b029bfa51..6259b69ca 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -5,8 +5,11 @@ using System.Threading.Tasks; using Microsoft.eShopOnContainers.Services.Marketing.API.Model; using Microsoft.EntityFrameworkCore; + using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; + using System.Collections.Generic; - [Route("api/[controller]")] + [Route("api/v1/[controller]")] + //[Authorize] public class CampaignsController : Controller { private readonly MarketingContext _context; @@ -16,6 +19,7 @@ _context = context; } + [HttpGet] public async Task GetAllCampaigns() { @@ -23,7 +27,9 @@ .Include(c => c.Rules) .ToListAsync(); - return Ok(campaignList); + var campaignDtoList = CampaignModelListToDtoList(campaignList); + + return Ok(campaignDtoList); } [HttpGet("{id:int}")] @@ -31,30 +37,44 @@ { var campaign = await _context.Campaigns .Include(c => c.Rules) - .SingleAsync(c => c.Id == id); + .SingleOrDefaultAsync(c => c.Id == id); if (campaign is null) { return NotFound(); } - return Ok(campaign); + var campaignDto = CampaignModelToDto(campaign); + + return Ok(campaignDto); } [HttpPost] - public async Task CreateCampaign([FromBody] Campaign campaign) + public async Task CreateCampaign([FromBody] CampaignDTO campaign) { - await _context.Campaigns.AddAsync(campaign); + if (campaign is null) + { + return BadRequest(); + } + + var campaingToCreate = CampaignDtoToModel(campaign); + + await _context.Campaigns.AddAsync(campaingToCreate); await _context.SaveChangesAsync(); - return CreatedAtAction(nameof(GetCampaignById), new { id = campaign.Id }, null); + return CreatedAtAction(nameof(GetCampaignById), new { id = campaingToCreate.Id }, null); } [HttpPut("{id:int}")] - public async Task UpdateCampaign(int id, [FromBody]Campaign campaign) + public async Task UpdateCampaign(int id, [FromBody]CampaignDTO campaign) { + if (id < 1 || campaign is null) + { + return BadRequest(); + } + var campaignToUpdate = await _context.Campaigns.FindAsync(id); - if (campaign is null) + if (campaignToUpdate is null) { return NotFound(); } @@ -71,16 +91,92 @@ [HttpDelete("{id:int}")] public async Task Delete(int id) { - var campaign = await _context.Campaigns.FindAsync(id); - if (campaign is null) + if (id < 1) + { + return BadRequest(); + } + + var campaignToDelete = await _context.Campaigns.FindAsync(id); + if (campaignToDelete is null) { return NotFound(); } - _context.Campaigns.Remove(campaign); + _context.Campaigns.Remove(campaignToDelete); await _context.SaveChangesAsync(); return NoContent(); } + + + + private List CampaignModelListToDtoList(List campaignList) + { + var campaignDtoList = new List(); + + campaignList.ForEach(campaign => campaignDtoList + .Add(CampaignModelToDto(campaign))); + + return campaignDtoList; + } + + private CampaignDTO CampaignModelToDto(Campaign campaign) + { + var campaignDto = new CampaignDTO + { + Description = campaign.Description, + From = campaign.From, + To = campaign.To, + Url = campaign.Url, + }; + + campaign.Rules.ForEach(c => + { + switch ((RuleTypeEnum)c.RuleTypeId) + { + case RuleTypeEnum.UserLocationRule: + var userLocationRule = c as UserLocationRule; + campaignDto.Rules.Add(new RuleDTO + { + LocationId = userLocationRule.LocationId, + RuleTypeId = userLocationRule.RuleTypeId, + Description = userLocationRule.Description + }); + break; + } + }); + + return campaignDto; + } + + private Campaign CampaignDtoToModel(CampaignDTO campaignDto) + { + var campaingModel = new Campaign + { + Description = campaignDto.Description, + From = campaignDto.From, + To = campaignDto.To, + Url = campaignDto.Url + }; + + campaignDto.Rules.ForEach(c => + { + switch (c.RuleType) + { + case RuleTypeEnum.UserLocationRule: + campaingModel.Rules.Add(new UserLocationRule + { + LocationId = c.LocationId.Value, + RuleTypeId = (int)c.RuleType, + Description = c.Description, + Campaign = campaingModel + }); + break; + } + }); + + return campaingModel; + } + } } \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs b/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs new file mode 100644 index 000000000..b5676db63 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs @@ -0,0 +1,25 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto +{ + using System; + using System.Collections.Generic; + + public class CampaignDTO + { + public int Id { get; set; } + + public string Description { get; set; } + + public DateTime From { get; set; } + + public DateTime To { get; set; } + + public string Url { get; set; } + + public List Rules { get; set; } + + public CampaignDTO() + { + Rules = new List(); + } + } +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs b/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs new file mode 100644 index 000000000..6a1d9aef6 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs @@ -0,0 +1,19 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto +{ + public class RuleDTO + { + public int Id { get; set; } + + public RuleTypeEnum RuleType => (RuleTypeEnum) RuleTypeId; + + public int RuleTypeId { get; set; } + + public int CampaignId { get; set; } + + public int? LocationId { get; set; } + + public string Description { get; set; } + } + + public enum RuleTypeEnum { UserProfileRule = 1, PurchaseHistoryRule = 2, UserLocationRule = 3 } +} \ No newline at end of file From 1bd13747f47d7ed375b636fbe01d14f4b9f6775a Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:31:03 +0200 Subject: [PATCH 09/23] Add exceptions filters --- .../InternalServerErrorObjectResult.cs | 14 ++++ .../Exceptions/MarketingDomainException.cs | 21 ++++++ .../Filters/HttpGlobalExceptionFilter.cs | 67 +++++++++++++++++++ .../Marketing/Marketing.API/Startup.cs | 6 +- 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/Services/Marketing/Marketing.API/Infrastructure/ActionResult/InternalServerErrorObjectResult.cs create mode 100644 src/Services/Marketing/Marketing.API/Infrastructure/Exceptions/MarketingDomainException.cs create mode 100644 src/Services/Marketing/Marketing.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/ActionResult/InternalServerErrorObjectResult.cs b/src/Services/Marketing/Marketing.API/Infrastructure/ActionResult/InternalServerErrorObjectResult.cs new file mode 100644 index 000000000..5975d92af --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Infrastructure/ActionResult/InternalServerErrorObjectResult.cs @@ -0,0 +1,14 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.ActionResults +{ + using AspNetCore.Http; + using Microsoft.AspNetCore.Mvc; + + public class InternalServerErrorObjectResult : ObjectResult + { + public InternalServerErrorObjectResult(object error) + : base(error) + { + StatusCode = StatusCodes.Status500InternalServerError; + } + } +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/Exceptions/MarketingDomainException.cs b/src/Services/Marketing/Marketing.API/Infrastructure/Exceptions/MarketingDomainException.cs new file mode 100644 index 000000000..68ccd9e52 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Infrastructure/Exceptions/MarketingDomainException.cs @@ -0,0 +1,21 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions +{ + using System; + + /// + /// Exception type for app exceptions + /// + public class MarketingDomainException : Exception + { + public MarketingDomainException() + { } + + public MarketingDomainException(string message) + : base(message) + { } + + public MarketingDomainException(string message, Exception innerException) + : base(message, innerException) + { } + } +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs b/src/Services/Marketing/Marketing.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs new file mode 100644 index 000000000..14e79775d --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Infrastructure/Filters/HttpGlobalExceptionFilter.cs @@ -0,0 +1,67 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Filters +{ + using AspNetCore.Mvc; + using global::Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Mvc.Filters; + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.ActionResults; + using Microsoft.Extensions.Logging; + using System.Net; + + public class HttpGlobalExceptionFilter : IExceptionFilter + { + private readonly IHostingEnvironment env; + private readonly ILogger logger; + + public HttpGlobalExceptionFilter(IHostingEnvironment env, ILogger logger) + { + this.env = env; + this.logger = logger; + } + + public void OnException(ExceptionContext context) + { + logger.LogError(new EventId(context.Exception.HResult), + context.Exception, + context.Exception.Message); + + if (context.Exception.GetType() == typeof(MarketingDomainException)) + { + var json = new JsonErrorResponse + { + Messages = new[] { context.Exception.Message } + }; + + // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 + //It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information + context.Result = new BadRequestObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest; + } + else + { + var json = new JsonErrorResponse + { + Messages = new[] { "An error occur.Try it again." } + }; + + if (env.IsDevelopment()) + { + json.DeveloperMessage = context.Exception; + } + + // Result asigned to a result object but in destiny the response is empty. This is a known bug of .net core 1.1 + // It will be fixed in .net core 1.1.2. See https://github.com/aspnet/Mvc/issues/5594 for more information + context.Result = new InternalServerErrorObjectResult(json); + context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + } + context.ExceptionHandled = true; + } + + private class JsonErrorResponse + { + public string[] Messages { get; set; } + + public object DeveloperMessage { get; set; } + } + } +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Startup.cs b/src/Services/Marketing/Marketing.API/Startup.cs index 378ba214c..9609f903f 100644 --- a/src/Services/Marketing/Marketing.API/Startup.cs +++ b/src/Services/Marketing/Marketing.API/Startup.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging; using System.Reflection; using System; + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Filters; public class Startup { @@ -37,7 +38,10 @@ public void ConfigureServices(IServiceCollection services) { // Add framework services. - services.AddMvc(); + services.AddMvc(options => + { + options.Filters.Add(typeof(HttpGlobalExceptionFilter)); + }).AddControllersAsServices(); //Injecting Controllers themselves thru DIFor further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services services.AddDbContext(options => { From c132d944d7bed527b06adc2069b5a2a1eb0b662d Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:31:35 +0200 Subject: [PATCH 10/23] update inital migration --- .../20170602122539_Initial.Designer.cs} | 13 ++++++---- .../20170602122539_Initial.cs} | 25 +++++++++++-------- .../MarketingContextModelSnapshot.cs | 11 +++++--- 3 files changed, 30 insertions(+), 19 deletions(-) rename src/Services/Marketing/Marketing.API/{Migrations/20170601175200_Initial.Designer.cs => Infrastructure/MarketingMigrations/20170602122539_Initial.Designer.cs} (86%) rename src/Services/Marketing/Marketing.API/{Migrations/20170601175200_Initial.cs => Infrastructure/MarketingMigrations/20170602122539_Initial.cs} (77%) rename src/Services/Marketing/Marketing.API/{Migrations => Infrastructure/MarketingMigrations}/MarketingContextModelSnapshot.cs (87%) diff --git a/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.Designer.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170602122539_Initial.Designer.cs similarity index 86% rename from src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.Designer.cs rename to src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170602122539_Initial.Designer.cs index b0305f171..5a5a6b66d 100644 --- a/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.Designer.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170602122539_Initial.Designer.cs @@ -5,10 +5,10 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations { [DbContext(typeof(MarketingContext))] - [Migration("20170601175200_Initial")] + [Migration("20170602122539_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -16,6 +16,7 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations modelBuilder .HasAnnotation("ProductVersion", "1.1.2") .HasAnnotation("SqlServer:Sequence:.campaign_hilo", "'campaign_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:Sequence:.rule_hilo", "'rule_hilo', '', '1', '10', '', '', 'Int64', 'False'") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", b => @@ -45,7 +46,9 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "rule_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); b.Property("CampaignId"); @@ -59,7 +62,7 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations b.HasIndex("CampaignId"); - b.ToTable("Rules"); + b.ToTable("Rule"); b.HasDiscriminator("RuleTypeId"); }); @@ -98,7 +101,7 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => { - b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", "Campaign") + b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign") .WithMany("Rules") .HasForeignKey("CampaignId") .OnDelete(DeleteBehavior.Cascade); diff --git a/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170602122539_Initial.cs similarity index 77% rename from src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.cs rename to src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170602122539_Initial.cs index dee3aa12f..d33fbb3d0 100644 --- a/src/Services/Marketing/Marketing.API/Migrations/20170601175200_Initial.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/20170602122539_Initial.cs @@ -1,9 +1,8 @@ using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Metadata; -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations { public partial class Initial : Migration { @@ -13,6 +12,10 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations name: "campaign_hilo", incrementBy: 10); + migrationBuilder.CreateSequence( + name: "rule_hilo", + incrementBy: 10); + migrationBuilder.CreateTable( name: "Campaign", columns: table => new @@ -29,11 +32,10 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations }); migrationBuilder.CreateTable( - name: "Rules", + name: "Rule", columns: table => new { - Id = table.Column(nullable: false) - .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), + Id = table.Column(nullable: false), CampaignId = table.Column(nullable: false), Description = table.Column(nullable: false), RuleTypeId = table.Column(nullable: false), @@ -41,9 +43,9 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations }, constraints: table => { - table.PrimaryKey("PK_Rules", x => x.Id); + table.PrimaryKey("PK_Rule", x => x.Id); table.ForeignKey( - name: "FK_Rules_Campaign_CampaignId", + name: "FK_Rule_Campaign_CampaignId", column: x => x.CampaignId, principalTable: "Campaign", principalColumn: "Id", @@ -51,21 +53,24 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations }); migrationBuilder.CreateIndex( - name: "IX_Rules_CampaignId", - table: "Rules", + name: "IX_Rule_CampaignId", + table: "Rule", column: "CampaignId"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "Rules"); + name: "Rule"); migrationBuilder.DropTable( name: "Campaign"); migrationBuilder.DropSequence( name: "campaign_hilo"); + + migrationBuilder.DropSequence( + name: "rule_hilo"); } } } diff --git a/src/Services/Marketing/Marketing.API/Migrations/MarketingContextModelSnapshot.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/MarketingContextModelSnapshot.cs similarity index 87% rename from src/Services/Marketing/Marketing.API/Migrations/MarketingContextModelSnapshot.cs rename to src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/MarketingContextModelSnapshot.cs index 3d954edcc..865daa028 100644 --- a/src/Services/Marketing/Marketing.API/Migrations/MarketingContextModelSnapshot.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingMigrations/MarketingContextModelSnapshot.cs @@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations { [DbContext(typeof(MarketingContext))] partial class MarketingContextModelSnapshot : ModelSnapshot @@ -15,6 +15,7 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations modelBuilder .HasAnnotation("ProductVersion", "1.1.2") .HasAnnotation("SqlServer:Sequence:.campaign_hilo", "'campaign_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:Sequence:.rule_hilo", "'rule_hilo', '', '1', '10', '', '', 'Int64', 'False'") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", b => @@ -44,7 +45,9 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => { b.Property("Id") - .ValueGeneratedOnAdd(); + .ValueGeneratedOnAdd() + .HasAnnotation("SqlServer:HiLoSequenceName", "rule_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); b.Property("CampaignId"); @@ -58,7 +61,7 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations b.HasIndex("CampaignId"); - b.ToTable("Rules"); + b.ToTable("Rule"); b.HasDiscriminator("RuleTypeId"); }); @@ -97,7 +100,7 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Migrations modelBuilder.Entity("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Rule", b => { - b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign", "Campaign") + b.HasOne("Microsoft.eShopOnContainers.Services.Marketing.API.Model.Campaign") .WithMany("Rules") .HasForeignKey("CampaignId") .OnDelete(DeleteBehavior.Cascade); From 3440427da30426a7f669b504781cf204ee2f3626 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:31:48 +0200 Subject: [PATCH 11/23] modify context --- .../Marketing.API/Infrastructure/MarketingContext.cs | 12 ++++++++---- .../Infrastructure/MarketingContextSeed.cs | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs index 798d8d152..af6a75cf1 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs @@ -28,9 +28,9 @@ { builder.ToTable("Campaign"); - builder.HasKey(ci => ci.Id); + builder.HasKey(m => m.Id); - builder.Property(ci => ci.Id) + builder.Property(m => m.Id) .ForSqlServerUseSequenceHiLo("campaign_hilo") .IsRequired(); @@ -52,16 +52,20 @@ builder.HasMany(m => m.Rules) .WithOne(r => r.Campaign) - .HasForeignKey(m => m.CampaignId) + .HasForeignKey(r => r.CampaignId) .IsRequired(); } void ConfigureRules(EntityTypeBuilder builder) { - builder.ToTable("Rules"); + builder.ToTable("Rule"); builder.HasKey(r => r.Id); + builder.Property(r => r.Id) + .ForSqlServerUseSequenceHiLo("rule_hilo") + .IsRequired(); + builder.HasDiscriminator("RuleTypeId") .HasValue(1) .HasValue(2) diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs index 9d2bb0157..5f15a725a 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContextSeed.cs @@ -57,7 +57,7 @@ new UserLocationRule { Description = "UserLocationRule2", - LocationId = 1 + LocationId = 3 } } } From 4c59c5ebe4e095cd0221f61c0586d64f1a27d8f8 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:32:24 +0200 Subject: [PATCH 12/23] Add migration folder --- src/Services/Marketing/Marketing.API/Marketing.API.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Services/Marketing/Marketing.API/Marketing.API.csproj b/src/Services/Marketing/Marketing.API/Marketing.API.csproj index f65be06bb..bd8e5a871 100644 --- a/src/Services/Marketing/Marketing.API/Marketing.API.csproj +++ b/src/Services/Marketing/Marketing.API/Marketing.API.csproj @@ -11,6 +11,7 @@ + From 6dcc6f84bca48bb8bf52d681893b600b93106d7c Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:32:50 +0200 Subject: [PATCH 13/23] initialitze the list in constructor --- src/Services/Marketing/Marketing.API/Model/Campaing.cs | 8 +++++++- src/Services/Marketing/Marketing.API/Model/Rule.cs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Services/Marketing/Marketing.API/Model/Campaing.cs b/src/Services/Marketing/Marketing.API/Model/Campaing.cs index bbe794b1a..c628b4a72 100644 --- a/src/Services/Marketing/Marketing.API/Model/Campaing.cs +++ b/src/Services/Marketing/Marketing.API/Model/Campaing.cs @@ -15,6 +15,12 @@ public string Url { get; set; } - public ICollection Rules { get; set; } + public List Rules { get; set; } + + + public Campaign() + { + Rules = new List(); + } } } \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Model/Rule.cs b/src/Services/Marketing/Marketing.API/Model/Rule.cs index 38a47d3eb..936e00c36 100644 --- a/src/Services/Marketing/Marketing.API/Model/Rule.cs +++ b/src/Services/Marketing/Marketing.API/Model/Rule.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model { @@ -26,4 +26,4 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model { public int LocationId { get; set; } } -} +} \ No newline at end of file From 3e47c840d5bf4966548d7b63f40ee59d981a99d6 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:33:01 +0200 Subject: [PATCH 14/23] Add integration test --- .../IntegrationTests/IntegrationTests.csproj | 4 + .../Marketing/MarketingScenarioBase.cs | 45 ++++++ .../Services/Marketing/MarketingScenarios.cs | 132 ++++++++++++++++++ .../Marketing/MarketingTestsStartup.cs | 26 ++++ .../Services/Marketing/appsettings.json | 5 + 5 files changed, 212 insertions(+) create mode 100644 test/Services/IntegrationTests/Services/Marketing/MarketingScenarioBase.cs create mode 100644 test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs create mode 100644 test/Services/IntegrationTests/Services/Marketing/MarketingTestsStartup.cs create mode 100644 test/Services/IntegrationTests/Services/Marketing/appsettings.json diff --git a/test/Services/IntegrationTests/IntegrationTests.csproj b/test/Services/IntegrationTests/IntegrationTests.csproj index 60911dd4d..a44e57ccb 100644 --- a/test/Services/IntegrationTests/IntegrationTests.csproj +++ b/test/Services/IntegrationTests/IntegrationTests.csproj @@ -20,6 +20,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -28,6 +31,7 @@ + diff --git a/test/Services/IntegrationTests/Services/Marketing/MarketingScenarioBase.cs b/test/Services/IntegrationTests/Services/Marketing/MarketingScenarioBase.cs new file mode 100644 index 000000000..01383410b --- /dev/null +++ b/test/Services/IntegrationTests/Services/Marketing/MarketingScenarioBase.cs @@ -0,0 +1,45 @@ +namespace IntegrationTests.Services.Marketing +{ + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.TestHost; + using System.IO; + + public class MarketingScenarioBase + { + private const string _campaignsUrlBase = "api/v1/campaigns"; + + public TestServer CreateServer() + { + var webHostBuilder = new WebHostBuilder(); + webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory() + "\\Services\\Marketing"); + webHostBuilder.UseStartup(); + + return new TestServer(webHostBuilder); + } + + public static class Get + { + public static string Campaigns = _campaignsUrlBase; + + public static string CampaignBy(int id) + => $"{_campaignsUrlBase}/{id}"; + } + + public static class Post + { + public static string AddNewCampaign = _campaignsUrlBase; + } + + public static class Put + { + public static string CampaignBy(int id) + => $"{_campaignsUrlBase}/{id}"; + } + + public static class Delete + { + public static string CampaignBy(int id) + => $"{_campaignsUrlBase}/{id}"; + } + } +} \ No newline at end of file diff --git a/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs b/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs new file mode 100644 index 000000000..931975fb8 --- /dev/null +++ b/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs @@ -0,0 +1,132 @@ +namespace IntegrationTests.Services.Marketing +{ + using System.Net.Http; + using System.Text; + using System.Threading.Tasks; + using Xunit; + using System; + using Newtonsoft.Json; + using Microsoft.eShopOnContainers.Services.Marketing.API.Model; + using System.Collections.Generic; + using System.Net; + using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; + + public class MarketingScenarios + : MarketingScenarioBase + { + [Fact] + public async Task Get_get_all_campaigns_and_response_ok_status_code() + { + using (var server = CreateServer()) + { + var response = await server.CreateClient() + .GetAsync(Get.Campaigns); + + response.EnsureSuccessStatusCode(); + } + } + + [Fact] + public async Task Get_get_campaign_by_id_and_response_ok_status_code() + { + using (var server = CreateServer()) + { + var response = await server.CreateClient() + .GetAsync(Get.CampaignBy(1)); + + response.EnsureSuccessStatusCode(); + } + } + + [Fact] + public async Task Get_get_campaign_by_id_and_response_not_found_status_code() + { + using (var server = CreateServer()) + { + var response = await server.CreateClient() + .GetAsync(Get.CampaignBy(9999999)); + + Assert.True(response.StatusCode == HttpStatusCode.NotFound); + } + } + + [Fact] + public async Task Post_add_new_campaign_and_response_ok_status_code() + { + using (var server = CreateServer()) + { + var content = new StringContent(JsonConvert.SerializeObject(FakeCampaignDto), Encoding.UTF8, "application/json"); + var response = await server.CreateClient() + .PostAsync(Post.AddNewCampaign, content); + + response.EnsureSuccessStatusCode(); + } + } + + [Fact] + public async Task Delete_delete_campaign_and_response_not_content_status_code() + { + using (var server = CreateServer()) + { + var content = new StringContent(JsonConvert.SerializeObject(FakeCampaignDto), Encoding.UTF8, "application/json"); + + //add campaign + var campaignResponse = await server.CreateClient() + .PostAsync(Post.AddNewCampaign, content); + + if (int.TryParse(campaignResponse.Headers.Location.Segments[4], out int id)) + { + var response = await server.CreateClient() + .DeleteAsync(Delete.CampaignBy(id)); + + Assert.True(response.StatusCode == HttpStatusCode.NoContent); + } + + campaignResponse.EnsureSuccessStatusCode(); + } + } + + [Fact] + public async Task Put_update_campaign_and_response_not_content_status_code() + { + using (var server = CreateServer()) + { + var content = new StringContent(JsonConvert.SerializeObject(FakeCampaignDto), Encoding.UTF8, "application/json"); + + //add campaign + var campaignResponse = await server.CreateClient() + .PostAsync(Post.AddNewCampaign, content); + + if (int.TryParse(campaignResponse.Headers.Location.Segments[4], out int id)) + { + FakeCampaignDto.Description = "FakeCampaignUpdatedDescription"; + content = new StringContent(JsonConvert.SerializeObject(FakeCampaignDto), Encoding.UTF8, "application/json"); + var response = await server.CreateClient() + .PutAsync(Put.CampaignBy(id), content); + + Assert.True(response.StatusCode == HttpStatusCode.Created); + } + + campaignResponse.EnsureSuccessStatusCode(); + } + } + + + private static CampaignDTO FakeCampaignDto = new CampaignDTO + { + Description = "FakeCampaignDescription", + From = DateTime.Now, + To = DateTime.Now.AddDays(7), + Url = "http://CampaignUrl.test/fdaf91ad0cef5419719f50198", + Rules = new List + { + new RuleDTO + { + LocationId = 1, + Description = "testDescription", + RuleTypeId = 3, + } + } + }; + } +} diff --git a/test/Services/IntegrationTests/Services/Marketing/MarketingTestsStartup.cs b/test/Services/IntegrationTests/Services/Marketing/MarketingTestsStartup.cs new file mode 100644 index 000000000..b8d337ab2 --- /dev/null +++ b/test/Services/IntegrationTests/Services/Marketing/MarketingTestsStartup.cs @@ -0,0 +1,26 @@ +namespace IntegrationTests.Services.Marketing +{ + using Microsoft.eShopOnContainers.Services.Marketing.API; + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.Builder; + using IntegrationTests.Middleware; + + public class MarketingTestsStartup : Startup + { + public MarketingTestsStartup(IHostingEnvironment env) : base(env) + { + } + + protected override void ConfigureAuth(IApplicationBuilder app) + { + if (Configuration["isTest"] == bool.TrueString.ToLowerInvariant()) + { + app.UseMiddleware(); + } + else + { + base.ConfigureAuth(app); + } + } + } +} diff --git a/test/Services/IntegrationTests/Services/Marketing/appsettings.json b/test/Services/IntegrationTests/Services/Marketing/appsettings.json new file mode 100644 index 000000000..8e3e07891 --- /dev/null +++ b/test/Services/IntegrationTests/Services/Marketing/appsettings.json @@ -0,0 +1,5 @@ +{ + "ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.MarketingDb;User Id=sa;Password=Pass@word", + "IdentityUrl": "http://localhost:5105", + "isTest": "true" +} From 82182e3184dd5dd4df4b117151c042d2bdd15b24 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:35:21 +0200 Subject: [PATCH 15/23] cleanup --- src/Services/Marketing/Marketing.API/Model/Rule.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Services/Marketing/Marketing.API/Model/Rule.cs b/src/Services/Marketing/Marketing.API/Model/Rule.cs index 936e00c36..03686bf86 100644 --- a/src/Services/Marketing/Marketing.API/Model/Rule.cs +++ b/src/Services/Marketing/Marketing.API/Model/Rule.cs @@ -1,6 +1,4 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model { public abstract class Rule { From 385f1f094affa720a708f11a7b67130f8ad760e1 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Fri, 2 Jun 2017 16:36:16 +0200 Subject: [PATCH 16/23] fix error merge --- .../Marketing/Marketing.API/Controllers/CampaignsController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index 04f0d7ba9..1786bc3b7 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; using System.Collections.Generic; + using Microsoft.AspNetCore.Authorization; [Route("api/v1/[controller]")] [Authorize] From a8178fbbb4aa33c801455453d39ea66d7233ef26 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Sat, 3 Jun 2017 00:59:58 +0200 Subject: [PATCH 17/23] remove unused dbset --- .../Marketing/Marketing.API/Infrastructure/MarketingContext.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs index af6a75cf1..2fb45868b 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs @@ -13,9 +13,6 @@ public DbSet Campaigns { get; set; } public DbSet Rules { get; set; } - public DbSet UserProfileRules { get; set; } - public DbSet PurchaseHistoryRules { get; set; } - public DbSet UserLocationRules { get; set; } protected override void OnModelCreating(ModelBuilder builder) { From b5f90cf7b4abf2f9c3c02bfeece6bfb3a4c0c5e2 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Sat, 3 Jun 2017 01:00:55 +0200 Subject: [PATCH 18/23] Create RuleType class to manage RuleTypeEnum --- .../Controllers/CampaignsController.cs | 23 ++++++++++--------- .../Marketing/Marketing.API/Dto/RuleDTO.cs | 11 +++++---- .../Marketing.API/Dto/RuleTypeEnum.cs | 20 ++++++++++++++++ 3 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 src/Services/Marketing/Marketing.API/Dto/RuleTypeEnum.cs diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index 1786bc3b7..41773a78c 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -8,9 +8,10 @@ using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; using System.Collections.Generic; using Microsoft.AspNetCore.Authorization; + using System.Linq; [Route("api/v1/[controller]")] - [Authorize] + //[Authorize] public class CampaignsController : Controller { private readonly MarketingContext _context; @@ -27,7 +28,7 @@ .Include(c => c.Rules) .ToListAsync(); - var campaignDtoList = CampaignModelListToDtoList(campaignList); + var campaignDtoList = MapCampaignModelListToDtoList(campaignList); return Ok(campaignDtoList); } @@ -44,7 +45,7 @@ return NotFound(); } - var campaignDto = CampaignModelToDto(campaign); + var campaignDto = MapCampaignModelToDto(campaign); return Ok(campaignDto); } @@ -57,7 +58,7 @@ return BadRequest(); } - var campaingToCreate = CampaignDtoToModel(campaign); + var campaingToCreate = MapCampaignDtoToModel(campaign); await _context.Campaigns.AddAsync(campaingToCreate); await _context.SaveChangesAsync(); @@ -110,17 +111,17 @@ - private List CampaignModelListToDtoList(List campaignList) + private List MapCampaignModelListToDtoList(List campaignList) { var campaignDtoList = new List(); campaignList.ForEach(campaign => campaignDtoList - .Add(CampaignModelToDto(campaign))); + .Add(MapCampaignModelToDto(campaign))); return campaignDtoList; } - private CampaignDTO CampaignModelToDto(Campaign campaign) + private CampaignDTO MapCampaignModelToDto(Campaign campaign) { var campaignDto = new CampaignDTO { @@ -132,7 +133,7 @@ campaign.Rules.ForEach(c => { - switch ((RuleTypeEnum)c.RuleTypeId) + switch (RuleType.From(c.RuleTypeId)) { case RuleTypeEnum.UserLocationRule: var userLocationRule = c as UserLocationRule; @@ -149,7 +150,7 @@ return campaignDto; } - private Campaign CampaignDtoToModel(CampaignDTO campaignDto) + private Campaign MapCampaignDtoToModel(CampaignDTO campaignDto) { var campaingModel = new Campaign { @@ -161,13 +162,13 @@ campaignDto.Rules.ForEach(c => { - switch (c.RuleType) + switch (RuleType.From(c.RuleTypeId)) { case RuleTypeEnum.UserLocationRule: campaingModel.Rules.Add(new UserLocationRule { LocationId = c.LocationId.Value, - RuleTypeId = (int)c.RuleType, + RuleTypeId = c.RuleTypeId, Description = c.Description, Campaign = campaingModel }); diff --git a/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs b/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs index 6a1d9aef6..a79756e9c 100644 --- a/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs +++ b/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs @@ -1,11 +1,14 @@ -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto +using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto { public class RuleDTO { public int Id { get; set; } - public RuleTypeEnum RuleType => (RuleTypeEnum) RuleTypeId; - public int RuleTypeId { get; set; } public int CampaignId { get; set; } @@ -14,6 +17,4 @@ public string Description { get; set; } } - - public enum RuleTypeEnum { UserProfileRule = 1, PurchaseHistoryRule = 2, UserLocationRule = 3 } } \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dto/RuleTypeEnum.cs b/src/Services/Marketing/Marketing.API/Dto/RuleTypeEnum.cs new file mode 100644 index 000000000..f00b7fbf9 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Dto/RuleTypeEnum.cs @@ -0,0 +1,20 @@ +using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions; +using System; + +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto +{ + public enum RuleTypeEnum { UserProfileRule = 1, PurchaseHistoryRule = 2, UserLocationRule = 3 } + + public static class RuleType + { + public static RuleTypeEnum From(int id) + { + if (!Enum.IsDefined(typeof(RuleTypeEnum), id)) + { + throw new MarketingDomainException($"Invalid value for RuleType, RuleTypeId: {id}"); + } + + return (RuleTypeEnum)id; + } + } +} \ No newline at end of file From ea8d893d4ecf61bea238ff62db81c5a43cf8e443 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Mon, 5 Jun 2017 19:36:09 +0200 Subject: [PATCH 19/23] change the FakeCampaignDto private object to an GetFakeCampaignDto funtion --- .../Controllers/CampaignsController.cs | 3 +- .../Services/Marketing/MarketingScenarios.cs | 41 +++++++++++-------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index 41773a78c..a525adec5 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -11,7 +11,7 @@ using System.Linq; [Route("api/v1/[controller]")] - //[Authorize] + [Authorize] public class CampaignsController : Controller { private readonly MarketingContext _context; @@ -178,6 +178,5 @@ return campaingModel; } - } } \ No newline at end of file diff --git a/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs b/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs index 931975fb8..29dcbebda 100644 --- a/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs +++ b/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs @@ -55,7 +55,8 @@ { using (var server = CreateServer()) { - var content = new StringContent(JsonConvert.SerializeObject(FakeCampaignDto), Encoding.UTF8, "application/json"); + var fakeCampaignDto = GetFakeCampaignDto(); + var content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); var response = await server.CreateClient() .PostAsync(Post.AddNewCampaign, content); @@ -68,7 +69,8 @@ { using (var server = CreateServer()) { - var content = new StringContent(JsonConvert.SerializeObject(FakeCampaignDto), Encoding.UTF8, "application/json"); + var fakeCampaignDto = GetFakeCampaignDto(); + var content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); //add campaign var campaignResponse = await server.CreateClient() @@ -91,7 +93,8 @@ { using (var server = CreateServer()) { - var content = new StringContent(JsonConvert.SerializeObject(FakeCampaignDto), Encoding.UTF8, "application/json"); + var fakeCampaignDto = GetFakeCampaignDto(); + var content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); //add campaign var campaignResponse = await server.CreateClient() @@ -99,8 +102,8 @@ if (int.TryParse(campaignResponse.Headers.Location.Segments[4], out int id)) { - FakeCampaignDto.Description = "FakeCampaignUpdatedDescription"; - content = new StringContent(JsonConvert.SerializeObject(FakeCampaignDto), Encoding.UTF8, "application/json"); + fakeCampaignDto.Description = "FakeCampaignUpdatedDescription"; + content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); var response = await server.CreateClient() .PutAsync(Put.CampaignBy(id), content); @@ -111,22 +114,24 @@ } } - - private static CampaignDTO FakeCampaignDto = new CampaignDTO + private static CampaignDTO GetFakeCampaignDto() { - Description = "FakeCampaignDescription", - From = DateTime.Now, - To = DateTime.Now.AddDays(7), - Url = "http://CampaignUrl.test/fdaf91ad0cef5419719f50198", - Rules = new List + return new CampaignDTO() { - new RuleDTO + Description = "FakeCampaignDescription", + From = DateTime.Now, + To = DateTime.Now.AddDays(7), + Url = "http://CampaignUrl.test/fdaf91ad0cef5419719f50198", + Rules = new List { - LocationId = 1, - Description = "testDescription", - RuleTypeId = 3, + new RuleDTO + { + LocationId = 1, + Description = "testDescription", + RuleTypeId = 3, + } } - } - }; + }; + } } } From 3c811156031676d6f43efda25ed3fbf4cbeb8096 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Mon, 5 Jun 2017 23:20:46 +0200 Subject: [PATCH 20/23] Edit mapper methods --- .../Controllers/CampaignsController.cs | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index a525adec5..ca8e93d8c 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -125,26 +125,32 @@ { var campaignDto = new CampaignDTO { + Id = campaign.Id, Description = campaign.Description, From = campaign.From, To = campaign.To, Url = campaign.Url, }; - campaign.Rules.ForEach(c => + campaign.Rules.ForEach(rule => { - switch (RuleType.From(c.RuleTypeId)) + var ruleDto = new RuleDTO + { + Id = rule.Id, + RuleTypeId = rule.RuleTypeId, + Description = rule.Description, + CampaignId = rule.CampaignId + }; + + switch (RuleType.From(rule.RuleTypeId)) { case RuleTypeEnum.UserLocationRule: - var userLocationRule = c as UserLocationRule; - campaignDto.Rules.Add(new RuleDTO - { - LocationId = userLocationRule.LocationId, - RuleTypeId = userLocationRule.RuleTypeId, - Description = userLocationRule.Description - }); + var userLocationRule = rule as UserLocationRule; + ruleDto.LocationId = userLocationRule.LocationId; break; } + + campaignDto.Rules.Add(ruleDto); }); return campaignDto; @@ -154,22 +160,24 @@ { var campaingModel = new Campaign { + Id = campaignDto.Id, Description = campaignDto.Description, From = campaignDto.From, To = campaignDto.To, Url = campaignDto.Url }; - campaignDto.Rules.ForEach(c => + campaignDto.Rules.ForEach(ruleDto => { - switch (RuleType.From(c.RuleTypeId)) + switch (RuleType.From(ruleDto.RuleTypeId)) { case RuleTypeEnum.UserLocationRule: campaingModel.Rules.Add(new UserLocationRule { - LocationId = c.LocationId.Value, - RuleTypeId = c.RuleTypeId, - Description = c.Description, + Id = ruleDto.Id, + LocationId = ruleDto.LocationId.Value, + RuleTypeId = ruleDto.RuleTypeId, + Description = ruleDto.Description, Campaign = campaingModel }); break; From 56c07aaafac8d15f633a781bcc18095f2f6a9377 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Tue, 6 Jun 2017 10:26:11 +0200 Subject: [PATCH 21/23] Edit campaigns controller --- .../Controllers/CampaignsController.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index ca8e93d8c..0082ee71f 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -8,10 +8,9 @@ using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; using System.Collections.Generic; using Microsoft.AspNetCore.Authorization; - using System.Linq; [Route("api/v1/[controller]")] - [Authorize] + //[Authorize] public class CampaignsController : Controller { private readonly MarketingContext _context; @@ -51,14 +50,14 @@ } [HttpPost] - public async Task CreateCampaign([FromBody] CampaignDTO campaign) + public async Task CreateCampaign([FromBody] CampaignDTO campaignDto) { - if (campaign is null) + if (campaignDto is null) { return BadRequest(); } - var campaingToCreate = MapCampaignDtoToModel(campaign); + var campaingToCreate = MapCampaignDtoToModel(campaignDto); await _context.Campaigns.AddAsync(campaingToCreate); await _context.SaveChangesAsync(); @@ -67,9 +66,9 @@ } [HttpPut("{id:int}")] - public async Task UpdateCampaign(int id, [FromBody]CampaignDTO campaign) + public async Task UpdateCampaign(int id, [FromBody]CampaignDTO campaignDto) { - if (id < 1 || campaign is null) + if (id < 1 || campaignDto is null) { return BadRequest(); } @@ -80,9 +79,10 @@ return NotFound(); } - campaignToUpdate.Description = campaign.Description; - campaignToUpdate.From = campaign.From; - campaignToUpdate.To = campaign.To; + campaignToUpdate.Description = campaignDto.Description; + campaignToUpdate.From = campaignDto.From; + campaignToUpdate.To = campaignDto.To; + campaignToUpdate.Url = campaignDto.Url; await _context.SaveChangesAsync(); From ce56a42bd1d4608e54d959905aefe4d63cc5c713 Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Tue, 6 Jun 2017 14:18:46 +0200 Subject: [PATCH 22/23] Redesign marketing campaign api in two different controllers (campaigns/locations) --- .../Controllers/CampaignsController.cs | 63 ++------ .../Controllers/LocationsController.cs | 146 ++++++++++++++++++ .../Marketing.API/Dto/CampaignDTO.cs | 8 - .../Marketing/Marketing.API/Dto/RuleDTO.cs | 20 --- .../Marketing.API/Dto/UserLocationRuleDTO.cs | 11 ++ .../Infrastructure/MarketingContext.cs | 6 +- .../Marketing.API/Marketing.API.csproj | 3 + .../Marketing/Marketing.API/Model/Rule.cs | 14 +- .../{Dto => Model}/RuleTypeEnum.cs | 8 +- 9 files changed, 189 insertions(+), 90 deletions(-) create mode 100644 src/Services/Marketing/Marketing.API/Controllers/LocationsController.cs delete mode 100644 src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs create mode 100644 src/Services/Marketing/Marketing.API/Dto/UserLocationRuleDTO.cs rename src/Services/Marketing/Marketing.API/{Dto => Model}/RuleTypeEnum.cs (71%) diff --git a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs index 0082ee71f..f3dde55ef 100644 --- a/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs +++ b/src/Services/Marketing/Marketing.API/Controllers/CampaignsController.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Authorization; [Route("api/v1/[controller]")] - //[Authorize] + [Authorize] public class CampaignsController : Controller { private readonly MarketingContext _context; @@ -24,9 +24,13 @@ public async Task GetAllCampaigns() { var campaignList = await _context.Campaigns - .Include(c => c.Rules) .ToListAsync(); + if (campaignList is null) + { + return Ok(); + } + var campaignDtoList = MapCampaignModelListToDtoList(campaignList); return Ok(campaignDtoList); @@ -36,7 +40,6 @@ public async Task GetCampaignById(int id) { var campaign = await _context.Campaigns - .Include(c => c.Rules) .SingleOrDefaultAsync(c => c.Id == id); if (campaign is null) @@ -57,16 +60,16 @@ return BadRequest(); } - var campaingToCreate = MapCampaignDtoToModel(campaignDto); + var campaign = MapCampaignDtoToModel(campaignDto); - await _context.Campaigns.AddAsync(campaingToCreate); + await _context.Campaigns.AddAsync(campaign); await _context.SaveChangesAsync(); - return CreatedAtAction(nameof(GetCampaignById), new { id = campaingToCreate.Id }, null); + return CreatedAtAction(nameof(GetCampaignById), new { id = campaign.Id }, null); } [HttpPut("{id:int}")] - public async Task UpdateCampaign(int id, [FromBody]CampaignDTO campaignDto) + public async Task UpdateCampaign(int id, [FromBody] CampaignDTO campaignDto) { if (id < 1 || campaignDto is null) { @@ -123,7 +126,7 @@ private CampaignDTO MapCampaignModelToDto(Campaign campaign) { - var campaignDto = new CampaignDTO + return new CampaignDTO { Id = campaign.Id, Description = campaign.Description, @@ -131,34 +134,11 @@ To = campaign.To, Url = campaign.Url, }; - - campaign.Rules.ForEach(rule => - { - var ruleDto = new RuleDTO - { - Id = rule.Id, - RuleTypeId = rule.RuleTypeId, - Description = rule.Description, - CampaignId = rule.CampaignId - }; - - switch (RuleType.From(rule.RuleTypeId)) - { - case RuleTypeEnum.UserLocationRule: - var userLocationRule = rule as UserLocationRule; - ruleDto.LocationId = userLocationRule.LocationId; - break; - } - - campaignDto.Rules.Add(ruleDto); - }); - - return campaignDto; } private Campaign MapCampaignDtoToModel(CampaignDTO campaignDto) { - var campaingModel = new Campaign + return new Campaign { Id = campaignDto.Id, Description = campaignDto.Description, @@ -166,25 +146,6 @@ To = campaignDto.To, Url = campaignDto.Url }; - - campaignDto.Rules.ForEach(ruleDto => - { - switch (RuleType.From(ruleDto.RuleTypeId)) - { - case RuleTypeEnum.UserLocationRule: - campaingModel.Rules.Add(new UserLocationRule - { - Id = ruleDto.Id, - LocationId = ruleDto.LocationId.Value, - RuleTypeId = ruleDto.RuleTypeId, - Description = ruleDto.Description, - Campaign = campaingModel - }); - break; - } - }); - - return campaingModel; } } } \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Controllers/LocationsController.cs b/src/Services/Marketing/Marketing.API/Controllers/LocationsController.cs new file mode 100644 index 000000000..843e58030 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Controllers/LocationsController.cs @@ -0,0 +1,146 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Controllers +{ + using Microsoft.AspNetCore.Authorization; + using Microsoft.AspNetCore.Mvc; + using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; + using Microsoft.eShopOnContainers.Services.Marketing.API.Model; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + [Authorize] + public class LocationsController : Controller + { + private readonly MarketingContext _context; + + public LocationsController(MarketingContext context) + { + _context = context; + } + + [HttpGet] + [Route("api/v1/campaigns/{campaignId:int}/locations/{userLocationRuleId:int}")] + public IActionResult GetLocationByCampaignAndLocationRuleId(int campaignId, + int userLocationRuleId) + { + if (campaignId < 1 || userLocationRuleId < 1) + { + return BadRequest(); + } + + var location = _context.Rules + .OfType() + .SingleOrDefault(c => c.CampaignId == campaignId && c.Id == userLocationRuleId); + + if (location is null) + { + return NotFound(); + } + + var locationDto = MapUserLocationRuleModelToDto(location); + + return Ok(locationDto); + } + + [HttpGet] + [Route("api/v1/campaigns/{campaignId:int}/locations")] + public IActionResult GetAllLocationsByCampaignId(int campaignId) + { + if (campaignId < 1) + { + return BadRequest(); + } + + var locationList = _context.Rules + .OfType() + .Where(c => c.CampaignId == campaignId) + .ToList(); + + if(locationList is null) + { + return Ok(); + } + + var locationDtoList = MapUserLocationRuleModelListToDtoList(locationList); + + return Ok(locationDtoList); + } + + [HttpPost] + [Route("api/v1/campaigns/{campaignId:int}/locations")] + public async Task CreateLocation(int campaignId, + [FromBody] UserLocationRuleDTO locationRuleDto) + { + if (campaignId < 1 || locationRuleDto is null) + { + return BadRequest(); + } + + var locationRule = MapUserLocationRuleDtoToModel(locationRuleDto); + locationRule.CampaignId = campaignId; + + await _context.Rules.AddAsync(locationRule); + await _context.SaveChangesAsync(); + + return CreatedAtAction(nameof(GetLocationByCampaignAndLocationRuleId), + new { campaignId = campaignId, locationRuleId = locationRule.Id }, null); + } + + [HttpDelete] + [Route("api/v1/campaigns/{campaignId:int}/locations/{userLocationRuleId:int}")] + public async Task DeleteLocationById(int campaignId, int userLocationRuleId) + { + if (campaignId < 1 || userLocationRuleId < 1) + { + return BadRequest(); + } + + var locationToDelete = _context.Rules + .OfType() + .SingleOrDefault(c => c.CampaignId == campaignId && c.Id == userLocationRuleId); + + if (locationToDelete is null) + { + return NotFound(); + } + + _context.Rules.Remove(locationToDelete); + await _context.SaveChangesAsync(); + + return NoContent(); + } + + + + private List MapUserLocationRuleModelListToDtoList(List userLocationRuleList) + { + var userLocationRuleDtoList = new List(); + + userLocationRuleList.ForEach(userLocationRule => userLocationRuleDtoList + .Add(MapUserLocationRuleModelToDto(userLocationRule))); + + return userLocationRuleDtoList; + } + + private UserLocationRuleDTO MapUserLocationRuleModelToDto(UserLocationRule userLocationRule) + { + return new UserLocationRuleDTO + { + Id = userLocationRule.Id, + Description = userLocationRule.Description, + LocationId = userLocationRule.LocationId + }; + } + + private UserLocationRule MapUserLocationRuleDtoToModel(UserLocationRuleDTO userLocationRuleDto) + { + return new UserLocationRule + { + Id = userLocationRuleDto.Id, + Description = userLocationRuleDto.Description, + LocationId = userLocationRuleDto.LocationId + }; + } + } +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs b/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs index b5676db63..a43dcdbda 100644 --- a/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs +++ b/src/Services/Marketing/Marketing.API/Dto/CampaignDTO.cs @@ -1,7 +1,6 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto { using System; - using System.Collections.Generic; public class CampaignDTO { @@ -14,12 +13,5 @@ public DateTime To { get; set; } public string Url { get; set; } - - public List Rules { get; set; } - - public CampaignDTO() - { - Rules = new List(); - } } } \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs b/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs deleted file mode 100644 index a79756e9c..000000000 --- a/src/Services/Marketing/Marketing.API/Dto/RuleDTO.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto -{ - public class RuleDTO - { - public int Id { get; set; } - - public int RuleTypeId { get; set; } - - public int CampaignId { get; set; } - - public int? LocationId { get; set; } - - public string Description { get; set; } - } -} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dto/UserLocationRuleDTO.cs b/src/Services/Marketing/Marketing.API/Dto/UserLocationRuleDTO.cs new file mode 100644 index 000000000..a76eb90a0 --- /dev/null +++ b/src/Services/Marketing/Marketing.API/Dto/UserLocationRuleDTO.cs @@ -0,0 +1,11 @@ +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto +{ + public class UserLocationRuleDTO + { + public int Id { get; set; } + + public int LocationId { get; set; } + + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs index 2fb45868b..15be03431 100644 --- a/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs +++ b/src/Services/Marketing/Marketing.API/Infrastructure/MarketingContext.cs @@ -64,9 +64,9 @@ .IsRequired(); builder.HasDiscriminator("RuleTypeId") - .HasValue(1) - .HasValue(2) - .HasValue(3); + .HasValue((int)RuleTypeEnum.UserProfileRule) + .HasValue((int)RuleTypeEnum.PurchaseHistoryRule) + .HasValue((int)RuleTypeEnum.UserLocationRule); builder.Property(r => r.Description) .HasColumnName("Description") diff --git a/src/Services/Marketing/Marketing.API/Marketing.API.csproj b/src/Services/Marketing/Marketing.API/Marketing.API.csproj index bd8e5a871..34d594e51 100644 --- a/src/Services/Marketing/Marketing.API/Marketing.API.csproj +++ b/src/Services/Marketing/Marketing.API/Marketing.API.csproj @@ -19,6 +19,7 @@ + @@ -36,6 +37,8 @@ + + diff --git a/src/Services/Marketing/Marketing.API/Model/Rule.cs b/src/Services/Marketing/Marketing.API/Model/Rule.cs index 03686bf86..e02128fa6 100644 --- a/src/Services/Marketing/Marketing.API/Model/Rule.cs +++ b/src/Services/Marketing/Marketing.API/Model/Rule.cs @@ -4,24 +4,30 @@ { public int Id { get; set; } - public int RuleTypeId { get; set; } - public int CampaignId { get; set; } public Campaign Campaign { get; set; } public string Description { get; set; } + + public abstract int RuleTypeId { get;} } public class UserProfileRule : Rule - { } + { + public override int RuleTypeId => (int)RuleTypeEnum.UserProfileRule; + } public class PurchaseHistoryRule : Rule - { } + { + public override int RuleTypeId => (int)RuleTypeEnum.PurchaseHistoryRule; + } public class UserLocationRule : Rule { + public override int RuleTypeId => (int)RuleTypeEnum.UserLocationRule; + public int LocationId { get; set; } } } \ No newline at end of file diff --git a/src/Services/Marketing/Marketing.API/Dto/RuleTypeEnum.cs b/src/Services/Marketing/Marketing.API/Model/RuleTypeEnum.cs similarity index 71% rename from src/Services/Marketing/Marketing.API/Dto/RuleTypeEnum.cs rename to src/Services/Marketing/Marketing.API/Model/RuleTypeEnum.cs index f00b7fbf9..c58dbf75c 100644 --- a/src/Services/Marketing/Marketing.API/Dto/RuleTypeEnum.cs +++ b/src/Services/Marketing/Marketing.API/Model/RuleTypeEnum.cs @@ -1,8 +1,8 @@ -using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions; -using System; - -namespace Microsoft.eShopOnContainers.Services.Marketing.API.Dto +namespace Microsoft.eShopOnContainers.Services.Marketing.API.Model { + using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Exceptions; + using System; + public enum RuleTypeEnum { UserProfileRule = 1, PurchaseHistoryRule = 2, UserLocationRule = 3 } public static class RuleType From 6ff6864f912be6a4434d3e8c9af57ecc7193b3ad Mon Sep 17 00:00:00 2001 From: Christian Arenas Date: Tue, 6 Jun 2017 14:19:01 +0200 Subject: [PATCH 23/23] Edit the integration test --- .../Marketing/CampaignScenarioBase.cs | 30 +++++++ ...etingScenarios.cs => CampaignScenarios.cs} | 20 ++--- .../Marketing/MarketingScenarioBase.cs | 45 ----------- .../Marketing/MarketingScenariosBase.cs | 20 +++++ .../Marketing/UserLocationRoleScenarios.cs | 80 +++++++++++++++++++ .../UserLocationRoleScenariosBase.cs | 40 ++++++++++ 6 files changed, 175 insertions(+), 60 deletions(-) create mode 100644 test/Services/IntegrationTests/Services/Marketing/CampaignScenarioBase.cs rename test/Services/IntegrationTests/Services/Marketing/{MarketingScenarios.cs => CampaignScenarios.cs} (88%) delete mode 100644 test/Services/IntegrationTests/Services/Marketing/MarketingScenarioBase.cs create mode 100644 test/Services/IntegrationTests/Services/Marketing/MarketingScenariosBase.cs create mode 100644 test/Services/IntegrationTests/Services/Marketing/UserLocationRoleScenarios.cs create mode 100644 test/Services/IntegrationTests/Services/Marketing/UserLocationRoleScenariosBase.cs diff --git a/test/Services/IntegrationTests/Services/Marketing/CampaignScenarioBase.cs b/test/Services/IntegrationTests/Services/Marketing/CampaignScenarioBase.cs new file mode 100644 index 000000000..a23fd1677 --- /dev/null +++ b/test/Services/IntegrationTests/Services/Marketing/CampaignScenarioBase.cs @@ -0,0 +1,30 @@ +namespace IntegrationTests.Services.Marketing +{ + public class CampaignScenarioBase : MarketingScenarioBase + { + public static class Get + { + public static string Campaigns = CampaignsUrlBase; + + public static string CampaignBy(int id) + => $"{CampaignsUrlBase}/{id}"; + } + + public static class Post + { + public static string AddNewCampaign = CampaignsUrlBase; + } + + public static class Put + { + public static string CampaignBy(int id) + => $"{CampaignsUrlBase}/{id}"; + } + + public static class Delete + { + public static string CampaignBy(int id) + => $"{CampaignsUrlBase}/{id}"; + } + } +} \ No newline at end of file diff --git a/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs b/test/Services/IntegrationTests/Services/Marketing/CampaignScenarios.cs similarity index 88% rename from test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs rename to test/Services/IntegrationTests/Services/Marketing/CampaignScenarios.cs index 29dcbebda..8e27958b9 100644 --- a/test/Services/IntegrationTests/Services/Marketing/MarketingScenarios.cs +++ b/test/Services/IntegrationTests/Services/Marketing/CampaignScenarios.cs @@ -6,13 +6,11 @@ using Xunit; using System; using Newtonsoft.Json; - using Microsoft.eShopOnContainers.Services.Marketing.API.Model; - using System.Collections.Generic; using System.Net; using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; - public class MarketingScenarios - : MarketingScenarioBase + public class CampaignScenarios + : CampaignScenarioBase { [Fact] public async Task Get_get_all_campaigns_and_response_ok_status_code() @@ -29,10 +27,11 @@ [Fact] public async Task Get_get_campaign_by_id_and_response_ok_status_code() { + var campaignId = 1; using (var server = CreateServer()) { var response = await server.CreateClient() - .GetAsync(Get.CampaignBy(1)); + .GetAsync(Get.CampaignBy(campaignId)); response.EnsureSuccessStatusCode(); } @@ -44,7 +43,7 @@ using (var server = CreateServer()) { var response = await server.CreateClient() - .GetAsync(Get.CampaignBy(9999999)); + .GetAsync(Get.CampaignBy(int.MaxValue)); Assert.True(response.StatusCode == HttpStatusCode.NotFound); } @@ -122,15 +121,6 @@ From = DateTime.Now, To = DateTime.Now.AddDays(7), Url = "http://CampaignUrl.test/fdaf91ad0cef5419719f50198", - Rules = new List - { - new RuleDTO - { - LocationId = 1, - Description = "testDescription", - RuleTypeId = 3, - } - } }; } } diff --git a/test/Services/IntegrationTests/Services/Marketing/MarketingScenarioBase.cs b/test/Services/IntegrationTests/Services/Marketing/MarketingScenarioBase.cs deleted file mode 100644 index 01383410b..000000000 --- a/test/Services/IntegrationTests/Services/Marketing/MarketingScenarioBase.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace IntegrationTests.Services.Marketing -{ - using Microsoft.AspNetCore.Hosting; - using Microsoft.AspNetCore.TestHost; - using System.IO; - - public class MarketingScenarioBase - { - private const string _campaignsUrlBase = "api/v1/campaigns"; - - public TestServer CreateServer() - { - var webHostBuilder = new WebHostBuilder(); - webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory() + "\\Services\\Marketing"); - webHostBuilder.UseStartup(); - - return new TestServer(webHostBuilder); - } - - public static class Get - { - public static string Campaigns = _campaignsUrlBase; - - public static string CampaignBy(int id) - => $"{_campaignsUrlBase}/{id}"; - } - - public static class Post - { - public static string AddNewCampaign = _campaignsUrlBase; - } - - public static class Put - { - public static string CampaignBy(int id) - => $"{_campaignsUrlBase}/{id}"; - } - - public static class Delete - { - public static string CampaignBy(int id) - => $"{_campaignsUrlBase}/{id}"; - } - } -} \ No newline at end of file diff --git a/test/Services/IntegrationTests/Services/Marketing/MarketingScenariosBase.cs b/test/Services/IntegrationTests/Services/Marketing/MarketingScenariosBase.cs new file mode 100644 index 000000000..832fb1b82 --- /dev/null +++ b/test/Services/IntegrationTests/Services/Marketing/MarketingScenariosBase.cs @@ -0,0 +1,20 @@ +namespace IntegrationTests.Services.Marketing +{ + using Microsoft.AspNetCore.Hosting; + using Microsoft.AspNetCore.TestHost; + using System.IO; + + public class MarketingScenarioBase + { + public static string CampaignsUrlBase => "api/v1/campaigns"; + + public TestServer CreateServer() + { + var webHostBuilder = new WebHostBuilder(); + webHostBuilder.UseContentRoot(Directory.GetCurrentDirectory() + "\\Services\\Marketing"); + webHostBuilder.UseStartup(); + + return new TestServer(webHostBuilder); + } + } +} \ No newline at end of file diff --git a/test/Services/IntegrationTests/Services/Marketing/UserLocationRoleScenarios.cs b/test/Services/IntegrationTests/Services/Marketing/UserLocationRoleScenarios.cs new file mode 100644 index 000000000..f7fbd6cee --- /dev/null +++ b/test/Services/IntegrationTests/Services/Marketing/UserLocationRoleScenarios.cs @@ -0,0 +1,80 @@ +namespace IntegrationTests.Services.Marketing +{ + using System.Net.Http; + using System.Text; + using System.Threading.Tasks; + using Xunit; + using System; + using Newtonsoft.Json; + using System.Net; + using Microsoft.eShopOnContainers.Services.Marketing.API.Dto; + + public class UserLocationRoleScenarios + : UserLocationRoleScenariosBase + { + [Fact] + public async Task Get_get_all_user_location_rules_by_campaignId_and_response_ok_status_code() + { + var campaignId = 1; + + using (var server = CreateServer()) + { + var response = await server.CreateClient() + .GetAsync(Get.UserLocationRulesByCampaignId(campaignId)); + + response.EnsureSuccessStatusCode(); + } + } + + [Fact] + public async Task Post_add_new_user_location_rule_and_response_ok_status_code() + { + var campaignId = 1; + + using (var server = CreateServer()) + { + var fakeCampaignDto = GetFakeUserLocationRuleDto(); + var content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); + var response = await server.CreateClient() + .PostAsync(Post.AddNewuserLocationRule(campaignId), content); + + response.EnsureSuccessStatusCode(); + } + } + + [Fact] + public async Task Delete_delete_user_location_role_and_response_not_content_status_code() + { + var campaignId = 1; + + using (var server = CreateServer()) + { + var fakeCampaignDto = GetFakeUserLocationRuleDto(); + var content = new StringContent(JsonConvert.SerializeObject(fakeCampaignDto), Encoding.UTF8, "application/json"); + + //add user location role + var campaignResponse = await server.CreateClient() + .PostAsync(Post.AddNewuserLocationRule(campaignId), content); + + if (int.TryParse(campaignResponse.Headers.Location.Segments[6], out int userLocationRuleId)) + { + var response = await server.CreateClient() + .DeleteAsync(Delete.UserLocationRoleBy(campaignId, userLocationRuleId)); + + Assert.True(response.StatusCode == HttpStatusCode.NoContent); + } + + campaignResponse.EnsureSuccessStatusCode(); + } + } + + private static UserLocationRuleDTO GetFakeUserLocationRuleDto() + { + return new UserLocationRuleDTO + { + LocationId = 20, + Description = "FakeUserLocationRuleDescription" + }; + } + } +} diff --git a/test/Services/IntegrationTests/Services/Marketing/UserLocationRoleScenariosBase.cs b/test/Services/IntegrationTests/Services/Marketing/UserLocationRoleScenariosBase.cs new file mode 100644 index 000000000..cd6fcc9f3 --- /dev/null +++ b/test/Services/IntegrationTests/Services/Marketing/UserLocationRoleScenariosBase.cs @@ -0,0 +1,40 @@ +namespace IntegrationTests.Services.Marketing +{ + public class UserLocationRoleScenariosBase : MarketingScenarioBase + { + private const string EndpointLocationName = "locations"; + public static class Get + { + public static string UserLocationRulesByCampaignId(int campaignId) + => GetUserLocationRolesUrlBase(campaignId); + + public static string UserLocationRuleByCampaignAndUserLocationRuleId(int campaignId, + int userLocationRuleId) + => $"{GetUserLocationRolesUrlBase(campaignId)}/{userLocationRuleId}"; + } + + public static class Post + { + public static string AddNewuserLocationRule(int campaignId) + => GetUserLocationRolesUrlBase(campaignId); + } + + public static class Put + { + public static string UserLocationRoleBy(int campaignId, + int userLocationRuleId) + => $"{GetUserLocationRolesUrlBase(campaignId)}/{userLocationRuleId}"; + } + + public static class Delete + { + public static string UserLocationRoleBy(int campaignId, + int userLocationRuleId) + => $"{GetUserLocationRolesUrlBase(campaignId)}/{userLocationRuleId}"; + } + + + private static string GetUserLocationRolesUrlBase(int campaignId) + => $"{CampaignsUrlBase}/{campaignId}/{EndpointLocationName}"; + } +} \ No newline at end of file