Merge branch 'feature/display-marketing-banner-mvc' into dev
This commit is contained in:
		
						commit
						a537e689f4
					
				| @ -61,6 +61,7 @@ services: | ||||
|       - MongoConnectionString=mongodb://nosql.data | ||||
|       - MongoDatabase=MarketingDb | ||||
|       - EventBusConnection=rabbitmq | ||||
|       - ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110   #Local: You need to open your local dev-machine firewall at range 5100-5105.  at range 5100-5105.	   | ||||
|       - 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" | ||||
| @ -73,6 +74,7 @@ services: | ||||
|       - OrderingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5102 | ||||
|       - IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105              #Local: You need to open your local dev-machine firewall at range 5100-5105.  at range 5100-5105.  | ||||
|       - BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103 | ||||
|       - MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110 | ||||
|       - CatalogUrlHC=http://catalog.api/hc | ||||
|       - OrderingUrlHC=http://ordering.api/hc | ||||
|       - IdentityUrlHC=http://identity.api/hc     #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.           | ||||
| @ -87,7 +89,8 @@ services: | ||||
|       - CatalogUrl=http://catalog.api | ||||
|       - OrderingUrl=http://ordering.api | ||||
|       - BasketUrl=http://basket.api | ||||
|       - IdentityUrl=http://10.0.75.1:5105        #Local:  Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser.  | ||||
|       - IdentityUrl=http://10.0.75.1:5105 | ||||
|       - MarketingUrl=http://marketing.api  #Local:  Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser.  | ||||
|                                                  #Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.  | ||||
|     ports: | ||||
|       - "5100:80" | ||||
|  | ||||
| @ -76,6 +76,7 @@ services: | ||||
|       - ordering.api | ||||
|       - identity.api | ||||
|       - basket.api | ||||
|       - marketing.api | ||||
| 
 | ||||
|   sql.data: | ||||
|     image: microsoft/mssql-server-linux | ||||
|  | ||||
| @ -108,7 +108,8 @@ namespace Identity.API.Configuration | ||||
|                         IdentityServerConstants.StandardScopes.OfflineAccess, | ||||
|                         "orders", | ||||
|                         "basket", | ||||
|                         "locations" | ||||
|                         "locations", | ||||
|                         "marketing" | ||||
|                     }, | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
| @ -11,19 +11,23 @@ | ||||
|     using Microsoft.AspNetCore.Authorization; | ||||
|     using System; | ||||
|     using System.Linq; | ||||
|     using Microsoft.Extensions.Options; | ||||
| 
 | ||||
|     [Route("api/v1/[controller]")]
 | ||||
|     [Authorize] | ||||
|     public class CampaignsController : Controller | ||||
|     { | ||||
|         private readonly MarketingContext _context; | ||||
|         private readonly MarketingSettings _settings; | ||||
|         private readonly IMarketingDataRepository _marketingDataRepository; | ||||
| 
 | ||||
|         public CampaignsController(MarketingContext context, | ||||
|             IMarketingDataRepository marketingDataRepository) | ||||
|             IMarketingDataRepository marketingDataRepository, | ||||
|              IOptionsSnapshot<MarketingSettings> settings) | ||||
|         { | ||||
|             _context = context; | ||||
|             _marketingDataRepository = marketingDataRepository; | ||||
|             _settings = settings.Value; | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet] | ||||
| @ -88,10 +92,11 @@ | ||||
|                 return NotFound(); | ||||
|             } | ||||
| 
 | ||||
|             campaignToUpdate.Name = campaignDto.Name; | ||||
|             campaignToUpdate.Description = campaignDto.Description; | ||||
|             campaignToUpdate.From = campaignDto.From; | ||||
|             campaignToUpdate.To = campaignDto.To; | ||||
|             campaignToUpdate.Url = campaignDto.Url; | ||||
|             campaignToUpdate.PictureUri = campaignDto.PictureUri; | ||||
| 
 | ||||
|             await _context.SaveChangesAsync(); | ||||
| 
 | ||||
| @ -136,7 +141,9 @@ | ||||
|                 var userCampaignList = await _context.Rules | ||||
|                     .OfType<UserLocationRule>() | ||||
|                     .Include(c => c.Campaign) | ||||
|                     .Where(c => c.LocationId == userLocation.LocationId) | ||||
|                     .Where(c => c.Campaign.From <= DateTime.Now | ||||
|                     && c.Campaign.To >= DateTime.Now  | ||||
|                     && c.LocationId == userLocation.LocationId) | ||||
|                     .Select(c => c.Campaign) | ||||
|                     .ToListAsync(); | ||||
| 
 | ||||
| @ -166,10 +173,11 @@ | ||||
|             return new CampaignDTO | ||||
|             { | ||||
|                 Id = campaign.Id, | ||||
|                 Name = campaign.Name, | ||||
|                 Description = campaign.Description, | ||||
|                 From = campaign.From, | ||||
|                 To = campaign.To, | ||||
|                 Url = campaign.Url, | ||||
|                 PictureUri = GetUriPlaceholder(campaign.PictureUri) | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
| @ -178,11 +186,21 @@ | ||||
|             return new Campaign | ||||
|             { | ||||
|                 Id = campaignDto.Id, | ||||
|                 Name = campaignDto.Name, | ||||
|                 Description = campaignDto.Description, | ||||
|                 From = campaignDto.From, | ||||
|                 To = campaignDto.To, | ||||
|                 Url = campaignDto.Url | ||||
|                 PictureUri = campaignDto.PictureUri | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         private string GetUriPlaceholder(string campaignUri) | ||||
|         { | ||||
|             var baseUri = _settings.ExternalCatalogBaseUrl; | ||||
| 
 | ||||
|             campaignUri = campaignUri.Replace("http://externalcatalogbaseurltobereplaced", baseUri); | ||||
| 
 | ||||
|             return campaignUri; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,28 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Marketing.API.Controllers | ||||
| { | ||||
|     using Microsoft.AspNetCore.Hosting; | ||||
|     using Microsoft.AspNetCore.Mvc; | ||||
|     using System.IO; | ||||
| 
 | ||||
| 
 | ||||
|     public class PicController : Controller | ||||
|     { | ||||
|         private readonly IHostingEnvironment _env; | ||||
|         public PicController(IHostingEnvironment env) | ||||
|         { | ||||
|             _env = env; | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet] | ||||
|         [Route("api/v1/campaigns/{campaignId:int}/pic")] | ||||
|         public IActionResult GetImage(int campaignId) | ||||
|         { | ||||
|             var webRoot = _env.WebRootPath; | ||||
|             var path = Path.Combine(webRoot, campaignId + ".png"); | ||||
| 
 | ||||
|             var buffer = System.IO.File.ReadAllBytes(path);  | ||||
|              | ||||
|             return File(buffer, "image/png"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -6,12 +6,14 @@ | ||||
|     { | ||||
|         public int Id { get; set; } | ||||
| 
 | ||||
|         public string Name { get; set; } | ||||
| 
 | ||||
|         public string Description { get; set; } | ||||
| 
 | ||||
|         public DateTime From { get; set; } | ||||
| 
 | ||||
|         public DateTime To { get; set; } | ||||
| 
 | ||||
|         public string Url { get; set; } | ||||
|         public string PictureUri { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -31,8 +31,8 @@ | ||||
|                 .ForSqlServerUseSequenceHiLo("campaign_hilo") | ||||
|                 .IsRequired(); | ||||
| 
 | ||||
|             builder.Property(m => m.Description) | ||||
|                 .HasColumnName("Description") | ||||
|             builder.Property(m => m.Name) | ||||
|                 .HasColumnName("Name") | ||||
|                 .IsRequired(); | ||||
| 
 | ||||
|             builder.Property(m => m.From) | ||||
| @ -47,6 +47,10 @@ | ||||
|                 .HasColumnName("Description") | ||||
|                 .IsRequired(); | ||||
| 
 | ||||
|             builder.Property(m => m.PictureUri) | ||||
|                 .HasColumnName("PictureUri") | ||||
|                 .IsRequired(); | ||||
| 
 | ||||
|             builder.HasMany(m => m.Rules) | ||||
|                 .WithOne(r => r.Campaign) | ||||
|                 .HasForeignKey(r => r.CampaignId) | ||||
|  | ||||
| @ -9,7 +9,7 @@ | ||||
|     using System.Linq; | ||||
|     using System.Threading.Tasks; | ||||
| 
 | ||||
|     public class MarketingContextSeed | ||||
|     public static class MarketingContextSeed | ||||
|     { | ||||
|         public static async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, int? retry = 0) | ||||
|         { | ||||
| @ -33,31 +33,33 @@ | ||||
|             { | ||||
|                 new Campaign | ||||
|                 { | ||||
|                     Description = "Campaign1", | ||||
|                     Name = ".NET Bot Black Hoodie 50% OFF", | ||||
|                     Description = "Campaign Description 1", | ||||
|                     From = DateTime.Now, | ||||
|                     To = DateTime.Now.AddDays(7), | ||||
|                     Url = "http://CampaignUrl.test/12f09ed3cef54187123f500ad", | ||||
|                     PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/campaigns/1/pic", | ||||
|                     Rules = new List<Rule> | ||||
|                     { | ||||
|                         new UserLocationRule | ||||
|                         { | ||||
|                             Description = "UserLocationRule1", | ||||
|                             Description = "Campaign is only for United States users.", | ||||
|                             LocationId = 1 | ||||
|                         } | ||||
|                     } | ||||
|                 }, | ||||
|                 new Campaign | ||||
|                 { | ||||
|                     Description = "Campaign2", | ||||
|                     From = DateTime.Now.AddDays(7), | ||||
|                     Name = "Roslyn Red T-Shirt 3x2", | ||||
|                     Description = "Campaign Description 2", | ||||
|                     From = DateTime.Now.AddDays(-7), | ||||
|                     To = DateTime.Now.AddDays(14), | ||||
|                     Url = "http://CampaignUrl.test/02a59eda65f241871239000ff", | ||||
|                     PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/campaigns/2/pic", | ||||
|                     Rules = new List<Rule> | ||||
|                     { | ||||
|                         new UserLocationRule | ||||
|                         { | ||||
|                             Description = "UserLocationRule2", | ||||
|                             LocationId = 6 | ||||
|                             Description = "Campaign is only for Seattle users.", | ||||
|                             LocationId = 3 | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
| @ -8,8 +8,8 @@ using Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure; | ||||
| namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations | ||||
| { | ||||
|     [DbContext(typeof(MarketingContext))] | ||||
|     [Migration("20170609104915_Initial")] | ||||
|     partial class Initial | ||||
|     [Migration("20170615163431_Init")] | ||||
|     partial class Init | ||||
|     { | ||||
|         protected override void BuildTargetModel(ModelBuilder modelBuilder) | ||||
|         { | ||||
| @ -33,11 +33,17 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Mark | ||||
|                     b.Property<DateTime>("From") | ||||
|                         .HasColumnName("From"); | ||||
| 
 | ||||
|                     b.Property<string>("Name") | ||||
|                         .IsRequired() | ||||
|                         .HasColumnName("Name"); | ||||
| 
 | ||||
|                     b.Property<string>("PictureUri") | ||||
|                         .IsRequired() | ||||
|                         .HasColumnName("PictureUri"); | ||||
| 
 | ||||
|                     b.Property<DateTime>("To") | ||||
|                         .HasColumnName("To"); | ||||
| 
 | ||||
|                     b.Property<string>("Url"); | ||||
| 
 | ||||
|                     b.HasKey("Id"); | ||||
| 
 | ||||
|                     b.ToTable("Campaign"); | ||||
| @ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore.Migrations; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.MarketingMigrations | ||||
| { | ||||
|     public partial class Initial : Migration | ||||
|     public partial class Init : Migration | ||||
|     { | ||||
|         protected override void Up(MigrationBuilder migrationBuilder) | ||||
|         { | ||||
| @ -23,8 +23,9 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Mark | ||||
|                     Id = table.Column<int>(nullable: false), | ||||
|                     Description = table.Column<string>(nullable: false), | ||||
|                     From = table.Column<DateTime>(nullable: false), | ||||
|                     To = table.Column<DateTime>(nullable: false), | ||||
|                     Url = table.Column<string>(nullable: true) | ||||
|                     Name = table.Column<string>(nullable: false), | ||||
|                     PictureUri = table.Column<string>(nullable: false), | ||||
|                     To = table.Column<DateTime>(nullable: false) | ||||
|                 }, | ||||
|                 constraints: table => | ||||
|                 { | ||||
| @ -32,11 +32,17 @@ namespace Microsoft.eShopOnContainers.Services.Marketing.API.Infrastructure.Mark | ||||
|                     b.Property<DateTime>("From") | ||||
|                         .HasColumnName("From"); | ||||
| 
 | ||||
|                     b.Property<string>("Name") | ||||
|                         .IsRequired() | ||||
|                         .HasColumnName("Name"); | ||||
| 
 | ||||
|                     b.Property<string>("PictureUri") | ||||
|                         .IsRequired() | ||||
|                         .HasColumnName("PictureUri"); | ||||
| 
 | ||||
|                     b.Property<DateTime>("To") | ||||
|                         .HasColumnName("To"); | ||||
| 
 | ||||
|                     b.Property<string>("Url"); | ||||
| 
 | ||||
|                     b.HasKey("Id"); | ||||
| 
 | ||||
|                     b.ToTable("Campaign"); | ||||
|  | ||||
| @ -12,6 +12,10 @@ | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Folder Include="Infrastructure\MarketingMigrations\" /> | ||||
|     <Content Include="Pics\**\*;"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|     <Folder Include="Infrastructure\MarketingMigrations\" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" /> | ||||
| @ -53,4 +57,13 @@ | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\..\..\BuildingBlocks\EventBus\EventBusRabbitMQ\EventBusRabbitMQ.csproj" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <None Update="Dockerfile"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Update="Pics\*"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
| </Project> | ||||
|  | ||||
| @ -5,5 +5,6 @@ | ||||
|         public string ConnectionString { get; set; } | ||||
|         public string MongoConnectionString { get; set; } | ||||
|         public string MongoDatabase { get; set; } | ||||
|         public string ExternalCatalogBaseUrl { get; set; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -7,13 +7,15 @@ | ||||
|     { | ||||
|         public int Id { get; set; } | ||||
| 
 | ||||
|         public string Name { get; set; } | ||||
| 
 | ||||
|         public string Description { get; set; } | ||||
| 
 | ||||
|         public DateTime From { get; set; } | ||||
| 
 | ||||
|         public DateTime To { get; set; } | ||||
| 
 | ||||
|         public string Url { get; set; } | ||||
|         public string PictureUri { get; set; } | ||||
| 
 | ||||
|         public List<Rule> Rules { get; set; } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								src/Services/Marketing/Marketing.API/Pics/1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Services/Marketing/Marketing.API/Pics/1.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 136 KiB | 
							
								
								
									
										
											BIN
										
									
								
								src/Services/Marketing/Marketing.API/Pics/2.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Services/Marketing/Marketing.API/Pics/2.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 166 KiB | 
| @ -12,6 +12,7 @@ | ||||
|                 .UseKestrel() | ||||
|                 .UseContentRoot(Directory.GetCurrentDirectory()) | ||||
|                 .UseStartup<Startup>() | ||||
|                 .UseWebRoot("Pics") | ||||
|                 .Build(); | ||||
| 
 | ||||
|             host.Run(); | ||||
|  | ||||
| @ -20,6 +20,9 @@ | ||||
|     using Infrastructure.Repositories; | ||||
|     using Autofac; | ||||
|     using Autofac.Extensions.DependencyInjection; | ||||
|     using Polly; | ||||
|     using System.Threading.Tasks; | ||||
|     using System.Data.SqlClient; | ||||
| 
 | ||||
|     public class Startup | ||||
|     { | ||||
| @ -133,10 +136,12 @@ | ||||
|                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); | ||||
|                }); | ||||
| 
 | ||||
|             ConfigureEventBus(app); | ||||
|             var context = (MarketingContext)app | ||||
|                         .ApplicationServices.GetService(typeof(MarketingContext)); | ||||
| 
 | ||||
|             MarketingContextSeed.SeedAsync(app, loggerFactory) | ||||
|                 .Wait(); | ||||
|             WaitForSqlAvailabilityAsync(context, loggerFactory, app).Wait(); | ||||
| 
 | ||||
|             ConfigureEventBus(app); | ||||
|         } | ||||
| 
 | ||||
|         protected virtual void ConfigureAuth(IApplicationBuilder app) | ||||
| @ -166,5 +171,28 @@ | ||||
|             eventBus.Subscribe<UserLocationUpdatedIntegrationEvent,  | ||||
|                 IIntegrationEventHandler<UserLocationUpdatedIntegrationEvent>>(); | ||||
|         } | ||||
| 
 | ||||
|         private async Task WaitForSqlAvailabilityAsync(MarketingContext ctx, ILoggerFactory loggerFactory, IApplicationBuilder app, int retries = 0) | ||||
|         { | ||||
|             var logger = loggerFactory.CreateLogger(nameof(Startup)); | ||||
|             var policy = CreatePolicy(retries, logger, nameof(WaitForSqlAvailabilityAsync)); | ||||
|             await policy.ExecuteAsync(async () => | ||||
|             { | ||||
|                 await MarketingContextSeed.SeedAsync(app, loggerFactory); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         private Policy CreatePolicy(int retries, ILogger logger, string prefix) | ||||
|         { | ||||
|             return Policy.Handle<SqlException>(). | ||||
|                 WaitAndRetryAsync( | ||||
|                     retryCount: retries, | ||||
|                     sleepDurationProvider: retry => TimeSpan.FromSeconds(5), | ||||
|                     onRetry: (exception, timeSpan, retry, ctx) => | ||||
|                     { | ||||
|                         logger.LogTrace($"[{prefix}] Exception {exception.GetType().Name} with message ${exception.Message} detected on attempt {retry} of {retries}"); | ||||
|                     } | ||||
|                 ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -8,5 +8,6 @@ | ||||
|   "ConnectionString": "127.0.0.1", | ||||
|   "MongoConnectionString": "mongodb://nosql.data", | ||||
|   "MongoDatabase": "MarketingDb", | ||||
|   "IdentityUrl": "http://localhost:5105" | ||||
|   "IdentityUrl": "http://localhost:5105", | ||||
|   "ExternalCatalogBaseUrl": "http://localhost:5110" | ||||
| } | ||||
|  | ||||
| @ -11,6 +11,7 @@ namespace Microsoft.eShopOnContainers.WebMVC | ||||
|         public string CatalogUrl { get; set; } | ||||
|         public string OrderingUrl { get; set; } | ||||
|         public string BasketUrl { get; set; } | ||||
|         public string MarketingUrl { get; set; } | ||||
|         public Logging Logging { get; set; } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										80
									
								
								src/Web/WebMVC/Controllers/CampaignsController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								src/Web/WebMVC/Controllers/CampaignsController.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | ||||
| namespace Microsoft.eShopOnContainers.WebMVC.Controllers | ||||
| { | ||||
|     using Microsoft.AspNetCore.Authorization; | ||||
|     using Microsoft.AspNetCore.Mvc; | ||||
|     using Microsoft.eShopOnContainers.WebMVC.Models; | ||||
|     using Microsoft.eShopOnContainers.WebMVC.Services; | ||||
|     using Microsoft.eShopOnContainers.WebMVC.ViewModels; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Threading.Tasks; | ||||
| 
 | ||||
|     [Authorize] | ||||
|     public class CampaignsController : Controller | ||||
|     { | ||||
|         private ICampaignService _campaignService; | ||||
| 
 | ||||
|         public CampaignsController(ICampaignService campaignService) => | ||||
|             _campaignService = campaignService; | ||||
| 
 | ||||
|         public async Task<IActionResult> Index() | ||||
|         { | ||||
|             var campaignDtoList = await _campaignService.GetCampaigns(); | ||||
| 
 | ||||
|             if(campaignDtoList is null) | ||||
|             { | ||||
|                 return View(); | ||||
|             } | ||||
| 
 | ||||
|             var campaignList = MapCampaignModelListToDtoList(campaignDtoList); | ||||
| 
 | ||||
|             return View(campaignList); | ||||
|         } | ||||
| 
 | ||||
|         public async Task<IActionResult> Details(int id) | ||||
|         { | ||||
|             var campaignDto = await _campaignService.GetCampaignById(id); | ||||
| 
 | ||||
|             if (campaignDto is null) | ||||
|             { | ||||
|                 return NotFound(); | ||||
|             } | ||||
| 
 | ||||
|             var campaign = new Campaign | ||||
|             { | ||||
|                 Id = campaignDto.Id, | ||||
|                 Name = campaignDto.Name, | ||||
|                 Description = campaignDto.Description, | ||||
|                 From = campaignDto.From, | ||||
|                 To = campaignDto.To, | ||||
|                 PictureUri = campaignDto.PictureUri | ||||
|             }; | ||||
| 
 | ||||
|             return View(campaign); | ||||
|         } | ||||
| 
 | ||||
|         private List<Campaign> MapCampaignModelListToDtoList(IEnumerable<CampaignDTO> campaignDtoList) | ||||
|         { | ||||
|             var campaignList = new List<Campaign>(); | ||||
| 
 | ||||
|             foreach(var campaignDto in campaignDtoList) | ||||
|             { | ||||
|                 campaignList.Add(MapCampaignDtoToModel(campaignDto)); | ||||
|             } | ||||
| 
 | ||||
|             return campaignList; | ||||
|         } | ||||
| 
 | ||||
|         private Campaign MapCampaignDtoToModel(CampaignDTO campaign) | ||||
|         { | ||||
|             return new Campaign | ||||
|             { | ||||
|                 Id = campaign.Id, | ||||
|                 Name = campaign.Name, | ||||
|                 Description = campaign.Description, | ||||
|                 From = campaign.From, | ||||
|                 To = campaign.To, | ||||
|                 PictureUri = campaign.PictureUri | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,4 +1,6 @@ | ||||
| namespace WebMVC.Infrastructure | ||||
| using System; | ||||
| 
 | ||||
| namespace WebMVC.Infrastructure | ||||
| { | ||||
|     public static class API | ||||
|     { | ||||
| @ -79,5 +81,18 @@ | ||||
|                 return $"{baseUri}catalogTypes"; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static class Marketing | ||||
|         { | ||||
|             public static string GetAllCampaigns(string baseUri, Guid userId) | ||||
|             { | ||||
|                 return $"{baseUri}user/{userId}"; | ||||
|             } | ||||
| 
 | ||||
|             public static string GetAllCampaignById(string baseUri, int id) | ||||
|             { | ||||
|                 return $"{baseUri}{id}"; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										19
									
								
								src/Web/WebMVC/Models/CampaignDTO.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Web/WebMVC/Models/CampaignDTO.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| namespace Microsoft.eShopOnContainers.WebMVC.Models | ||||
| { | ||||
|     using System; | ||||
| 
 | ||||
|     public class CampaignDTO | ||||
|     { | ||||
|         public int Id { get; set; } | ||||
| 
 | ||||
|         public string Name { get; set; } | ||||
| 
 | ||||
|         public string Description { get; set; } | ||||
| 
 | ||||
|         public DateTime From { get; set; } | ||||
| 
 | ||||
|         public DateTime To { get; set; } | ||||
| 
 | ||||
|         public string PictureUri { get; set; } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										71
									
								
								src/Web/WebMVC/Services/CampaignService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/Web/WebMVC/Services/CampaignService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| namespace Microsoft.eShopOnContainers.WebMVC.Services | ||||
| { | ||||
|     using global::WebMVC.Infrastructure; | ||||
|     using Microsoft.AspNetCore.Authentication; | ||||
|     using Microsoft.AspNetCore.Http; | ||||
|     using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http; | ||||
|     using Microsoft.eShopOnContainers.WebMVC.Models; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using Microsoft.Extensions.Options; | ||||
|     using Newtonsoft.Json; | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Threading.Tasks; | ||||
| 
 | ||||
|     public class CampaignService : ICampaignService | ||||
|     { | ||||
|         private readonly IOptionsSnapshot<AppSettings> _settings; | ||||
|         private readonly IHttpClient _apiClient; | ||||
|         private readonly ILogger<CampaignService> _logger; | ||||
|         private readonly string _remoteServiceBaseUrl; | ||||
|         private readonly IHttpContextAccessor _httpContextAccesor; | ||||
| 
 | ||||
|         public CampaignService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient, | ||||
|             ILogger<CampaignService> logger, IHttpContextAccessor httpContextAccesor) | ||||
|         { | ||||
|             _settings = settings; | ||||
|             _apiClient = httpClient; | ||||
|             _logger = logger; | ||||
| 
 | ||||
|             _remoteServiceBaseUrl = $"{_settings.Value.MarketingUrl}/api/v1/campaigns/"; | ||||
|             _httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor)); | ||||
|         } | ||||
| 
 | ||||
|         public async Task<IEnumerable<CampaignDTO>> GetCampaigns() | ||||
|         { | ||||
|             var userId = GetUserIdentity(); | ||||
|             var allCampaignItemsUri = API.Marketing.GetAllCampaigns(_remoteServiceBaseUrl, Guid.Parse(userId)); | ||||
| 
 | ||||
|             var authorizationToken = await GetUserTokenAsync(); | ||||
|             var dataString = await _apiClient.GetStringAsync(allCampaignItemsUri, authorizationToken); | ||||
| 
 | ||||
|             var response = JsonConvert.DeserializeObject<IEnumerable<CampaignDTO>>(dataString); | ||||
| 
 | ||||
|             return response; | ||||
|         } | ||||
| 
 | ||||
|         public async Task<CampaignDTO> GetCampaignById(int id) | ||||
|         { | ||||
|             var userId = GetUserIdentity(); | ||||
|             var campaignByIdItemUri = API.Marketing.GetAllCampaignById(_remoteServiceBaseUrl, id); | ||||
| 
 | ||||
|             var authorizationToken = await GetUserTokenAsync(); | ||||
|             var dataString = await _apiClient.GetStringAsync(campaignByIdItemUri, authorizationToken); | ||||
| 
 | ||||
|             var response = JsonConvert.DeserializeObject<CampaignDTO>(dataString); | ||||
| 
 | ||||
|             return response; | ||||
|         } | ||||
| 
 | ||||
|         private string GetUserIdentity() | ||||
|         { | ||||
|             return _httpContextAccesor.HttpContext.User.FindFirst("sub").Value; | ||||
|         } | ||||
| 
 | ||||
|         private async Task<string> GetUserTokenAsync() | ||||
|         { | ||||
|             var context = _httpContextAccesor.HttpContext; | ||||
|             return await context.Authentication.GetTokenAsync("access_token"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								src/Web/WebMVC/Services/ICampaignService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/Web/WebMVC/Services/ICampaignService.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| namespace Microsoft.eShopOnContainers.WebMVC.Services | ||||
| { | ||||
|     using Microsoft.eShopOnContainers.WebMVC.Models; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Threading.Tasks; | ||||
| 
 | ||||
|     public interface ICampaignService | ||||
|     { | ||||
|         Task<IEnumerable<CampaignDTO>> GetCampaigns(); | ||||
| 
 | ||||
|         Task<CampaignDTO> GetCampaignById(int id); | ||||
|     } | ||||
| } | ||||
| @ -70,6 +70,7 @@ namespace Microsoft.eShopOnContainers.WebMVC | ||||
|             services.AddTransient<ICatalogService, CatalogService>(); | ||||
|             services.AddTransient<IOrderingService, OrderingService>(); | ||||
|             services.AddTransient<IBasketService, BasketService>(); | ||||
|             services.AddTransient<ICampaignService, CampaignService>(); | ||||
|             services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); | ||||
| 
 | ||||
|             if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString) | ||||
| @ -125,7 +126,7 @@ namespace Microsoft.eShopOnContainers.WebMVC | ||||
|                 SaveTokens = true, | ||||
|                 GetClaimsFromUserInfoEndpoint = true, | ||||
|                 RequireHttpsMetadata = false, | ||||
|                 Scope = { "openid", "profile", "orders", "basket" } | ||||
|                 Scope = { "openid", "profile", "orders", "basket", "marketing" } | ||||
|             }; | ||||
| 
 | ||||
|             //Wait untill identity service is ready on compose.  | ||||
|  | ||||
							
								
								
									
										19
									
								
								src/Web/WebMVC/ViewModels/Campaign.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/Web/WebMVC/ViewModels/Campaign.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | ||||
| namespace Microsoft.eShopOnContainers.WebMVC.ViewModels | ||||
| { | ||||
|     using System; | ||||
| 
 | ||||
|     public class Campaign | ||||
|     { | ||||
|         public int Id { get; set; } | ||||
| 
 | ||||
|         public string Name { get; set; } | ||||
| 
 | ||||
|         public string Description { get; set; } | ||||
| 
 | ||||
|         public DateTime From { get; set; } | ||||
| 
 | ||||
|         public DateTime To { get; set; } | ||||
| 
 | ||||
|         public string PictureUri { get; set; } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/Web/WebMVC/Views/Campaigns/Details.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Web/WebMVC/Views/Campaigns/Details.cshtml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| @{ | ||||
|     ViewData["Title"] = "Campaign details"; | ||||
|     @model Microsoft.eShopOnContainers.WebMVC.ViewModels.Campaign | ||||
| } | ||||
| <section class="esh-campaigns-hero"> | ||||
|     <div class="container"> | ||||
|         <img class="esh-campaigns-title" src="~/images/main_banner_text.png" /> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
| @Html.Partial("_Header", new List<Header>() { | ||||
|             new Header() { Controller = "Catalog", Text = "Back to catalog" }, | ||||
|         new Header() { Controller = "Campaigns", Text = "Back to Campaigns" } }) | ||||
| 
 | ||||
| <div class="container"> | ||||
|     <div class="card esh-campaigns-items"> | ||||
|         <img class="card-img-top" src="@Model.PictureUri" alt="Card image cap"> | ||||
|         <div class="card-block"> | ||||
|             <h4 class="card-title">@Model.Name</h4> | ||||
|             <p class="card-text">@Model.Description</p> | ||||
|             <p class="card-text"> | ||||
|                 <small class="text-muted"> | ||||
|                     From @Model.From.ToString("MMMM dd, yyyy") until @Model.To.ToString("MMMM dd, yyyy") | ||||
|                 </small> | ||||
|             </p> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
							
								
								
									
										36
									
								
								src/Web/WebMVC/Views/Campaigns/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/Web/WebMVC/Views/Campaigns/Index.cshtml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| @{ | ||||
|     ViewData["Title"] = "Campaigns"; | ||||
|     @model IEnumerable<Campaign> | ||||
| } | ||||
| 
 | ||||
| <section class="esh-campaigns-hero"> | ||||
|     <div class="container"> | ||||
|         <img class="esh-campaigns-title" src="~/images/main_banner_text.png" /> | ||||
|     </div> | ||||
| </section> | ||||
| 
 | ||||
|     @Html.Partial("_Header", new List<Header>() { | ||||
|             new Header() { Controller = "Catalog", Text = "Back to catalog" } }) | ||||
| 
 | ||||
| <div class="container"> | ||||
|     <div class="card-group esh-campaigns-items row"> | ||||
|             @foreach (var campaign in Model ?? Enumerable.Empty<Campaign>()) | ||||
|             { | ||||
|                 <div class="esh-campaigns-item col-md-4"> | ||||
|                     <form asp-controller="Campaigns" asp-action="Details" asp-route-id="@campaign.Id"> | ||||
|                         <div class="card-block"> | ||||
|                             <h4 class="card-title esh-campaigns-name">@campaign.Name</h4> | ||||
|                             <img class="card-img-top esh-campaigns-thumbnail" src="@campaign.PictureUri" alt="@campaign.Name"> | ||||
|                         <input class="esh-campaigns-button" type="submit" value="More details"> | ||||
|                         </div> | ||||
|                         <div class="card-footer"> | ||||
| 
 | ||||
|                             <small class="text-muted"> | ||||
|                                 From @campaign.From.ToString("MMMM dd, yyyy") until @campaign.To.ToString("MMMM dd, yyyy") | ||||
|                             </small> | ||||
|                         </div>  | ||||
|                     </form> | ||||
|                 </div> | ||||
|             } | ||||
|         </div> | ||||
| </div> | ||||
| @ -13,6 +13,7 @@ | ||||
|         <link rel="stylesheet" href="~/css/shared/components/identity/identity.css" /> | ||||
|         <link rel="stylesheet" href="~/css/shared/components/pager/pager.css" /> | ||||
|         <link rel="stylesheet" href="~/css/basket/basket.component.css" /> | ||||
|         <link rel="stylesheet" href="~/css/campaigns/campaigns.component.css" /> | ||||
|         <link rel="stylesheet" href="~/css/basket/basket-status/basket-status.component.css" /> | ||||
|         <link rel="stylesheet" href="~/css/catalog/catalog.component.css" /> | ||||
|         <link rel="stylesheet" href="~/css/orders/orders.component.css" /> | ||||
|  | ||||
| @ -26,6 +26,14 @@ | ||||
|                         <img class="esh-identity-image" src="~/images/my_orders.png"> | ||||
|                     </a> | ||||
| 
 | ||||
|                     <a class="esh-identity-item" | ||||
|                        asp-controller="Campaigns" | ||||
|                        asp-action="Index"> | ||||
| 
 | ||||
|                         <div class="esh-identity-name esh-identity-name--upper">Campaigns</div> | ||||
|                         <img class="esh-identity-image" src="~/images/my_orders.png"> | ||||
|                     </a> | ||||
| 
 | ||||
|                     <a class="esh-identity-item" | ||||
|                        href="javascript:document.getElementById('logoutForm').submit()"> | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,18 @@ | ||||
|     <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | ||||
|   </PropertyGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Content Remove="wwwroot/css\campaigns\catalog.component.css" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Content Include="wwwroot\css\campaigns\campaigns.component.css" /> | ||||
|     <Content Include="wwwroot\css\campaigns\orders.component.css" /> | ||||
|     <Content Include="wwwroot\css\catalog\orders.component.css"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <!--<ItemGroup> | ||||
|     <Compile Remove="wwwroot\lib\bootstrap\**" /> | ||||
|     <Content Remove="wwwroot\lib\bootstrap\**" /> | ||||
| @ -67,6 +79,7 @@ | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Folder Include="Views\Campaigns\" /> | ||||
|     <Folder Include="wwwroot\lib\" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
|   "CatalogUrl": "http://localhost:5101", | ||||
|   "OrderingUrl": "http://localhost:5102", | ||||
|   "BasketUrl": "http://localhost:5103", | ||||
|   "MarketingUrl": "http://localhost:5110", | ||||
|   "IdentityUrl": "http://localhost:5105", | ||||
|   "CallBackUrl": "http://localhost:5100/", | ||||
|   "IsClusterEnv": "False", | ||||
|  | ||||
							
								
								
									
										98
									
								
								src/Web/WebMVC/wwwroot/css/campaigns/campaigns.component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/Web/WebMVC/wwwroot/css/campaigns/campaigns.component.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | ||||
| .esh-campaigns-hero { | ||||
|     background-image: url("../../images/main_banner.png"); | ||||
|     background-size: cover; | ||||
|     height: 260px; | ||||
|     width: 100%; | ||||
| } | ||||
| 
 | ||||
| .esh-campaigns-title { | ||||
|     position: relative; | ||||
|     top: 74.28571px; | ||||
| } | ||||
| 
 | ||||
|     .esh-campaigns-label::before { | ||||
|         color: rgba(255, 255, 255, 0.5); | ||||
|         content: attr(data-title); | ||||
|         font-size: 0.65rem; | ||||
|         margin-top: 0.65rem; | ||||
|         margin-left: 0.5rem; | ||||
|         position: absolute; | ||||
|         text-transform: uppercase; | ||||
|         z-index: 1; | ||||
|     } | ||||
| 
 | ||||
|     .esh-campaigns-label::after { | ||||
|         background-image: url("../../images/arrow-down.png"); | ||||
|         height: 7px; | ||||
|         content: ''; | ||||
|         position: absolute; | ||||
|         right: 1.5rem; | ||||
|         top: 2.5rem; | ||||
|         width: 10px; | ||||
|         z-index: 1; | ||||
|     } | ||||
| 
 | ||||
| .esh-campaigns-items { | ||||
|     margin-top: 1rem; | ||||
| } | ||||
| 
 | ||||
| .esh-campaigns-item { | ||||
|     text-align: center; | ||||
|     margin-bottom: 1.5rem; | ||||
|     width: 33%; | ||||
|     display: inline-block; | ||||
|     float: none !important; | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 1024px) { | ||||
|     .esh-campaigns-item { | ||||
|         width: 50%; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 768px) { | ||||
|     .esh-campaigns-item { | ||||
|         width: 100%; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .esh-campaigns-thumbnail { | ||||
|     max-width: 370px; | ||||
|     width: 100%; | ||||
| } | ||||
| 
 | ||||
| .esh-campaigns-button { | ||||
|     background-color: #83D01B; | ||||
|     border: none; | ||||
|     color: #FFFFFF; | ||||
|     cursor: pointer; | ||||
|     font-size: 1rem; | ||||
|     height: 3rem; | ||||
|     margin-top: 1rem; | ||||
|     transition: all 0.35s; | ||||
|     width: 80%; | ||||
| } | ||||
|     .esh-campaigns-button.is-disabled { | ||||
|         opacity: .5; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| 
 | ||||
|     .esh-campaigns-button:hover { | ||||
|         background-color: #4a760f; | ||||
|         transition: all 0.35s; | ||||
|     } | ||||
| 
 | ||||
| .esh-campaigns-name { | ||||
|     font-size: 1rem; | ||||
|     font-weight: 300; | ||||
|     margin-top: .5rem; | ||||
|     text-align: center; | ||||
|     text-transform: uppercase; | ||||
| } | ||||
| 
 | ||||
| .esh-campaigns-description { | ||||
|     text-align: center; | ||||
|     font-weight: 300; | ||||
|     font-size: 14px; | ||||
| } | ||||
| 
 | ||||
| @ -29,7 +29,7 @@ | ||||
| 
 | ||||
| .esh-identity-drop { | ||||
|     background: #FFFFFF; | ||||
|     height: 0; | ||||
|     height: 0rem; | ||||
|     min-width: 14rem; | ||||
|     right: 0; | ||||
|     overflow: hidden; | ||||
| @ -41,7 +41,7 @@ | ||||
| 
 | ||||
| .esh-identity:hover .esh-identity-drop { | ||||
|     border: 1px solid #EEEEEE; | ||||
|     height: 7rem; | ||||
|     height: 9.5rem; | ||||
|     transition: height 0.35s; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -117,10 +117,11 @@ | ||||
|         { | ||||
|             return new CampaignDTO() | ||||
|             { | ||||
|                 Name = "FakeCampaignName", | ||||
|                 Description = "FakeCampaignDescription", | ||||
|                 From = DateTime.Now, | ||||
|                 To = DateTime.Now.AddDays(7), | ||||
|                 Url = "http://CampaignUrl.test/fdaf91ad0cef5419719f50198", | ||||
|                 PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/campaigns/0/pic" | ||||
|             };  | ||||
|         } | ||||
|     } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user