Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Main v1 #528

Open
wants to merge 27 commits into
base: main-v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
7b5810b
Fix for decoding url that should not be
apetkovic Apr 30, 2021
540ad74
Support for Azure Front Door
apetkovic May 4, 2021
5b123ab
Support for legacy
apetkovic May 4, 2021
a0c4ab9
Update UrlShortener.cs
apetkovic May 4, 2021
fdd522d
Headers.Host attempt
apetkovic May 4, 2021
463ffc1
Support for Azure Front Door using X-Forwarded-Host
apetkovic May 4, 2021
c5f72a0
Disable root proxy
apetkovic May 12, 2021
f95bdb9
Get settings using System.Environment.GetEnvironmentVariable
apetkovic May 13, 2021
9e6fb44
Get settings using System.Environment.GetEnvironmentVariable
apetkovic May 13, 2021
3eaa0d5
Removed FUNCTIONS_V2_COMPATIBILITY_MODE
apetkovic May 13, 2021
18a2ecd
Switch HttpResponseMessage to IActionResult
apetkovic May 13, 2021
4e51a33
Switch HttpResponseMessage to IActionResult
apetkovic May 13, 2021
576e30d
Switch HttpResponseMessage to IActionResult
apetkovic May 13, 2021
ed30cb2
Switch HttpResponseMessage to IActionResult
apetkovic May 13, 2021
75da309
Switch HttpResponseMessage to IActionResult
apetkovic May 13, 2021
9ec76db
Switch HttpResponseMessage to IActionResult
apetkovic May 13, 2021
1a06af8
Check for autogenerated vanity already existing and regenerate
apetkovic Jun 2, 2021
df96bc3
Update Utility.cs
apetkovic Jun 2, 2021
bc3f995
Update Utility.cs
apetkovic Jun 2, 2021
2bfff38
Update Utility.cs
apetkovic Jun 2, 2021
c315552
Swagger update
apetkovic Sep 8, 2021
d22530c
Update OpenApiConfigurationOptions.cs
apetkovic Jun 20, 2024
2389fdf
Traxero swagger update
apetkovic Jun 20, 2024
4696dad
Use POST on swagger docs
apetkovic Jun 20, 2024
8ee617e
Update StorageTableHelper.cs
apetkovic Jun 26, 2024
c2398e9
Update Utility.cs
apetkovic Jun 26, 2024
91e286d
Update StorageTableHelper.cs
apetkovic Jun 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/shortenerTools/Domain/StorageTableHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Table;

Expand Down Expand Up @@ -34,7 +35,7 @@ private CloudTable GetTable(string tableName){
CloudStorageAccount storageAccount = this.CreateStorageAccountFromConnectionString();
CloudTableClient tableClient = storageAccount.CreateCloudTableClient(new TableClientConfiguration());
CloudTable table = tableClient.GetTableReference(tableName);
table.CreateIfNotExists();
//table.CreateIfNotExists(); Removed to cut response time in half

return table;
}
Expand Down Expand Up @@ -85,6 +86,22 @@ public async Task<List<ClickStatsEntity>> GetAllStatsByVanity(string vanity)
return lstShortUrl;
}

/// <summary>
/// Returns the ShortUrlEntity of the <paramref name="vanity"/>
/// </summary>
/// <param name="vanity"></param>
/// <returns>ShortUrlEntity</returns>
public async Task<ShortUrlEntity> GetShortUrlEntityByVanity(string vanity)
{
var tempUrl = new ShortUrlEntity(string.Empty, vanity);
return await GetShortUrlEntity(tempUrl);
}

public async Task<bool> IfShortUrlEntityExistByVanity(string vanity)
{
ShortUrlEntity shortUrlEntity = await GetShortUrlEntityByVanity(vanity);
return (shortUrlEntity != null);
}

public async Task<bool> IfShortUrlEntityExist(ShortUrlEntity row)
{
Expand Down
51 changes: 38 additions & 13 deletions src/shortenerTools/Domain/Utility.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;

namespace Cloud5mins.domain
{
public static class Utility
{
private const string Alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
private static readonly int Base = Alphabet.Length;
//reshuffled for randomisation, same unique characters just jumbled up, you can replace with your own version
private const string ConversionCode = "FjTG0s5dgWkbLf_8etOZqMzNhmp7u6lUJoXIDiQB9-wRxCKyrPcv4En3Y21aASHV";
private static readonly int Base = ConversionCode.Length;
//sets the length of the unique code to add to vanity
private const int MinVanityCodeLength = 5;

public static async Task<string> GetValidEndUrl(string vanity, StorageTableHelper stgHelper)
{
if(string.IsNullOrEmpty(vanity))
if (string.IsNullOrEmpty(vanity))
{
var newKey = await stgHelper.GetNextTableId();
string getCode() => Encode(newKey);
return string.Join(string.Empty, getCode());
string randomVanity = Encode(newKey);
if (await stgHelper.IfShortUrlEntityExistByVanity(randomVanity))
return await GetValidEndUrl(null, stgHelper);

return string.Join(string.Empty, randomVanity);
}
else
{
Expand All @@ -25,19 +32,37 @@ public static async Task<string> GetValidEndUrl(string vanity, StorageTableHelpe
public static string Encode(int i)
{
if (i == 0)
return Alphabet[0].ToString();
var s = string.Empty;
while (i > 0)
{
s += Alphabet[i % Base];
i = i / Base;
}
return ConversionCode[0].ToString();

return string.Join(string.Empty, s.Reverse());
return GenerateUniqueRandomToken(i);
}

public static string GetShortUrl(string host, string vanity){
return host + "/" + vanity;
}

// generates a unique, random, and alphanumeric token for the use as a url
//(not entirely secure but not sequential so generally not guessable)
public static string GenerateUniqueRandomToken(int uniqueId)
{
//Encode the unique id prefix (less characters and prevent displaying url count)
var s = string.Empty;
while (uniqueId > 0)
{
s += ConversionCode[uniqueId % Base];
uniqueId = uniqueId / Base;
}

using (var generator = new RNGCryptoServiceProvider())
{
//minimum size I would suggest is 5, longer the better but we want short URLs!
var bytes = new byte[MinVanityCodeLength];
generator.GetBytes(bytes);
var chars = bytes
.Select(b => ConversionCode[b % Base]);
var token = new string(chars.ToArray());
return string.Join(string.Empty, s.Reverse().Concat(token.Reverse()));
}
}
}
}
36 changes: 36 additions & 0 deletions src/shortenerTools/OpenApiConfigurationOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Configurations;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Text;

namespace BeaconServices.VINDecoderService.FunctionsApp
{
public class OpenApiConfigurationOptions : DefaultOpenApiConfigurationOptions
{
public override OpenApiInfo Info { get; set; } = new OpenApiInfo()
{
Version = "1.0.0",
Title = "TRAXERO URL Shortener",
Description = @"### Overview
The TRAXERO URL Shortener API provides a simple way to turn a long url into a short one. View and test the API methods at the bottom of this page.

An API key is required to use this API. Enter your API key in authorize/api key box to enable testing on this page.

### URL Redirect

A short url is provided as part of the response to the /UrlShortener endpoint. To manually reconstruct a short url, append the vanity to the domain name: `https://tows.at/{vanity}`. [Example short url](https://tows.at/TRAXERO)

Custom domains names can be configured upon request."
};

public override List<OpenApiServer> Servers { get; set; } = new List<OpenApiServer>()
{
new OpenApiServer() { Url = "https://tows.at/api/", Description = "Production" },
};

public override OpenApiVersionType OpenApiVersion { get; set; } = OpenApiVersionType.V3;

}
}
28 changes: 14 additions & 14 deletions src/shortenerTools/UrlArchive/UrlArchive.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/*
/*
```c#
Input:
{
// [Required]
"PartitionKey": "d",
{
// [Required]
"PartitionKey": "d",

// [Required]
"RowKey": "doc",
// [Required]
"RowKey": "doc",

}
}


*/
Expand All @@ -18,18 +18,18 @@
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Net.Http;
using Cloud5mins.domain;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Mvc;

namespace Cloud5mins.Function
{
public static class UrlArchive
{
[FunctionName("UrlArchive")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req,
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequestMessage req,
ILogger log,
ExecutionContext context)
{
Expand All @@ -38,13 +38,13 @@ public static async Task<HttpResponseMessage> Run(
// Validation of the inputs
if (req == null)
{
return req.CreateResponse(HttpStatusCode.NotFound);
return new NotFoundResult();
}

ShortUrlEntity input = await req.Content.ReadAsAsync<ShortUrlEntity>();
if (input == null)
{
return req.CreateResponse(HttpStatusCode.NotFound);
return new NotFoundResult();
}

ShortUrlEntity result;
Expand All @@ -63,10 +63,10 @@ public static async Task<HttpResponseMessage> Run(
catch (Exception ex)
{
log.LogError(ex, "An unexpected error was encountered.");
return req.CreateResponse(HttpStatusCode.BadRequest, ex);
return new BadRequestObjectResult(ex);
}

return req.CreateResponse(HttpStatusCode.OK, result);
return new OkObjectResult(result);
}
}
}
20 changes: 14 additions & 6 deletions src/shortenerTools/UrlClickStats/UrlClickStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Net.Http;
using Cloud5mins.domain;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Attributes;
using Microsoft.OpenApi.Models;
using System.Net;
using Microsoft.Azure.WebJobs.Extensions.OpenApi.Core.Enums;

namespace Cloud5mins.Function
{
public static class UrlClickStats
{
//[OpenApiOperation(operationId: "UrlClickStats", tags: new[] { "Urls" }, Summary = "Get short URL click stats", Description = "Returns statistics for a specific short URL.", Visibility = OpenApiVisibilityType.Important)]
//[OpenApiRequestBody(contentType: "application/json", bodyType: typeof(UrlClickStatsRequest), Required = true)]
//[OpenApiSecurity("function_key", SecuritySchemeType.ApiKey, Name = "code", In = OpenApiSecurityLocationType.Query)]
//[OpenApiResponseWithBody(statusCode: HttpStatusCode.OK, contentType: "application/json", bodyType: typeof(ClickStatsEntityList))]
[FunctionName("UrlClickStats")]
public static async Task<HttpResponseMessage> Run(
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req,
ILogger log,
ExecutionContext context)
Expand All @@ -23,13 +31,13 @@ public static async Task<HttpResponseMessage> Run(
// Validation of the inputs
if (req == null)
{
return req.CreateResponse(HttpStatusCode.NotFound);
return new NotFoundResult();
}

UrlClickStatsRequest input = await req.Content.ReadAsAsync<UrlClickStatsRequest>();
if (input == null)
{
return req.CreateResponse(HttpStatusCode.NotFound);
return new NotFoundResult();
}

var result = new ClickStatsEntityList();
Expand All @@ -48,10 +56,10 @@ public static async Task<HttpResponseMessage> Run(
catch (Exception ex)
{
log.LogError(ex, "An unexpected error was encountered.");
return req.CreateResponse(HttpStatusCode.BadRequest, ex);
return new BadRequestObjectResult(ex);
}

return req.CreateResponse(HttpStatusCode.OK, result);
return new OkObjectResult(result);
}
}
}
25 changes: 13 additions & 12 deletions src/shortenerTools/UrlList/UrlList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Net.Http;
using Cloud5mins.domain;
using Microsoft.Extensions.Configuration;
using System.Linq;
using Microsoft.AspNetCore.Mvc;

namespace Cloud5mins.Function
{
public static class UrlList
{
[FunctionName("UrlList")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]HttpRequestMessage req,
ILogger log,
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequestMessage req,
ILogger log,
ExecutionContext context)
{
log.LogInformation($"C# HTTP trigger function processed this request: {req}");
Expand All @@ -45,24 +45,25 @@ public static async Task<HttpResponseMessage> Run(
.AddEnvironmentVariables()
.Build();

StorageTableHelper stgHelper = new StorageTableHelper(config["UlsDataStorage"]);
StorageTableHelper stgHelper = new StorageTableHelper(config["UlsDataStorage"]);

try
{
result.UrlList = await stgHelper.GetAllShortUrlEntities();
result.UrlList = await stgHelper.GetAllShortUrlEntities();
result.UrlList = result.UrlList.Where(p => !(p.IsArchived ?? false)).ToList();
var host = req.RequestUri.GetLeftPart(UriPartial.Authority);
foreach(ShortUrlEntity url in result.UrlList){
url.ShortUrl = Utility.GetShortUrl(host, url.RowKey);
}
var host = req.RequestUri.GetLeftPart(UriPartial.Authority);
foreach (ShortUrlEntity url in result.UrlList)
{
url.ShortUrl = Utility.GetShortUrl(host, url.RowKey);
}
}
catch (Exception ex)
{
log.LogError(ex, "An unexpected error was encountered.");
return req.CreateResponse(HttpStatusCode.BadRequest, ex);
return new BadRequestObjectResult(ex);
}

return req.CreateResponse(HttpStatusCode.OK, result);
return new OkObjectResult(result);
}
}
}
Loading