-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
ozkank
committed
Jun 14, 2024
1 parent
79fed99
commit 340801a
Showing
44 changed files
with
1,270 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
namespace UrlShortener.ApiService.Domain | ||
{ | ||
public class ShortenedUrl | ||
{ | ||
public Guid Id { get; set; } | ||
|
||
public string LongUrl { get; set; } = string.Empty; | ||
|
||
public string ShortUrl { get; set; } = string.Empty; | ||
|
||
public string Code { get; set; } = string.Empty; | ||
|
||
public DateTime CreatedOnUtc { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
using Carter; | ||
using MediatR; | ||
using Microsoft.EntityFrameworkCore; | ||
using UrlShortener.ApiService.Infrastructure.Database; | ||
|
||
namespace UrlShortener.ApiService.Features | ||
{ | ||
public class RedirectUrlQuery : IRequest<string> | ||
{ | ||
public string Code { get; set; } | ||
} | ||
|
||
internal sealed class RedirectUrlQueryHandler : | ||
IRequestHandler<RedirectUrlQuery, string> | ||
{ | ||
private readonly ApplicationDbContext _context; | ||
public const int NumberOfCharsInShortLink = 7; | ||
private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||
|
||
private readonly Random _random = new(); | ||
|
||
public RedirectUrlQueryHandler(ApplicationDbContext context) | ||
{ | ||
_context = context; | ||
} | ||
|
||
public async Task<string> Handle(RedirectUrlQuery request, CancellationToken cancellationToken) | ||
{ | ||
var shortenedUrl = await _context | ||
.ShortenedUrls | ||
.FirstOrDefaultAsync(s => s.Code == request.Code); | ||
|
||
if (shortenedUrl is null) | ||
{ | ||
//return Results.NotFound(); | ||
} | ||
|
||
return shortenedUrl.ShortUrl; | ||
} | ||
} | ||
|
||
|
||
public class RedirectUrlEndpoint : ICarterModule | ||
{ | ||
public void AddRoutes(IEndpointRouteBuilder app) | ||
{ | ||
app.MapGet("api/{code}", async (String code, ISender sender) => | ||
{ | ||
var query = new RedirectUrlQuery { Code = code }; | ||
var result = await sender.Send(query); | ||
|
||
return Results.Ok(result); | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
using Carter; | ||
using FluentValidation; | ||
using MediatR; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.EntityFrameworkCore; | ||
using System; | ||
using UrlShortener.ApiService.Domain; | ||
using UrlShortener.ApiService.Infrastructure.Database; | ||
|
||
namespace UrlShortener.ApiService.Features | ||
{ | ||
public class ShortenUrlQuery : IRequest<string> | ||
{ | ||
public string LongUrl { get; set; } | ||
} | ||
|
||
|
||
internal sealed class ShortenUrlQueryHandler : | ||
IRequestHandler<ShortenUrlQuery, string> | ||
{ | ||
private readonly ApplicationDbContext _context; | ||
public const int NumberOfCharsInShortLink = 7; | ||
private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||
|
||
private readonly Random _random = new(); | ||
|
||
public ShortenUrlQueryHandler(ApplicationDbContext context) | ||
{ | ||
_context = context; | ||
} | ||
|
||
public async Task<string> Handle(ShortenUrlQuery request, CancellationToken cancellationToken) | ||
{ | ||
if (!Uri.TryCreate(request.LongUrl, UriKind.Absolute, out _)) | ||
{ | ||
throw new Exception("The specified URL is invalid."); | ||
//return Results.BadRequest("The specified URL is invalid."); | ||
} | ||
|
||
var code = await GenerateUniqueCode(); | ||
|
||
var shortenedUrl = new ShortenedUrl | ||
{ | ||
Id = Guid.NewGuid(), | ||
LongUrl = request.LongUrl, | ||
Code = code, | ||
//ShortUrl = $"{_context.Request.Scheme}://{_context.Request.Host}/api/{code}", | ||
CreatedOnUtc = DateTime.UtcNow | ||
}; | ||
|
||
_context.ShortenedUrls.Add(shortenedUrl); | ||
|
||
await _context.SaveChangesAsync(); | ||
|
||
return shortenedUrl.ShortUrl; | ||
} | ||
|
||
public async Task<string> GenerateUniqueCode() | ||
{ | ||
var codeChars = new char[NumberOfCharsInShortLink]; | ||
|
||
while (true) | ||
{ | ||
for (var i = 0; i < NumberOfCharsInShortLink; i++) | ||
{ | ||
var randomIndex = _random.Next(Alphabet.Length - 1); | ||
|
||
codeChars[i] = Alphabet[randomIndex]; | ||
} | ||
|
||
var code = new string(codeChars); | ||
|
||
if (!await _context.ShortenedUrls.AnyAsync(s => s.Code == code)) | ||
{ | ||
return code; | ||
} | ||
} | ||
} | ||
} | ||
|
||
public class ShortenUrlEndpoint : ICarterModule | ||
{ | ||
public void AddRoutes(IEndpointRouteBuilder app) | ||
{ | ||
app.MapGet("api/shorten/{longUrl}", async (String longUrl, ISender sender) => | ||
{ | ||
var query = new ShortenUrlQuery { LongUrl = longUrl }; | ||
var result = await sender.Send(query); | ||
|
||
return Results.Ok(result); | ||
}); | ||
} | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
UrlShortener.ApiService/Infrastructure/Database/ApplicationDbContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using System.Collections.Generic; | ||
using System.Reflection.Emit; | ||
using UrlShortener.ApiService.Domain; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.EntityFrameworkCore.ChangeTracking; | ||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; | ||
using System.Reflection; | ||
|
||
namespace UrlShortener.ApiService.Infrastructure.Database | ||
{ | ||
public class ApplicationDbContext : DbContext | ||
{ | ||
public ApplicationDbContext(DbContextOptions options) | ||
: base(options) | ||
{ | ||
} | ||
|
||
public DbSet<ShortenedUrl> ShortenedUrls { get; set; } | ||
|
||
protected override void OnModelCreating(ModelBuilder modelBuilder) | ||
{ | ||
modelBuilder.Entity<ShortenedUrl>(builder => | ||
{ | ||
builder | ||
.Property(shortenedUrl => shortenedUrl.Code) | ||
.HasMaxLength(ShortLinkSettings.Length); | ||
|
||
builder | ||
.HasIndex(shortenedUrl => shortenedUrl.Code) | ||
.IsUnique(); | ||
}); | ||
} | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
UrlShortener.ApiService/Infrastructure/Extensions/EndpointExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
using System.Reflection; | ||
using UrlShortener.ApiService.Infrastructure; | ||
|
||
namespace UrlShortener.ApiService.Infrastructure.Extensions | ||
{ | ||
public static class EndpointExtensions | ||
{ | ||
public static IServiceCollection AddEndpoints(this IServiceCollection services, Assembly assembly) | ||
{ | ||
ServiceDescriptor[] serviceDescriptors = assembly | ||
.DefinedTypes | ||
.Where(type => type is { IsAbstract: false, IsInterface: false } && | ||
type.IsAssignableTo(typeof(IEndpoint))) | ||
.Select(type => ServiceDescriptor.Transient(typeof(IEndpoint), type)) | ||
.ToArray(); | ||
|
||
services.TryAddEnumerable(serviceDescriptors); | ||
|
||
return services; | ||
} | ||
|
||
public static IApplicationBuilder MapEndpoints(this WebApplication app, RouteGroupBuilder? routeGroupBuilder = null) | ||
{ | ||
IEnumerable<IEndpoint> endpoints = app.Services.GetRequiredService<IEnumerable<IEndpoint>>(); | ||
|
||
IEndpointRouteBuilder builder = routeGroupBuilder is null ? app : routeGroupBuilder; | ||
|
||
foreach (IEndpoint endpoint in endpoints) | ||
{ | ||
endpoint.MapEndpoint(builder); | ||
} | ||
|
||
return app; | ||
} | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
UrlShortener.ApiService/Infrastructure/Extensions/MigrationExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using Microsoft.EntityFrameworkCore; | ||
using UrlShortener.ApiService.Infrastructure.Database; | ||
|
||
namespace UrlShortener.ApiService.Infrastructure.Extensions | ||
{ | ||
public static class MigrationExtensions | ||
{ | ||
public static void ApplyMigrations(this WebApplication app) | ||
{ | ||
using var scope = app.Services.CreateScope(); | ||
|
||
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); | ||
|
||
dbContext.Database.Migrate(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
namespace UrlShortener.ApiService.Infrastructure | ||
{ | ||
public interface IEndpoint | ||
{ | ||
void MapEndpoint(IEndpointRouteBuilder app); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using Carter; | ||
using FluentValidation; | ||
using Microsoft.EntityFrameworkCore; | ||
using UrlShortener.ApiService.Infrastructure.Database; | ||
using UrlShortener.ApiService.Infrastructure.Extensions; | ||
|
||
var builder = WebApplication.CreateBuilder(args); | ||
|
||
// Add service defaults & Aspire components. | ||
builder.AddServiceDefaults(); | ||
|
||
// Add services to the container. | ||
builder.Services.AddProblemDetails(); | ||
|
||
builder.Services.AddEndpointsApiExplorer(); | ||
builder.Services.AddSwaggerGen(); | ||
|
||
builder.Services.AddDbContext<ApplicationDbContext>(o => | ||
o.UseSqlServer(builder.Configuration.GetConnectionString("Database"))); | ||
|
||
builder.Services.AddEndpoints(typeof(Program).Assembly); | ||
|
||
|
||
//builder.Services.AddMediatR(config => config.RegisterServicesFromAssembly(typeof(Program).Assembly)); | ||
|
||
builder.Services.AddCarter(); | ||
|
||
builder.Services.AddValidatorsFromAssembly(typeof(Program).Assembly); | ||
|
||
var app = builder.Build(); | ||
|
||
// Configure the HTTP request pipeline. | ||
app.UseExceptionHandler(); | ||
|
||
app.MapEndpoints(); | ||
|
||
app.MapDefaultEndpoints(); | ||
|
||
if (app.Environment.IsDevelopment()) | ||
{ | ||
app.UseSwagger(); | ||
app.UseSwaggerUI(); | ||
app.ApplyMigrations(); | ||
} | ||
|
||
app.MapCarter(); | ||
|
||
app.UseHttpsRedirection(); | ||
|
||
app.Run(); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/launchsettings.json", | ||
"profiles": { | ||
"http": { | ||
"commandName": "Project", | ||
"dotnetRunMessages": true, | ||
"launchBrowser": true, | ||
"launchUrl": "swagger", | ||
"applicationUrl": "http://localhost:5385", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
} | ||
}, | ||
"https": { | ||
"commandName": "Project", | ||
"dotnetRunMessages": true, | ||
"launchBrowser": true, | ||
"launchUrl": "weatherforecast", | ||
"applicationUrl": "https://localhost:7549;http://localhost:5385", | ||
"environmentVariables": { | ||
"ASPNETCORE_ENVIRONMENT": "Development" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace UrlShortener.ApiService | ||
{ | ||
public static class ShortLinkSettings | ||
{ | ||
public const int Length = 7; | ||
public const string Alphabet = | ||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<Project Sdk="Microsoft.NET.Sdk.Web"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\UrlShortener.ServiceDefaults\UrlShortener.ServiceDefaults.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Carter" Version="7.1.0" /> | ||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.7.1" /> | ||
<PackageReference Include="Mapster" Version="7.3.0" /> | ||
<PackageReference Include="MediatR" Version="12.1.1" /> | ||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.10" /> | ||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10" /> | ||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" /> | ||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Folder Include="Shared\" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.