Skip to content

Commit

Permalink
Merge pull request #25 from brenordv/feat/latest-glucose
Browse files Browse the repository at this point in the history
[feat] Updated the endpoint that returns the latest glucose reading.
  • Loading branch information
brenordv authored Dec 17, 2024
2 parents d3da7a4 + 2c743db commit 85baa42
Show file tree
Hide file tree
Showing 16 changed files with 319 additions and 331 deletions.
7 changes: 7 additions & 0 deletions NightScout.Raccoon.sln
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "97. Pipelines", "97. Pipeli
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Raccoon.Ninja.AzFn.ScheduledTasks.Tests", "Raccoon.Ninja.AzFn.ScheduledTasks.Tests\Raccoon.Ninja.AzFn.ScheduledTasks.Tests.csproj", "{D4D62A6B-724A-4499-B462-A9E95E7896CD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Raccoon.Ninja.AzFn.DataApi.Tests", "Raccoon.Ninja.AzFn.DataApi.Tests\Raccoon.Ninja.AzFn.DataApi.Tests.csproj", "{A4B79A3C-396D-429F-BC30-012DF68C690A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -88,6 +90,10 @@ Global
{D4D62A6B-724A-4499-B462-A9E95E7896CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4D62A6B-724A-4499-B462-A9E95E7896CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4D62A6B-724A-4499-B462-A9E95E7896CD}.Release|Any CPU.Build.0 = Release|Any CPU
{A4B79A3C-396D-429F-BC30-012DF68C690A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4B79A3C-396D-429F-BC30-012DF68C690A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4B79A3C-396D-429F-BC30-012DF68C690A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4B79A3C-396D-429F-BC30-012DF68C690A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{2B19A689-B703-450C-9E06-DFD0BE969053} = {11F72AFB-B94A-4E9A-8817-F1FB60E9DF6D}
Expand All @@ -100,5 +106,6 @@ Global
{7BC539F4-D458-40C0-8277-0E6D24416E8C} = {0F4894B1-AE2F-4119-94D4-82B7B33A7C39}
{1CAC242F-719D-4E31-BEF8-7E3972F12482} = {11F72AFB-B94A-4E9A-8817-F1FB60E9DF6D}
{D4D62A6B-724A-4499-B462-A9E95E7896CD} = {BC1845AD-353C-4548-A86D-80A7EBC55C34}
{A4B79A3C-396D-429F-BC30-012DF68C690A} = {BC1845AD-353C-4548-A86D-80A7EBC55C34}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using FluentAssertions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using Raccoon.Ninja.AzFn.DataApi.ExtensionMethods;

namespace Raccoon.Ninja.AzFn.DataApi.Tests.ExtensionMethods;

public class HttpRequestExtensionsTests
{
[Fact]
public void TryGetReadSinceParam_NoParameter_ReturnsNull()
{
// Arrange
var context = new DefaultHttpContext();
var request = context.Request;

// Act
var result = request.TryGetReadSinceParam();

// Assert
result.Should().BeNull();
}

[Fact]
public void TryGetReadSinceParam_InvalidParameter_ReturnsNull()
{
// Arrange
var context = new DefaultHttpContext();
var request = context.Request;
request.QueryString = new QueryString("?readSince=notANumber");

// Act
var result = request.TryGetReadSinceParam();

// Assert
result.Should().BeNull();
}

[Fact]
public void TryGetReadSinceParam_ValidParameter_ReturnsLongValue()
{
// Arrange
var context = new DefaultHttpContext();
var request = context.Request;
request.QueryString = new QueryString("?readSince=123456");

// Act
var result = request.TryGetReadSinceParam();

// Assert
result.Should().NotBeNull();
result.Should().Be(123456L);
}

[Fact]
public void TryGetReadSinceParam_ValidParameterSetThroughQueryProperty_ReturnsLongValue()
{
// Arrange
var context = new DefaultHttpContext();
var request = context.Request;

// Setting Query directly using a dictionary.
// Although Query is read-only, we can reassign it using the constructor of QueryCollection.
var queryDictionary = new Dictionary<string, StringValues>
{
{ "readSince", "987654" }
};
request.QueryString = QueryString.Create(queryDictionary);

// Act
var result = request.TryGetReadSinceParam();

// Assert
result.Should().NotBeNull();
result.Should().Be(987654L);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="FluentAssertions" Version="6.12.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="xunit" Version="2.5.3"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"/>
</ItemGroup>

<ItemGroup>
<Using Include="Xunit"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Raccoon.Ninja.AzFn.DataApi\Raccoon.Ninja.AzFn.DataApi.csproj"/>
</ItemGroup>

</Project>
63 changes: 46 additions & 17 deletions Raccoon.Ninja.AzFn.DataApi/DataApiFunc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
using Raccoon.Ninja.AzFn.DataApi.ExtensionMethods;
using Raccoon.Ninja.AzFn.DataApi.Utils;
using Raccoon.Ninja.Domain.Core.Entities;
using Raccoon.Ninja.Domain.Core.Models;
using Microsoft.Azure.Functions.Worker;
using Newtonsoft.Json;

namespace Raccoon.Ninja.AzFn.DataApi;

Expand All @@ -26,39 +26,68 @@ public DataApiFunc(ILogger<DataApiFunc> logger)
[Function("DataApiFunc")]
public async Task<IActionResult> RunAsync(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]
HttpRequest req,
HttpRequest req,
[CosmosDBInput(
databaseName: "%CosmosDatabaseName%",
containerName: "%CosmosContainerName%",
Connection = "CosmosConnectionString",
SqlQuery = "SELECT TOP 1 * FROM c ORDER BY c.readAt DESC"
"%CosmosDatabaseName%",
"%CosmosContainerName%",
Connection = "CosmosConnectionString"
)]
IEnumerable<GlucoseReading> previousReadings)
Container container)
{
_logger.LogInformation("Data API call received. Request by IP: {Ip}", req.HttpContext.Connection.RemoteIpAddress);
_logger.LogInformation("Data API call received. Request by IP: {Ip}",
req.HttpContext.Connection.RemoteIpAddress);

GlucoseReading latestReading = null;
var results = new List<GlucoseReadingResponse>();

try
{
if (!Validators.IsKeyValid(await req.Body.ExtractKey()))
return new UnauthorizedResult();

latestReading = previousReadings.FirstOrDefault();
var readSince = req.TryGetReadSinceParam();

if (readSince.HasValue)
{
// Build a parameterized query
var queryDefinition = new QueryDefinition(
"SELECT * FROM c WHERE c.readAt > @readSince ORDER BY c.readAt DESC"
).WithParameter("@readSince", readSince);


using var iteratorReadsince = container.GetItemQueryIterator<GlucoseReading>(queryDefinition);
while (iteratorReadsince.HasMoreResults)
{
var response = await iteratorReadsince.ReadNextAsync();
results.AddRange(response.Select(item => (GlucoseReadingResponse)item));
}

GlucoseReadingResponse response = latestReading;
return results.Count != 0 ? new OkObjectResult(results) : new NoContentResult();
}

return latestReading is null
? new NoContentResult()
: new OkObjectResult(response);
// If no readSince parameter is provided, return the latest reading
using var iteratorLatestReading = container.GetItemQueryIterator<GlucoseReading>(
new QueryDefinition("SELECT TOP 1 * FROM c ORDER BY c.readAt DESC")
);

while (iteratorLatestReading.HasMoreResults)
{
var response = await iteratorLatestReading.ReadNextAsync();
var latestReading = response.FirstOrDefault();

if (latestReading is null) continue;

results.Add(latestReading);
return new OkObjectResult(results);
}

return new NoContentResult();
}
catch (Exception e)
{
_logger.LogError(e, "Error while processing request from IP: {Ip} | Latest reading : {LatestReading}",
req.HttpContext.Connection.RemoteIpAddress, latestReading);
req.HttpContext.Connection.RemoteIpAddress, results);

return new StatusCodeResult(500);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Http;

namespace Raccoon.Ninja.AzFn.DataApi.ExtensionMethods;

public static class HttpRequestExtensions
{
public static long? TryGetReadSinceParam(this HttpRequest req)
{
if (!req.Query.ContainsKey("readSince")) return null;

if (long.TryParse(req.Query["readSince"], out var readSince))
return readSince;

return null;
}
}
66 changes: 33 additions & 33 deletions Raccoon.Ninja.AzFn.DataApi/Raccoon.Ninja.AzFn.DataApi.csproj
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<AssemblyVersion>3.0.0</AssemblyVersion>
<FileVersion>3.0.0</FileVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<OutputType>Exe</OutputType>
<ImplicitUsings>enabled</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Raccoon.Ninja.Domain.Core\Raccoon.Ninja.Domain.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="4.9.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.2" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.22.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.3.2" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<AssemblyVersion>4.0.0</AssemblyVersion>
<FileVersion>4.0.0</FileVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<OutputType>Exe</OutputType>
<ImplicitUsings>enabled</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Raccoon.Ninja.Domain.Core\Raccoon.Ninja.Domain.Core.csproj"/>
</ItemGroup>
<ItemGroup>
<Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="4.9.0"/>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.2.0"/>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.2"/>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0"/>
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.22.0"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.3.2"/>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="FluentAssertions" Version="6.12.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="xunit" Version="2.8.1"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand All @@ -24,8 +24,8 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Raccoon.Ninja.AzFn.ScheduledTasks\Raccoon.Ninja.AzFn.ScheduledTasks.csproj" />
<ProjectReference Include="..\Raccoon.Ninja.TestHelpers\Raccoon.Ninja.TestHelpers.csproj" />
<ProjectReference Include="..\Raccoon.Ninja.AzFn.ScheduledTasks\Raccoon.Ninja.AzFn.ScheduledTasks.csproj"/>
<ProjectReference Include="..\Raccoon.Ninja.TestHelpers\Raccoon.Ninja.TestHelpers.csproj"/>
</ItemGroup>

</Project>
Loading

0 comments on commit 85baa42

Please sign in to comment.