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