Skip to content

Commit

Permalink
Update endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
ozkank committed Jul 3, 2024
1 parent 1ec4402 commit 3877303
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 77 deletions.
123 changes: 56 additions & 67 deletions UrlShortener.ApiService/Features/RedirectUrl.cs
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);
});
}
}
}
15 changes: 9 additions & 6 deletions UrlShortener.ApiService/Features/ShortenUrl.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using Carter;
using FluentValidation;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using System;
using UrlShortener.ApiService.Domain;
using UrlShortener.ApiService.Infrastructure.Database;
using UrlShortener.ApiService.Shared;
Expand Down Expand Up @@ -32,14 +30,16 @@ internal sealed class Handler : IRequestHandler<Command, Result<string>>
public const int NumberOfCharsInShortLink = 7;
private const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private readonly Random _random = new();
private readonly IHttpContextAccessor _httpContextAccessor;

public Handler(ApplicationDbContext dbContext, IValidator<Command> validator)
public Handler(ApplicationDbContext dbContext, IValidator<Command> validator, IHttpContextAccessor httpContextAccessor)
{
_dbContext = dbContext;
_validator = validator;
_httpContextAccessor = httpContextAccessor;
}

public async Task<Result<string>> Handle(Command request, CancellationToken cancellationToken)
public async Task<Result<string>> Handle(Command request, CancellationToken cancellationToken)
{
var validationResult = _validator.Validate(request);
if (!validationResult.IsValid)
Expand All @@ -51,12 +51,15 @@ public async Task<Result<string>> Handle(Command request, CancellationToken can

var code = await GenerateUniqueCode();

var httpContext = _httpContextAccessor.HttpContext;
var shortUrl = $"{httpContext.Request.Scheme}://{httpContext.Request.Host}/api/{code}";

var shortenedUrl = new ShortenedUrl
{
Id = Guid.NewGuid(),
LongUrl = request.LongUrl,
Code = code,
//ShortUrl = $"{httpContext.Request.Scheme}://{httpContext.Request.Host}/api/{code}",
ShortUrl = shortUrl,
CreatedOnUtc = DateTime.UtcNow
};

Expand Down Expand Up @@ -95,7 +98,7 @@ public class ShortenUrlEndpoint : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
app.MapPost("api/articles", async (ShortenUrl.Command request, ISender sender) =>
app.MapPost("api/shorten", async (ShortenUrl.Command request, ISender sender) =>
{
var result = await sender.Send(request);

Expand Down
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 UrlShortener.ApiService/Migrations/20240702143044_Init.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions UrlShortener.ApiService/Migrations/20240702143044_Init.cs
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");
}
}
}
Loading

0 comments on commit 3877303

Please sign in to comment.