Browse Source

Initial event detail page with url system

pull/15/head
Halil İbrahim Kalkan 6 years ago
parent
commit
48502a729c
  1. 30
      eventhub/src/EventHub.Application.Contracts/Events/EventDetailDto.cs
  2. 4
      eventhub/src/EventHub.Application.Contracts/Events/EventDto.cs
  3. 8
      eventhub/src/EventHub.Application.Contracts/Events/EventInListDto.cs
  4. 2
      eventhub/src/EventHub.Application.Contracts/Events/IEventAppService.cs
  5. 8
      eventhub/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs
  6. 28
      eventhub/src/EventHub.Application/Events/EventAppService.cs
  7. 4
      eventhub/src/EventHub.Domain.Shared/Events/EventConsts.cs
  8. 15
      eventhub/src/EventHub.Domain.Shared/Events/EventUrlCodeHelper.cs
  9. 7
      eventhub/src/EventHub.Domain/Events/Event.cs
  10. 26
      eventhub/src/EventHub.Domain/Events/EventManager.cs
  11. 45
      eventhub/src/EventHub.Domain/Events/EventUrlCodeGenerator.cs
  12. 35
      eventhub/src/EventHub.Domain/Events/EventUrlHelper.cs
  13. 2368
      eventhub/src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210124103656_Added_Url_To_Event.Designer.cs
  14. 46
      eventhub/src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210124103656_Added_Url_To_Event.cs
  15. 12
      eventhub/src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/EventHubMigrationsDbContextModelSnapshot.cs
  16. 3
      eventhub/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubDbContextModelCreatingExtensions.cs
  17. 6
      eventhub/src/EventHub.Web/Pages/Events/Detail.cshtml
  18. 29
      eventhub/src/EventHub.Web/Pages/Events/Detail.cshtml.cs
  19. 5
      eventhub/src/EventHub.Web/Pages/Events/New.cshtml.cs
  20. 2
      eventhub/src/EventHub.Web/Pages/Index.cshtml
  21. 17
      eventhub/test/EventHub.Application.Tests/Events/EventAppServiceTests.cs
  22. 1
      eventhub/test/EventHub.Domain.Tests/Events/EventTests.cs
  23. 6
      eventhub/test/EventHub.TestBase/EventHubTestData.cs
  24. 2
      eventhub/test/EventHub.TestBase/EventHubTestDataSeedContributor.cs

30
eventhub/src/EventHub.Application.Contracts/Events/EventDetailDto.cs

@ -0,0 +1,30 @@
using System;
using Volo.Abp.Application.Dtos;
namespace EventHub.Events
{
public class EventDetailDto : EntityDto<Guid>
{
public string OrganizationName { get; set; }
public string OrganizationDisplayName { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public bool IsOnline { get; set; }
public bool IsLive { get; set; }
public string UrlCode { get; set; }
public string Url { get; set; }
public int? Capacity { get; set; }
}
}

4
eventhub/src/EventHub.Application.Contracts/Events/EventDto.cs

@ -7,6 +7,10 @@ namespace EventHub.Events
{
public Guid OrganizationId { get; set; }
public string UrlCode { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public DateTime StartTime { get; set; }

8
eventhub/src/EventHub.Application.Contracts/Events/EventInListDto.cs

@ -19,6 +19,12 @@ namespace EventHub.Events
public bool IsOnline { get; set; }
public bool IsLive { get; set; }
public int? Capacity { get; set; }
public bool IsLiveNow { get; set; }
public string UrlCode { get; set; }
public string Url { get; set; }
}
}

2
eventhub/src/EventHub.Application.Contracts/Events/IEventAppService.cs

@ -9,5 +9,7 @@ namespace EventHub.Events
Task<EventDto> CreateAsync(CreateEventDto input);
Task<PagedResultDto<EventInListDto>> GetListAsync(EventListFilterDto input);
Task<EventDetailDto> GetByUrlCodeAsync(string urlCode);
}
}

8
eventhub/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs

@ -1,6 +1,7 @@
using AutoMapper;
using EventHub.Events;
using EventHub.Organizations;
using Volo.Abp.AutoMapper;
namespace EventHub
{
@ -12,6 +13,13 @@ namespace EventHub
CreateMap<Organization, OrganizationProfileDto>();
CreateMap<Event, EventDto>();
CreateMap<Event, EventInListDto>()
.Ignore(x => x.OrganizationName)
.Ignore(x => x.OrganizationDisplayName)
.Ignore(x => x.IsLiveNow);
CreateMap<Event, EventDetailDto>()
.Ignore(x => x.OrganizationName)
.Ignore(x => x.OrganizationDisplayName);
}
}
}

28
eventhub/src/EventHub.Application/Events/EventAppService.cs

@ -79,20 +79,30 @@ namespace EventHub.Events
var now = Clock.Now;
var dtos = items.Select(
i => new EventInListDto
i =>
{
Id = i.@event.Id,
Title = i.@event.Title,
EndTime = i.@event.EndTime,
StartTime = i.@event.StartTime,
IsOnline = i.@event.IsOnline,
OrganizationName = i.organization.Name,
OrganizationDisplayName = i.organization.DisplayName,
IsLive = now.IsBetween(i.@event.StartTime, i.@event.EndTime)
var dto = ObjectMapper.Map<Event, EventInListDto>(i.@event);
dto.OrganizationName = i.organization.Name;
dto.OrganizationDisplayName = i.organization.DisplayName;
dto.IsLiveNow = now.IsBetween(i.@event.StartTime, i.@event.EndTime);
return dto;
}
).ToList();
return new PagedResultDto<EventInListDto>(totalCount, dtos);
}
public async Task<EventDetailDto> GetByUrlCodeAsync(string urlCode)
{
var @event = await _eventRepository.GetAsync(x => x.UrlCode == urlCode);
var organization = await _organizationRepository.GetAsync(@event.OrganizationId);
var dto = ObjectMapper.Map<Event, EventDetailDto>(@event);
dto.OrganizationName = organization.Name;
dto.OrganizationDisplayName = organization.DisplayName;
return dto;
}
}
}

4
eventhub/src/EventHub.Domain.Shared/Events/EventConsts.cs

@ -2,6 +2,10 @@
{
public static class EventConsts
{
public const int UrlCodeLength = 8;
public const int MaxTitleInUrlLength = 60;
public const int MaxUrlLength = MaxTitleInUrlLength + 1 + UrlCodeLength; //Format: {Title}-{UrlCode}
public const int MinTitleLength = 8;
public const int MaxTitleLength = 128;

15
eventhub/src/EventHub.Domain.Shared/Events/EventUrlCodeHelper.cs

@ -0,0 +1,15 @@
using System;
using Volo.Abp;
namespace EventHub.Events
{
public static class EventUrlCodeHelper
{
public static string GetCodeFromUrl(string url)
{
Check.NotNullOrEmpty(url, nameof(url), minLength: EventConsts.UrlCodeLength);
return url.Right(EventConsts.UrlCodeLength);
}
}
}

7
eventhub/src/EventHub.Domain/Events/Event.cs

@ -8,6 +8,10 @@ namespace EventHub.Events
{
public Guid OrganizationId { get; private set; }
public string UrlCode { get; private set; }
public string Url { get; private set; }
public string Title { get; private set; }
public DateTime StartTime { get; private set; }
@ -28,6 +32,7 @@ namespace EventHub.Events
internal Event(
Guid id,
Guid organizationId,
string urlCode,
string title,
DateTime startTime,
DateTime endTime,
@ -35,6 +40,7 @@ namespace EventHub.Events
: base(id)
{
OrganizationId = organizationId;
UrlCode = Check.NotNullOrWhiteSpace(urlCode, urlCode, EventConsts.UrlCodeLength, EventConsts.UrlCodeLength);
SetTitle(title);
SetDescription(description);
SetTime(startTime, endTime);
@ -43,6 +49,7 @@ namespace EventHub.Events
public Event SetTitle(string title)
{
Title = Check.NotNullOrWhiteSpace(title, nameof(title), EventConsts.MaxTitleLength, EventConsts.MinTitleLength);
Url = EventUrlHelper.ConvertTitleToUrlPart(Title) + "-" + UrlCode;
return this;
}

26
eventhub/src/EventHub.Domain/Events/EventManager.cs

@ -7,22 +7,28 @@ namespace EventHub.Events
{
public class EventManager : DomainService
{
public ValueTask<Event> CreateAsync(
private readonly EventUrlCodeGenerator _eventUrlCodeGenerator;
public EventManager(EventUrlCodeGenerator eventUrlCodeGenerator)
{
_eventUrlCodeGenerator = eventUrlCodeGenerator;
}
public async Task<Event> CreateAsync(
Organization organization,
string title,
DateTime startTime,
DateTime endTime,
string description)
{
return ValueTask.FromResult(
new Event(
GuidGenerator.Create(),
organization.Id,
title,
startTime,
endTime,
description
)
return new Event(
GuidGenerator.Create(),
organization.Id,
await _eventUrlCodeGenerator.GenerateAsync(),
title,
startTime,
endTime,
description
);
}
}

45
eventhub/src/EventHub.Domain/Events/EventUrlCodeGenerator.cs

@ -0,0 +1,45 @@
using System;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace EventHub.Events
{
public class EventUrlCodeGenerator : ITransientDependency
{
private static string UrlCodeChars = "abcdefghijklmnopqrstuvwxyz0123456789";
private readonly IRepository<Event, Guid> _eventRepository;
public EventUrlCodeGenerator(IRepository<Event, Guid> eventRepository)
{
_eventRepository = eventRepository;
}
public async Task<string> GenerateAsync()
{
string urlCode;
do
{
urlCode = GenerateRandomCodeAsync();
} while (await _eventRepository.AnyAsync(x => x.UrlCode == urlCode));
return urlCode;
}
private string GenerateRandomCodeAsync()
{
var sb = new StringBuilder();
for (var i = 0; i < EventConsts.UrlCodeLength; i++)
{
sb.Append(UrlCodeChars[RandomHelper.GetRandom(0, UrlCodeChars.Length)]);
}
return sb.ToString();
}
}
}

35
eventhub/src/EventHub.Domain/Events/EventUrlHelper.cs

@ -0,0 +1,35 @@
using System;
using System.Text;
namespace EventHub.Events
{
internal static class EventUrlHelper
{
private static string AllowedUrlChars = "abcdefghijklmnopqrstuvwxyz0123456789";
public static string ConvertTitleToUrlPart(string title)
{
var normalizedTitle = title
.Replace(' ', '-')
.ToKebabCase()
.ToLowerInvariant();
var urlPartBuilder = new StringBuilder();
foreach (var c in normalizedTitle)
{
if (AllowedUrlChars.Contains(c))
{
urlPartBuilder.Append(c);
}
if (urlPartBuilder.Length >= EventConsts.MaxTitleInUrlLength)
{
break;
}
}
return urlPartBuilder.ToString();
}
}
}

2368
eventhub/src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210124103656_Added_Url_To_Event.Designer.cs

File diff suppressed because it is too large

46
eventhub/src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/20210124103656_Added_Url_To_Event.cs

@ -0,0 +1,46 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace EventHub.Migrations
{
public partial class Added_Url_To_Event : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Url",
table: "AppEvents",
type: "nvarchar(69)",
maxLength: 69,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "UrlCode",
table: "AppEvents",
type: "nvarchar(8)",
maxLength: 8,
nullable: false,
defaultValue: "");
migrationBuilder.CreateIndex(
name: "IX_AppEvents_UrlCode",
table: "AppEvents",
column: "UrlCode");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "IX_AppEvents_UrlCode",
table: "AppEvents");
migrationBuilder.DropColumn(
name: "Url",
table: "AppEvents");
migrationBuilder.DropColumn(
name: "UrlCode",
table: "AppEvents");
}
}
}

12
eventhub/src/EventHub.EntityFrameworkCore.DbMigrations/Migrations/EventHubMigrationsDbContextModelSnapshot.cs

@ -92,10 +92,22 @@ namespace EventHub.Migrations
.HasMaxLength(128)
.HasColumnType("nvarchar(128)");
b.Property<string>("Url")
.IsRequired()
.HasMaxLength(69)
.HasColumnType("nvarchar(69)");
b.Property<string>("UrlCode")
.IsRequired()
.HasMaxLength(8)
.HasColumnType("nvarchar(8)");
b.HasKey("Id");
b.HasIndex("StartTime");
b.HasIndex("UrlCode");
b.HasIndex("OrganizationId", "StartTime");
b.ToTable("AppEvents");

3
eventhub/src/EventHub.EntityFrameworkCore/EntityFrameworkCore/EventHubDbContextModelCreatingExtensions.cs

@ -36,9 +36,12 @@ namespace EventHub.EntityFrameworkCore
b.Property(x => x.Title).IsRequired().HasMaxLength(EventConsts.MaxTitleLength);
b.Property(x => x.Description).IsRequired().HasMaxLength(EventConsts.MaxDescriptionLength);
b.Property(x => x.UrlCode).IsRequired().HasMaxLength(EventConsts.UrlCodeLength);
b.Property(x => x.Url).IsRequired().HasMaxLength(EventConsts.MaxUrlLength);
b.HasIndex(x => new {x.OrganizationId, x.StartTime});
b.HasIndex(x => x.StartTime);
b.HasIndex(x => x.UrlCode);
});
}
}

6
eventhub/src/EventHub.Web/Pages/Events/Detail.cshtml

@ -0,0 +1,6 @@
@page "/events/{url}"
@model EventHub.Web.Pages.Events.Detail
<h1>@Model.Event.Title</h1>
<p>
@Model.Event.Description
</p>

29
eventhub/src/EventHub.Web/Pages/Events/Detail.cshtml.cs

@ -0,0 +1,29 @@
using System.Threading.Tasks;
using EventHub.Events;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EventHub.Web.Pages.Events
{
public class Detail : PageModel
{
[BindProperty(SupportsGet = true)]
public string Url { get; set; }
public EventDetailDto Event { get; set; }
private readonly IEventAppService _eventAppService;
public Detail(IEventAppService eventAppService)
{
_eventAppService = eventAppService;
}
public async Task OnGetAsync()
{
var urlCode = EventUrlCodeHelper.GetCodeFromUrl(Url);
Event = await _eventAppService.GetByUrlCodeAsync(urlCode);
}
}
}

5
eventhub/src/EventHub.Web/Pages/Events/New.cshtml.cs

@ -60,10 +60,9 @@ namespace EventHub.Web.Pages.Events
ValidateModel();
var input = ObjectMapper.Map<NewEventViewModel, CreateEventDto>(Event);
await _eventAppService.CreateAsync(input);
var eventDto = await _eventAppService.CreateAsync(input);
//TODO: Redirect to the event!
return RedirectToPage("/");
return RedirectToPage("/Events/Detail", new {url = eventDto.UrlCode});
}
catch (Exception exception)
{

2
eventhub/src/EventHub.Web/Pages/Index.cshtml

@ -30,7 +30,7 @@
<abp-card-text>
@eventItem.Description.TruncateWithPostfix(100)
</abp-card-text>
<a abp-button="Primary" href="#"> @L["SeeEvent"]</a>
<a abp-button="Primary" href="@Url.Page("/Events/Detail", new { url = eventItem.Url })"> @L["SeeEvent"]</a>
</abp-card-body>
</abp-card>
</abp-column>

17
eventhub/test/EventHub.Application.Tests/Events/EventAppServiceTests.cs

@ -37,6 +37,7 @@ namespace EventHub.Events
eventDto.Title.ShouldBe("Introduction to the ABP Framework");
eventDto.Description.ShouldBe("In this event, we will introduce the ABP Framework and explore the fundamental features.");
eventDto.IsOnline.ShouldBeTrue();
eventDto.UrlCode.ShouldNotBeNullOrWhiteSpace();
}
[Fact]
@ -75,7 +76,8 @@ namespace EventHub.Events
result.TotalCount.ShouldBeGreaterThanOrEqualTo(1);
result.Items.ShouldContain(x =>
x.Id == _testData.AbpMicroservicesFutureEventId &&
x.Title == _testData.AbpMicroservicesFutureEventTitle
x.Title == _testData.AbpMicroservicesFutureEventTitle &&
x.UrlCode == _testData.AbpMicroservicesFutureEventUrlCode
);
result.Items.ShouldAllBe(x => x.EndTime >= now);
}
@ -95,9 +97,20 @@ namespace EventHub.Events
result.TotalCount.ShouldBeGreaterThanOrEqualTo(1);
result.Items.ShouldContain(x =>
x.Id == _testData.AbpBlazorPastEventId &&
x.Title == _testData.AbpBlazorPastEventTitle
x.Title == _testData.AbpBlazorPastEventTitle &&
x.UrlCode == _testData.AbpBlazorPastEventUrlCode
);
result.Items.ShouldAllBe(x => x.EndTime <= now);
}
[Fact]
public async Task Should_Get_By_UrlCode()
{
var eventDetailDto = await _eventAppService.GetByUrlCodeAsync(_testData.AbpBlazorPastEventUrlCode);
eventDetailDto.Id.ShouldBe(_testData.AbpBlazorPastEventId);
eventDetailDto.Title.ShouldBe(_testData.AbpBlazorPastEventTitle);
eventDetailDto.UrlCode.ShouldBe(_testData.AbpBlazorPastEventUrlCode);
}
}
}

1
eventhub/test/EventHub.Domain.Tests/Events/EventTests.cs

@ -15,6 +15,7 @@ namespace EventHub.Events
new Event(
Guid.NewGuid(),
Guid.NewGuid(),
"1a8j3v0d",
"Introduction to the ABP Framework",
DateTime.Now,
DateTime.Now.AddDays(-2),

6
eventhub/test/EventHub.TestBase/EventHubTestData.cs

@ -16,9 +16,11 @@ namespace EventHub
/* EVENTS ************************************************************/
public Guid AbpBlazorPastEventId { get; } = Guid.NewGuid();
public string AbpBlazorPastEventTitle { get; internal set; } = "ABP Framework Blazor UI Introduction";
public string AbpBlazorPastEventTitle { get; } = "ABP Framework Blazor UI Introduction";
public string AbpBlazorPastEventUrlCode { get; } = "00000001";
public Guid AbpMicroservicesFutureEventId { get;} = Guid.NewGuid();
public string AbpMicroservicesFutureEventTitle { get; internal set; } = "ABP Framework Microservice Solution Development";
public string AbpMicroservicesFutureEventTitle { get; } = "ABP Framework Microservice Solution Development";
public string AbpMicroservicesFutureEventUrlCode { get; } = "00000002";
}
}

2
eventhub/test/EventHub.TestBase/EventHubTestDataSeedContributor.cs

@ -67,6 +67,7 @@ namespace EventHub
new Event(
_eventHubTestData.AbpBlazorPastEventId,
_eventHubTestData.OrganizationVolosoftId,
_eventHubTestData.AbpBlazorPastEventUrlCode,
_eventHubTestData.AbpBlazorPastEventTitle,
_clock.Now.ClearTime().AddDays(-2).AddHours(15),
_clock.Now.ClearTime().AddDays(-2).AddHours(17),
@ -78,6 +79,7 @@ namespace EventHub
new Event(
_eventHubTestData.AbpMicroservicesFutureEventId,
_eventHubTestData.OrganizationVolosoftId,
_eventHubTestData.AbpMicroservicesFutureEventUrlCode,
_eventHubTestData.AbpMicroservicesFutureEventTitle,
_clock.Now.ClearTime().AddDays(1).AddHours(15),
_clock.Now.ClearTime().AddDays(1).AddHours(17),

Loading…
Cancel
Save