From b24878756b13f0cedf920acf8d19f98492dbbafb Mon Sep 17 00:00:00 2001 From: Rajeev Soni Date: Mon, 6 Jan 2020 13:18:16 +0530 Subject: [PATCH 1/2] schema changes and SHA generator function --- .../DBContext/URLShortenerDBContext.cs | 6 +---- ...200106074624_InitialMigration.Designer.cs} | 6 ++--- ....cs => 20200106074624_InitialMigration.cs} | 7 +----- .../URLShortenerDBContextModelSnapshot.cs | 4 +--- URLShortener/Services/URLShortenerService.cs | 11 ++++++++++ .../interfaces/IURLShortenerService.cs | 12 ++++++++++ URLShortener/Utils/HashGenerator.cs | 22 +++++++++++++++++++ 7 files changed, 50 insertions(+), 18 deletions(-) rename URLShortener.Data/Migrations/{20191213094430_InitialMigration.Designer.cs => 20200106074624_InitialMigration.Designer.cs} (92%) rename URLShortener.Data/Migrations/{20191213094430_InitialMigration.cs => 20200106074624_InitialMigration.cs} (84%) create mode 100644 URLShortener/Services/URLShortenerService.cs create mode 100644 URLShortener/Services/interfaces/IURLShortenerService.cs create mode 100644 URLShortener/Utils/HashGenerator.cs diff --git a/URLShortener.Data/DBContext/URLShortenerDBContext.cs b/URLShortener.Data/DBContext/URLShortenerDBContext.cs index 2b19169..a4186e7 100644 --- a/URLShortener.Data/DBContext/URLShortenerDBContext.cs +++ b/URLShortener.Data/DBContext/URLShortenerDBContext.cs @@ -11,11 +11,7 @@ public URLShortenerDBContext (DbContextOptions options) : protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity() - .HasIndex(x => x.HashedURL); - - modelBuilder.Entity() - .HasIndex(x => new { x.URL, x.HashedURL }).IsUnique(); - + .HasIndex(x => x.HashedURL).IsUnique(); } public DbSet URLInfos { get; set; } diff --git a/URLShortener.Data/Migrations/20191213094430_InitialMigration.Designer.cs b/URLShortener.Data/Migrations/20200106074624_InitialMigration.Designer.cs similarity index 92% rename from URLShortener.Data/Migrations/20191213094430_InitialMigration.Designer.cs rename to URLShortener.Data/Migrations/20200106074624_InitialMigration.Designer.cs index 8688342..eef3c43 100644 --- a/URLShortener.Data/Migrations/20191213094430_InitialMigration.Designer.cs +++ b/URLShortener.Data/Migrations/20200106074624_InitialMigration.Designer.cs @@ -10,7 +10,7 @@ namespace URLShortener.Data.Migrations { [DbContext(typeof(URLShortenerDBContext))] - [Migration("20191213094430_InitialMigration")] + [Migration("20200106074624_InitialMigration")] partial class InitialMigration { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -43,9 +43,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("HashedURL"); - - b.HasIndex("URL", "HashedURL") + b.HasIndex("HashedURL") .IsUnique(); b.ToTable("URLInfos"); diff --git a/URLShortener.Data/Migrations/20191213094430_InitialMigration.cs b/URLShortener.Data/Migrations/20200106074624_InitialMigration.cs similarity index 84% rename from URLShortener.Data/Migrations/20191213094430_InitialMigration.cs rename to URLShortener.Data/Migrations/20200106074624_InitialMigration.cs index 9e0096c..ea3b2bc 100644 --- a/URLShortener.Data/Migrations/20191213094430_InitialMigration.cs +++ b/URLShortener.Data/Migrations/20200106074624_InitialMigration.cs @@ -25,12 +25,7 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.CreateIndex( name: "IX_URLInfos_HashedURL", table: "URLInfos", - column: "HashedURL"); - - migrationBuilder.CreateIndex( - name: "IX_URLInfos_URL_HashedURL", - table: "URLInfos", - columns: new[] { "URL", "HashedURL" }, + column: "HashedURL", unique: true); } diff --git a/URLShortener.Data/Migrations/URLShortenerDBContextModelSnapshot.cs b/URLShortener.Data/Migrations/URLShortenerDBContextModelSnapshot.cs index 3fcf405..77d10bb 100644 --- a/URLShortener.Data/Migrations/URLShortenerDBContextModelSnapshot.cs +++ b/URLShortener.Data/Migrations/URLShortenerDBContextModelSnapshot.cs @@ -41,9 +41,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("HashedURL"); - - b.HasIndex("URL", "HashedURL") + b.HasIndex("HashedURL") .IsUnique(); b.ToTable("URLInfos"); diff --git a/URLShortener/Services/URLShortenerService.cs b/URLShortener/Services/URLShortenerService.cs new file mode 100644 index 0000000..1c0ca8c --- /dev/null +++ b/URLShortener/Services/URLShortenerService.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace URLShortener.Services +{ + public class URLShortenerService + { + } +} diff --git a/URLShortener/Services/interfaces/IURLShortenerService.cs b/URLShortener/Services/interfaces/IURLShortenerService.cs new file mode 100644 index 0000000..0204c0c --- /dev/null +++ b/URLShortener/Services/interfaces/IURLShortenerService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace URLShortener.Services +{ + interface IURLShortenerService + { + + } +} diff --git a/URLShortener/Utils/HashGenerator.cs b/URLShortener/Utils/HashGenerator.cs new file mode 100644 index 0000000..286bd09 --- /dev/null +++ b/URLShortener/Utils/HashGenerator.cs @@ -0,0 +1,22 @@ +using System.Security.Cryptography; +using System.Text; + +namespace URLShortener.Utils +{ + public static class HashGenerator + { + public static string Generate_SHA256_Hash(string value) + { + StringBuilder Sb = new StringBuilder(); + + using (var hash = SHA256.Create()) + { + Encoding enc = Encoding.UTF8; + foreach (byte b in hash.ComputeHash(enc.GetBytes(value))) + Sb.Append(b.ToString("x2")); + } + + return Sb.ToString(); + } + } +} From 20533ed4b992767119f4573315007f4a7447787c Mon Sep 17 00:00:00 2001 From: Rajeev Soni Date: Mon, 6 Jan 2020 23:32:00 +0530 Subject: [PATCH 2/2] added working endpoints for URL Shortening service and redirecting --- .../Controllers/ShortURLController.cs | 50 +++++++++++++++++-- URLShortener/Response/ShortURLResponse.cs | 8 +++ URLShortener/Services/URLShortenerService.cs | 45 ++++++++++++++++- .../interfaces/IURLShortenerService.cs | 10 ++-- URLShortener/Startup.cs | 2 + URLShortener/appsettings.json | 1 + 6 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 URLShortener/Response/ShortURLResponse.cs diff --git a/URLShortener/Controllers/ShortURLController.cs b/URLShortener/Controllers/ShortURLController.cs index 3e7967a..c1cfbd0 100644 --- a/URLShortener/Controllers/ShortURLController.cs +++ b/URLShortener/Controllers/ShortURLController.cs @@ -1,6 +1,10 @@ using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using System; using System.Threading.Tasks; +using URLShortener.Response; +using URLShortener.Services; namespace URLShortener.Controllers { @@ -9,10 +13,15 @@ namespace URLShortener.Controllers public class ShortURLController : ControllerBase { private readonly ILogger _logger; + private readonly IConfiguration _configuration; + private readonly IURLShortenerService _urlShortenerService; + private const string ServiceURL = "ServiceURL"; - public ShortURLController(ILogger logger) + public ShortURLController(ILogger logger, IURLShortenerService urlShortenerService, IConfiguration configuration) { _logger = logger; + _configuration = configuration; + _urlShortenerService = urlShortenerService; } @@ -26,10 +35,43 @@ public ShortURLController(ILogger logger) /// With short url /// Unauthorized. [HttpPost] - public async Task Post() + public async Task Post([FromQuery]string url) { - await Task.Delay(10); - return Ok("tiny url"); + bool isValidUrl = Uri.TryCreate(url, UriKind.Absolute, out Uri uriResult) + && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); + + if(!isValidUrl) + { + return BadRequest(); + } + + var hashedURL = await _urlShortenerService.SaveURL(url); + var serviceURL = _configuration.GetValue(ServiceURL); + var shortURL = String.Concat(serviceURL,"/shorturl/",hashedURL); + var response = new ShortURLResponse() { ShortURL = shortURL, Code = hashedURL }; + return Ok(response); + } + + /// + /// Redirect to actual URL on providing shirtURL + /// + /// + /// ### Usage Notes ### + /// 1. A valid User with jwt + /// + /// With short url + /// Unauthorized. + [HttpGet] + [Route("{shortURL}")] + [ApiExplorerSettings(IgnoreApi = true)] + public async Task Get([FromRoute]string shortURL) + { + var url = await _urlShortenerService.GetURL(shortURL); + if(url == null) + { + return NotFound(); + } + return Redirect(url); } } } diff --git a/URLShortener/Response/ShortURLResponse.cs b/URLShortener/Response/ShortURLResponse.cs new file mode 100644 index 0000000..ec0f8a6 --- /dev/null +++ b/URLShortener/Response/ShortURLResponse.cs @@ -0,0 +1,8 @@ +namespace URLShortener.Response +{ + public class ShortURLResponse + { + public string ShortURL { get; set; } + public string Code { get; set; } + } +} diff --git a/URLShortener/Services/URLShortenerService.cs b/URLShortener/Services/URLShortenerService.cs index 1c0ca8c..528f5c2 100644 --- a/URLShortener/Services/URLShortenerService.cs +++ b/URLShortener/Services/URLShortenerService.cs @@ -1,11 +1,52 @@ -using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using URLShortener.Data.DBContext; +using URLShortener.Data.Entities; +using URLShortener.Utils; namespace URLShortener.Services { - public class URLShortenerService + public class URLShortenerService : IURLShortenerService { + private readonly ILogger _logger; + private readonly Func _dbContextProvider; + + public URLShortenerService(Func dbContextProvider, ILogger logger) + { + _dbContextProvider = dbContextProvider; + _logger = logger; + } + + + public async Task GetURL(string hashedURL) + { + using(var dbContext = _dbContextProvider()) + { + var urlInfo = await dbContext.URLInfos.AsNoTracking() + .SingleOrDefaultAsync(x => x.HashedURL == hashedURL); + return urlInfo.URL; + } + } + + public async Task SaveURL(string url) + { + using (var dbContext = _dbContextProvider()) + { + var hashedURL = HashGenerator.Generate_SHA256_Hash(url); + var urlInfo = await dbContext.URLInfos.SingleOrDefaultAsync(x => x.HashedURL == hashedURL); + if(urlInfo == null) + { + urlInfo = new URLInfo() { URL = url, CreatedOn = DateTime.UtcNow }; + urlInfo.HashedURL = hashedURL; + dbContext.URLInfos.Add(urlInfo); + await dbContext.SaveChangesAsync(); + } + return urlInfo.HashedURL; + } + } } } diff --git a/URLShortener/Services/interfaces/IURLShortenerService.cs b/URLShortener/Services/interfaces/IURLShortenerService.cs index 0204c0c..083e256 100644 --- a/URLShortener/Services/interfaces/IURLShortenerService.cs +++ b/URLShortener/Services/interfaces/IURLShortenerService.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace URLShortener.Services { - interface IURLShortenerService + public interface IURLShortenerService { - + Task SaveURL(string url); + Task GetURL(string hashedURL); } } diff --git a/URLShortener/Startup.cs b/URLShortener/Startup.cs index 7c23a46..c9fd80c 100644 --- a/URLShortener/Startup.cs +++ b/URLShortener/Startup.cs @@ -14,6 +14,7 @@ using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; using URLShortener.Data.DBContext; +using URLShortener.Services; namespace URLShortener { @@ -35,6 +36,7 @@ public void ConfigureServices(IServiceCollection services) var opt = new DbContextOptionsBuilder() .UseSqlServer(Configuration.GetConnectionString("URLShortenerDatabase")) .Options; + services.AddSingleton(); services.AddSingleton> ( () => new URLShortenerDBContext(opt) diff --git a/URLShortener/appsettings.json b/URLShortener/appsettings.json index ac46b00..e282806 100644 --- a/URLShortener/appsettings.json +++ b/URLShortener/appsettings.json @@ -6,6 +6,7 @@ "Microsoft.Hosting.Lifetime": "Information" } }, + "ServiceURL": "http://localhost:5011", "ConnectionStrings": { "URLShortenerDatabase": "Server=(LocalDb)\\MSSQLLocalDB;Database=URLShortener;Trusted_Connection=True;" },