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