Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 8 additions & 4 deletions FunctionApp/ErrorHandler.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;

namespace Coomes.Equipper.FunctionApp
{
public static class ErrorHandler
{
public static async Task<ActionResult> RunWithErrorHandling(ILogger logger, Func<Task<ActionResult>> method)
public static async Task<HttpResponseData> RunWithErrorHandling(ILogger logger, HttpRequestData req, Func<Task<HttpResponseData>> method)
{
try
{
return await method();
}
catch (BadRequestException bre)
{
return new BadRequestObjectResult(bre.Message);
var response = req.CreateResponse(HttpStatusCode.BadRequest);
await response.WriteStringAsync(bre.Message);
return response;
}
catch(UnauthorizedException)
{
return new UnauthorizedResult();
var response = req.CreateResponse(HttpStatusCode.Unauthorized);
return response;
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion FunctionApp/FunctionApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.3" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.23.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.18.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.3.2" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
Expand Down
35 changes: 22 additions & 13 deletions FunctionApp/Functions/Echo.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using System.Net;
using System.Web;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;

namespace Coomes.Equipper.FunctionApp.Functions
{
public static class Echo
public class Echo
{
[FunctionName("Echo")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger log)
private readonly ILogger _logger;

public Echo(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<Echo>();
}

[Function("Echo")]
public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequestData req)
{
var correlationID = Guid.NewGuid();
var user = StaticWebAppsAuth.ParseUser(req);
log.LogInformation("{function} {status} {cid} {userId}", "Echo", "Starting", correlationID.ToString(), user.UserId);
_logger.LogInformation("{function} {status} {cid} {userId}", "Echo", "Starting", correlationID.ToString(), user.UserId);

string value = req.Query["value"];
var query = HttpUtility.ParseQueryString(req.Url.Query);
string value = query["value"];

string responseMessage = "";
if(string.Equals("I'm an idiot", value, StringComparison.CurrentCultureIgnoreCase))
Expand All @@ -38,8 +44,11 @@ public static IActionResult Run(
responseMessage = "Saying nothing, you hear only the silence of the void...";
}

log.LogInformation("{function} {status} {cid} {userId}", "Echo", "Success", correlationID.ToString(), user.UserId);
return new OkObjectResult(responseMessage);
_logger.LogInformation("{function} {status} {cid} {userId}", "Echo", "Success", correlationID.ToString(), user.UserId);

var response = req.CreateResponse(HttpStatusCode.OK);
response.WriteString(responseMessage);
return response;
}
}
}
36 changes: 22 additions & 14 deletions FunctionApp/Functions/GetActivityCount.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Coomes.Equipper.CosmosStorage;
using Coomes.Equipper.Operations;
using System.Threading.Tasks;

namespace Coomes.Equipper.FunctionApp.Functions
{
public static class GetActivityCount
public class GetActivityCount
{
[FunctionName("ActivityCount")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger logger)
private readonly ILogger _logger;

public GetActivityCount(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<GetActivityCount>();
}

[Function("ActivityCount")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequestData req)
{
var correlationID = Guid.NewGuid();
var user = StaticWebAppsAuth.ParseUser(req);
logger.LogInformation("{function} {status} {cid} {userId}", "GetActivityCount", "Starting", correlationID.ToString(), user.UserId);
_logger.LogInformation("{function} {status} {cid} {userId}", "GetActivityCount", "Starting", correlationID.ToString(), user.UserId);

var count = await ExecuteGetCount(logger);
var count = await ExecuteGetCount(_logger);

logger.LogInformation("{function} {status} {cid} {userId}", "GetActivityCount", "Success", correlationID.ToString(), user.UserId);
return new OkObjectResult(count);
_logger.LogInformation("{function} {status} {cid} {userId}", "GetActivityCount", "Success", correlationID.ToString(), user.UserId);

var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(count);
return response;
}

private static Task<int> ExecuteGetCount(ILogger logger)
Expand Down
43 changes: 28 additions & 15 deletions FunctionApp/Functions/GetAthlete.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Coomes.Equipper.CosmosStorage;
using Coomes.Equipper.Operations;
using System.Threading.Tasks;
using Coomes.Equipper.StravaApi;

namespace Coomes.Equipper.FunctionApp.Functions
{
public static class GetAthlete
public class GetAthlete
{
[FunctionName("GetAthlete")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger logger)
private readonly ILogger _logger;

public GetAthlete(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<GetAthlete>();
}

[Function("GetAthlete")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequestData req)
{
var correlationID = Guid.NewGuid();
var user = StaticWebAppsAuth.ParseUser(req);
logger.LogInformation("{function} {status} {cid} {userId}", "GetAthlete", "Starting", correlationID.ToString(), user.UserId);
_logger.LogInformation("{function} {status} {cid} {userId}", "GetAthlete", "Starting", correlationID.ToString(), user.UserId);

var athlete = await ExecuteGetAthlete(user, logger);
var athlete = await ExecuteGetAthlete(user, _logger);

logger.LogInformation("{function} {status} {cid} {userId}", "GetAthlete", "Success", correlationID.ToString(), user.UserId);
_logger.LogInformation("{function} {status} {cid} {userId}", "GetAthlete", "Success", correlationID.ToString(), user.UserId);

if(athlete != null) return new OkObjectResult(athlete);
else return new NotFoundResult();
if(athlete != null)
{
var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(athlete);
return response;
}
else
{
return req.CreateResponse(HttpStatusCode.NotFound);
}
}

private static Task<Athlete> ExecuteGetAthlete(EquipperUser user, ILogger logger)
Expand Down
58 changes: 36 additions & 22 deletions FunctionApp/Functions/SubscriptionWebhook.cs
Original file line number Diff line number Diff line change
@@ -1,58 +1,68 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using System.Web;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Coomes.Equipper.Operations;
using Microsoft.AspNetCore.Mvc;
using Coomes.Equipper.StravaApi.Models;
using Coomes.Equipper.StravaApi;
using Coomes.Equipper.CosmosStorage;
using System.Net;

namespace Coomes.Equipper.FunctionApp.Functions
{
public static class SubscriptionWebhook
public class SubscriptionWebhook
{
[FunctionName("__SubscriptionWebhookPlaceholder__")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
private readonly ILogger _logger;

public SubscriptionWebhook(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<SubscriptionWebhook>();
}

[Function("__SubscriptionWebhookPlaceholder__")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequestData req)
{
switch(req.Method)
{
case "GET":
{
return await ConfirmSubscription(req, log);
return await ConfirmSubscription(req, _logger);
}
case "POST":
{
return await ProcessEvent(req, log);
return await ProcessEvent(req, _logger);
}
default:
{
log.LogWarning($"The SubscriptionWebhook function was called with the unsupported HTTP method {req.Method}");
return new StatusCodeResult((int)HttpStatusCode.MethodNotAllowed);
_logger.LogWarning($"The SubscriptionWebhook function was called with the unsupported HTTP method {req.Method}");
return req.CreateResponse(HttpStatusCode.MethodNotAllowed);
}
}
}

private static Task<IActionResult> ConfirmSubscription(HttpRequest req, ILogger logger)
private static async Task<HttpResponseData> ConfirmSubscription(HttpRequestData req, ILogger logger)
{
var correlationID = Guid.NewGuid();
logger.LogInformation("{function} {status} {cid}", "SubscriptionWebhook - ConfirmationSubscription", "Starting", correlationID.ToString());

var challenge = req.Query["hub.challenge"];
var query = HttpUtility.ParseQueryString(req.Url.Query);
var challenge = query["hub.challenge"];
if(challenge == String.Empty)
{
return Task.FromResult<IActionResult>(new BadRequestObjectResult("Subscription confirmations must contain a 'hub.challenge' query parameter."));
var errorResponse = req.CreateResponse(HttpStatusCode.BadRequest);
await errorResponse.WriteStringAsync("Subscription confirmations must contain a 'hub.challenge' query parameter.");
return errorResponse;
}

var verifyToken = req.Query["hub.verify_token"];
var verifyToken = query["hub.verify_token"];
if(verifyToken == String.Empty)
{
return Task.FromResult<IActionResult>(new BadRequestObjectResult("Subscription confirmations must contain a 'hub.verify_token' query parameter."));
var errorResponse = req.CreateResponse(HttpStatusCode.BadRequest);
await errorResponse.WriteStringAsync("Subscription confirmations must contain a 'hub.verify_token' query parameter.");
return errorResponse;
}

// todo: better way to build dependencies?
Expand All @@ -62,10 +72,13 @@ private static Task<IActionResult> ConfirmSubscription(HttpRequest req, ILogger
var confirmation = getConfirmation.Execute(challenge, verifyToken, expectedToken);

logger.LogInformation("{function} {status} {cid}", "SubscriptionWebhook - ConfirmationSubscription", "Success", correlationID.ToString());
return Task.FromResult<IActionResult>(new OkObjectResult(confirmation));

var response = req.CreateResponse(HttpStatusCode.OK);
await response.WriteAsJsonAsync(confirmation);
return response;
}

private static async Task<IActionResult> ProcessEvent(HttpRequest req, ILogger log)
private static async Task<HttpResponseData> ProcessEvent(HttpRequestData req, ILogger log)
{
var correlationID = Guid.NewGuid();
log.LogInformation("{function} {status} {cid}", "SubscriptionWebhook - ProcessEvent", "Starting", correlationID.ToString());
Expand All @@ -77,7 +90,8 @@ private static async Task<IActionResult> ProcessEvent(HttpRequest req, ILogger l
await ExecuteEventAction(stravaEvent, log);

log.LogInformation("{function} {status} {cid}", "SubscriptionWebhook - ProcessEvent", "Success", correlationID.ToString());
return new OkResult();

return req.CreateResponse(HttpStatusCode.OK);
}

private static async Task ExecuteEventAction(StravaEvent stravaEvent, ILogger log)
Expand Down
45 changes: 27 additions & 18 deletions FunctionApp/Functions/TokenExchange.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,47 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using System.Web;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Coomes.Equipper.StravaApi;
using Coomes.Equipper.Operations;
using Coomes.Equipper.CosmosStorage;

namespace Coomes.Equipper.FunctionApp
{
public static class TokenExchange
public class TokenExchange
{
[FunctionName("TokenExchange")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ILogger log)
private readonly ILogger _logger;

public TokenExchange(ILoggerFactory loggerFactory)
{
return await ErrorHandler.RunWithErrorHandling(log, async () => {
_logger = loggerFactory.CreateLogger<TokenExchange>();
}

[Function("TokenExchange")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequestData req)
{
return await ErrorHandler.RunWithErrorHandling(_logger, req, async () => {
var correlationID = Guid.NewGuid();
var user = StaticWebAppsAuth.ParseUser(req);
log.LogInformation("{function} {status} {cid} {userId}", "TokenExchange", "Starting", correlationID.ToString(), user.UserId);
_logger.LogInformation("{function} {status} {cid} {userId}", "TokenExchange", "Starting", correlationID.ToString(), user.UserId);

string code = req.Query["_code"]; // see https://github.com/Azure/static-web-apps/issues/165 and auth.html
string scopeString = req.Query["scope"];
string error = req.Query["error"];
var query = HttpUtility.ParseQueryString(req.Url.Query);
string code = query["_code"]; // see https://github.com/Azure/static-web-apps/issues/165 and auth.html
string scopeString = query["scope"];
string error = query["error"];

log.LogInformation("Received auth code with scope '{scope}'", scopeString);
_logger.LogInformation("Received auth code with scope '{scope}'", scopeString);

var token = await ExecuteTokenExchange(code, scopeString, user, error, log);
var token = await ExecuteTokenExchange(code, scopeString, user, error, _logger);

_logger.LogInformation("{function} {status} {cid} {userId}", "TokenExchange", "Success", correlationID.ToString(), user.UserId);

log.LogInformation("{function} {status} {cid} {userId}", "TokenExchange", "Success", correlationID.ToString(), user.UserId);
return new OkResult();
var response = req.CreateResponse(HttpStatusCode.OK);
return response;
});
}

Expand Down
Loading
Loading