-
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
Jul 3, 2024
1 parent
1ec4402
commit 3877303
Showing
10 changed files
with
309 additions
and
77 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 |
---|---|---|
@@ -1,79 +1,68 @@ | ||
//using Azure.Core; | ||
//using Carter; | ||
//using FluentValidation; | ||
//using MediatR; | ||
//using Microsoft.EntityFrameworkCore; | ||
//using UrlShortener.ApiService.Infrastructure.Database; | ||
//using UrlShortener.ApiService.Shared; | ||
using Azure.Core; | ||
using Carter; | ||
using FluentValidation; | ||
using MediatR; | ||
using Microsoft.EntityFrameworkCore; | ||
using UrlShortener.ApiService.Infrastructure.Database; | ||
using UrlShortener.ApiService.Shared; | ||
|
||
//namespace UrlShortener.ApiService.Features | ||
//{ | ||
// public class RedirectUrl | ||
// { | ||
// public class Query : IRequest<Result<RedirectUrlResponse>> | ||
// { | ||
// public string Code { get; set; } = string.Empty; | ||
// } | ||
namespace UrlShortener.ApiService.Features | ||
{ | ||
public class RedirectUrl | ||
{ | ||
public class Query : IRequest<Result<string>> | ||
{ | ||
public string Code { get; set; } = string.Empty; | ||
} | ||
|
||
// internal sealed class Handler : IRequestHandler<Query, Result<RedirectUrlResponse>> | ||
// { | ||
// private readonly ApplicationDbContext _context; | ||
// public const int NumberOfCharsInShortLink = 7; | ||
// private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||
internal sealed class Handler : IRequestHandler<Query, Result<string>> | ||
{ | ||
private readonly ApplicationDbContext _dbContext; | ||
public const int NumberOfCharsInShortLink = 7; | ||
private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | ||
|
||
// private readonly Random _random = new(); | ||
private readonly Random _random = new(); | ||
|
||
// public Handler(ApplicationDbContext context) | ||
// { | ||
// _context = context; | ||
// } | ||
public Handler(ApplicationDbContext dbContext) | ||
{ | ||
_dbContext = dbContext; | ||
} | ||
|
||
// public async Task<Result<RedirectUrlResponse>> Handle(Query request, CancellationToken cancellationToken) | ||
// { | ||
// var item = await _context | ||
// .ShortenedUrls | ||
// .Where(s => s.Code == request.Code) | ||
// .Select(x => new RedirectUrlResponse | ||
// { | ||
// LongUrl = x.LongUrl, | ||
// }) | ||
// .FirstOrDefaultAsync(cancellationToken); | ||
public async Task<Result<string>> Handle(Query request, CancellationToken cancellationToken) | ||
{ | ||
var shortenedUrl = await _dbContext.ShortenedUrls | ||
.FirstOrDefaultAsync(s => s.Code == request.Code); | ||
|
||
// if (item is null) | ||
// { | ||
// return Result.Failure<RedirectUrlResponse>(new Error( | ||
// "RedirectUrlResponse.Null", | ||
// "The longUrl with the specified shortUrl was not found")); | ||
// } | ||
if (shortenedUrl is null) | ||
{ | ||
return Result.Failure<string>(new Error( | ||
"RedirectUrlResponse.Null", | ||
"The longUrl with the specified shortUrl was not found")); | ||
} | ||
|
||
// return item; | ||
// } | ||
// } | ||
return shortenedUrl.LongUrl; | ||
} | ||
} | ||
|
||
// } | ||
} | ||
|
||
// public class RedirectUrlEndpoint : ICarterModule | ||
// { | ||
// public void AddRoutes(IEndpointRouteBuilder app) | ||
// { | ||
// app.MapGet("api/redirect/{shortUrl}", async (string shortUrl, ISender sender) => | ||
// { | ||
// var query = new RedirectUrl.Query { Code = shortUrl }; | ||
public class RedirectUrlEndpoint : ICarterModule | ||
{ | ||
public void AddRoutes(IEndpointRouteBuilder app) | ||
{ | ||
app.MapGet("api/{code}", async (string code, ISender sender) => | ||
{ | ||
var query = new RedirectUrl.Query { Code = code }; | ||
|
||
// var result = await sender.Send(query); | ||
var result = await sender.Send(query); | ||
|
||
// if (result.IsFailure) | ||
// { | ||
// return Results.NotFound(result.Error); | ||
// } | ||
if (result.IsFailure) | ||
{ | ||
return Results.NotFound(result.Error); | ||
} | ||
|
||
// return Results.Ok(result.Value); | ||
// }); | ||
// } | ||
// } | ||
|
||
// public class RedirectUrlResponse | ||
// { | ||
// public string LongUrl { get; set; } = string.Empty; | ||
// } | ||
//} | ||
return Results.Redirect(result.Value); | ||
}); | ||
} | ||
} | ||
} |
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
70 changes: 70 additions & 0 deletions
70
UrlShortener.ApiService/Infrastructure/Database/ApiDbInitializer.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,70 @@ | ||
using Microsoft.EntityFrameworkCore.Storage; | ||
using System.Diagnostics; | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using Microsoft.Data.SqlClient; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.EntityFrameworkCore.Infrastructure; | ||
using Microsoft.EntityFrameworkCore.Storage; | ||
using Microsoft.Extensions.Logging; | ||
using OpenTelemetry.Trace; | ||
|
||
namespace UrlShortener.ApiService.Infrastructure.Database | ||
{ | ||
public class ApiDbInitializer( | ||
IServiceProvider serviceProvider, | ||
IHostApplicationLifetime hostApplicationLifetime) : BackgroundService | ||
{ | ||
public const string ActivitySourceName = "Migrations"; | ||
private static readonly ActivitySource s_activitySource = new(ActivitySourceName); | ||
|
||
protected override async Task ExecuteAsync(CancellationToken cancellationToken) | ||
{ | ||
using var activity = s_activitySource.StartActivity("Migrating database", ActivityKind.Client); | ||
|
||
try | ||
{ | ||
using var scope = serviceProvider.CreateScope(); | ||
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); | ||
|
||
await EnsureDatabaseAsync(dbContext, cancellationToken); | ||
await RunMigrationAsync(dbContext, cancellationToken); | ||
} | ||
catch (Exception ex) | ||
{ | ||
activity?.RecordException(ex); | ||
throw; | ||
} | ||
|
||
hostApplicationLifetime.StopApplication(); | ||
} | ||
|
||
private static async Task EnsureDatabaseAsync(ApplicationDbContext dbContext, CancellationToken cancellationToken) | ||
{ | ||
var dbCreator = dbContext.GetService<IRelationalDatabaseCreator>(); | ||
|
||
var strategy = dbContext.Database.CreateExecutionStrategy(); | ||
await strategy.ExecuteAsync(async () => | ||
{ | ||
// Create the database if it does not exist. | ||
// Do this first so there is then a database to start a transaction against. | ||
if (!await dbCreator.ExistsAsync(cancellationToken)) | ||
{ | ||
await dbCreator.CreateAsync(cancellationToken); | ||
} | ||
}); | ||
} | ||
|
||
private static async Task RunMigrationAsync(ApplicationDbContext dbContext, CancellationToken cancellationToken) | ||
{ | ||
var strategy = dbContext.Database.CreateExecutionStrategy(); | ||
await strategy.ExecuteAsync(async () => | ||
{ | ||
// Run migration in a transaction to avoid partial migration if it fails. | ||
await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken); | ||
await dbContext.Database.MigrateAsync(cancellationToken); | ||
await transaction.CommitAsync(cancellationToken); | ||
}); | ||
} | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
UrlShortener.ApiService/Migrations/20240702143044_Init.Designer.cs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,43 @@ | ||
using System; | ||
using Microsoft.EntityFrameworkCore.Migrations; | ||
|
||
#nullable disable | ||
|
||
namespace UrlShortener.ApiService.Migrations | ||
{ | ||
/// <inheritdoc /> | ||
public partial class Init : Migration | ||
{ | ||
/// <inheritdoc /> | ||
protected override void Up(MigrationBuilder migrationBuilder) | ||
{ | ||
migrationBuilder.CreateTable( | ||
name: "ShortenedUrls", | ||
columns: table => new | ||
{ | ||
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), | ||
LongUrl = table.Column<string>(type: "nvarchar(max)", nullable: false), | ||
ShortUrl = table.Column<string>(type: "nvarchar(max)", nullable: false), | ||
Code = table.Column<string>(type: "nvarchar(7)", maxLength: 7, nullable: false), | ||
CreatedOnUtc = table.Column<DateTime>(type: "datetime2", nullable: false) | ||
}, | ||
constraints: table => | ||
{ | ||
table.PrimaryKey("PK_ShortenedUrls", x => x.Id); | ||
}); | ||
|
||
migrationBuilder.CreateIndex( | ||
name: "IX_ShortenedUrls_Code", | ||
table: "ShortenedUrls", | ||
column: "Code", | ||
unique: true); | ||
} | ||
|
||
/// <inheritdoc /> | ||
protected override void Down(MigrationBuilder migrationBuilder) | ||
{ | ||
migrationBuilder.DropTable( | ||
name: "ShortenedUrls"); | ||
} | ||
} | ||
} |
Oops, something went wrong.