Skip to content

Commit

Permalink
Merge pull request #37 from TrueVote/AD-72
Browse files Browse the repository at this point in the history
AD-72 Builds Version.cs for compilation, instead of reading version.json at runtime
  • Loading branch information
morrisonbrett authored Sep 27, 2023
2 parents cd65784 + 652fe40 commit 27d2f11
Show file tree
Hide file tree
Showing 8 changed files with 342 additions and 421 deletions.
540 changes: 271 additions & 269 deletions .gitignore

Large diffs are not rendered by default.

83 changes: 6 additions & 77 deletions TrueVote.Api.Tests/ServiceTests/StatusTest.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;
using System;
using System.IO.Abstractions;
using System.Threading.Tasks;
using TrueVote.Api.Models;
using TrueVote.Api.Services;
Expand All @@ -23,7 +21,7 @@ public async Task FailsIfNullArgs()
{
try
{
var status = new Status(_fileSystem, _logHelper.Object, true);
var status = new Status(_logHelper.Object);
_ = await status.GetStatus(null);
}
catch (ArgumentNullException ane)
Expand All @@ -43,18 +41,18 @@ public async Task FailsIfNullArgs()
[Fact]
public async Task LogsMessages()
{
var status = new Status(_fileSystem, _logHelper.Object, true);
var status = new Status(_logHelper.Object);
var requestData = new MockHttpRequestData("");
_ = await status.GetStatus(requestData);

_logHelper.Verify(LogLevel.Information, Times.AtLeast(4));
_logHelper.Verify(LogLevel.Information, Times.AtLeast(1));
_logHelper.Verify(LogLevel.Debug, Times.Exactly(2));
}

[Fact]
public async Task ReturnsValidModel()
{
var status = new Status(_fileSystem, _logHelper.Object, true);
var status = new Status(_logHelper.Object);
var requestData = new MockHttpRequestData("");
var res = await status.GetStatus(requestData);

Expand All @@ -67,7 +65,7 @@ public async Task ReturnsValidModel()
[Fact]
public async Task ReturnsValidBuildInfoModel()
{
var status = new Status(_fileSystem, _logHelper.Object, true);
var status = new Status(_logHelper.Object);
var requestData = new MockHttpRequestData("");
var res = await status.GetStatus(requestData);

Expand All @@ -77,61 +75,10 @@ public async Task ReturnsValidBuildInfoModel()
Assert.NotNull(statusModel.BuildInfo);
}

[Fact]
public async Task ReturnsValidBuildInfoModelNonQualifiedPath()
{
var ret = string.Empty;
var b = false;

var fileSystemMock = new Mock<IFileSystem>();

// Store the actual value of .IsPathFullyQualified(), but return false for this test
fileSystemMock.Setup(x => x.Path.IsPathFullyQualified(It.IsAny<string>())).Callback((string p) =>
{
b = _fileSystem.Path.IsPathFullyQualified(p);
}).Returns(false);

// Intercept the .Combine()
fileSystemMock.Setup(x => x.Path.Combine(It.IsAny<string>(), It.IsAny<string>())).Callback((string p1, string p2) =>
{
ret = _fileSystem.Path.Combine(p1, p2);
// Check the actual value of IsPathFullyQualified. If it is actually true, but we set to false above in the mock
// then the slash was added and needs to be removed at the combine step here.
if (b)
{
ret = ret.TrimStart('/');
}
}).Returns(() => ret);

// This mock of .ReadAllText() just calls the base, actual version
fileSystemMock.Setup(x => x.File.ReadAllText(It.IsAny<string>())).Callback((string p) =>
{
ret = _fileSystem.File.ReadAllText(p);
}).Returns(() => ret);

var statusMock = new Status(fileSystemMock.Object, _logHelper.Object, true);
var requestData = new MockHttpRequestData("");
var res = await statusMock.GetStatus(requestData);

Assert.NotNull(res);
var statusModel = await res.ReadAsJsonAsync<StatusModel>();
Assert.NotNull(statusModel);
Assert.NotNull(statusModel.BuildInfo);

_logHelper.Verify(x => x.Log(
LogLevel.Information,
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((o, t) => string.Equals("Added leading '/' to binDir", o.ToString())),
It.IsAny<Exception>(),
(Func<It.IsAnyType, Exception, string>) It.IsAny<object>()),
Times.Once);
}

[Fact]
public async Task RunsStopwatch()
{
var status = new Status(_fileSystem, _logHelper.Object);
var status = new Status(_logHelper.Object);
var requestData = new MockHttpRequestData("");
var res = await status.GetStatus(requestData);

Expand All @@ -140,23 +87,5 @@ public async Task RunsStopwatch()
Assert.NotNull(statusModel);
Assert.True(statusModel.ExecutionTime >= 0);
}

[Fact]
public async Task HandlesMissingVersionFileException()
{
var fileSystemMock = new Mock<IFileSystem>();
fileSystemMock.Setup(x => x.Path.IsPathFullyQualified(It.IsAny<string>())).Returns(true);
fileSystemMock.Setup(x => x.File.ReadAllText(It.IsAny<string>())).Throws(new Exception());

var statusMock = new Status(fileSystemMock.Object, _logHelper.Object, true);
var requestData = new MockHttpRequestData("");
var res = await statusMock.GetStatus(requestData);

Assert.NotNull(res);
var statusModel = await res.ReadAsJsonAsync<StatusModel>();
Assert.NotNull(statusModel);
Assert.Null(statusModel.BuildInfo);
_logHelper.Verify(LogLevel.Error, Times.Exactly(1));
}
}
}
1 change: 1 addition & 0 deletions TrueVote.Api.sln
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\truevote-api-preprod-integration.yml = .github\workflows\truevote-api-preprod-integration.yml
.github\workflows\truevote-api-version.yml = .github\workflows\truevote-api-version.yml
scripts\update-git.sh = scripts\update-git.sh
scripts\Version_Template.cs = scripts\Version_Template.cs
scripts\version_template.json = scripts\version_template.json
EndProjectSection
EndProject
Expand Down
83 changes: 12 additions & 71 deletions TrueVote.Api/Services/Status.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
using System.Diagnostics;
using System.IO;
using System.IO.Abstractions;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using TrueVote.Api.Helpers;
using TrueVote.Api.Models;
Expand All @@ -21,23 +19,11 @@ namespace TrueVote.Api.Services
{
public class Status : LoggerHelper
{
protected IFileSystem _fileSystem;
public static BuildInfo _BuildInfo = null;
private static BuildInfo _BuildInfo = null;
private static string _BuildInfoReadTime = null;

public Status(IFileSystem fileSystem, ILogger log, bool clearStatics = false): base(log)
public Status(ILogger log): base(log)
{
_fileSystem = fileSystem;
if (clearStatics)
{
ClearStatics();
}
}

private void ClearStatics()
{
_BuildInfo = null;
_BuildInfoReadTime = null;
}

[Function(nameof(GetStatus))]
Expand Down Expand Up @@ -68,25 +54,20 @@ public async Task<HttpResponseData> GetStatus(
status.Responds = true;
status.RespondsMsg = "TrueVote.Api is responding";

// Check if the static is already been initialized. If not, fetch the properties from the version.json file and set them.
// Check if the static is already been initialized. If not, fetch the properties from the Version.cs static.
if (_BuildInfo == null)
{
// Read the version file
var buildInfoString = GetBuildInfo();
if (buildInfoString != null)
{
// Marshall the contents of the version.json file into a model
_BuildInfo = JsonConvert.DeserializeObject<BuildInfo>(buildInfoString);

// Convert the time for consistency
if (_BuildInfo.BuildTime != string.Empty)
{
_BuildInfo.BuildTime = $"{DateTime.Parse(_BuildInfo.BuildTime)} UTC";
}
// Marshall the contents of the Version.cs static into a model
_BuildInfo = JsonConvert.DeserializeObject<BuildInfo>(VersionInfo.BuildInfo);

// Set the read time to now. This should never change because it's stored in a static.
_BuildInfoReadTime = DateTime.Now.ToUniversalTime().ToString("dddd, MMM dd, yyyy HH:mm:ss");
// Convert the time for consistency
if (_BuildInfo.BuildTime != string.Empty)
{
_BuildInfo.BuildTime = $"{DateTime.Parse(_BuildInfo.BuildTime)} UTC";
}

// Set the read time to now. This should never change because it's stored in a static.
_BuildInfoReadTime = DateTime.Now.ToUniversalTime().ToString("dddd, MMM dd, yyyy HH:mm:ss");
}

// Attach the static to the returned object
Expand All @@ -104,45 +85,5 @@ public async Task<HttpResponseData> GetStatus(

return await req.CreateOkResponseAsync(status);
}

private string GetBuildInfo()
{
try
{
var codeBase = new Uri(Assembly.GetExecutingAssembly().Location).ToString();
var binDir = codeBase.Replace(codeBase.Split('/').Last(), "");
binDir = binDir.Remove(binDir.LastIndexOf("bin/"));
if (binDir.Contains(".Tests/")) {
binDir = binDir.Remove(binDir.LastIndexOf(".Tests/"));
}
binDir = binDir.Replace("file:///", "");

LogInformation($"binDir: {binDir}");

// On Linux we may need to add a leading /
if (!_fileSystem.Path.IsPathFullyQualified(binDir))
{
binDir = "/" + binDir;
LogInformation("Added leading '/' to binDir");
LogInformation($"Modified binDir: {binDir}");
}

var versionFile = _fileSystem.Path.Combine(binDir, "version.json");

LogInformation($"Loading build info from: {versionFile}");

var versionContents = _fileSystem.File.ReadAllText(versionFile);

LogInformation($"Loaded build info from version.json");

return versionContents;
}
catch (Exception e)
{
LogError($"Could not load version.json file. Exception: {e.Message}");

return null;
}
}
}
}
4 changes: 2 additions & 2 deletions TrueVote.Api/TrueVote.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Cosmos" Version="7.0.11" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="Microsoft.OpenApi" Version="1.6.8" />
<PackageReference Include="Microsoft.OpenApi" Version="1.6.9" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.19.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.OpenApi" Version="2.0.0-preview2" />
Expand Down Expand Up @@ -75,7 +75,7 @@
<EmbeddedResource Include="dist\favicon-32x32.png" />
<EmbeddedResource Include="dist\favicon.ico" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Target Name="PreBuild" AfterTargets="PreBuildEvent">
<Exec Condition="$(OS) == 'Windows_NT'" Command="cscript.exe ..\scripts\build-version.vbs" />
<Exec Condition="$(OS) != 'Windows_NT'" Command="$(ProjectDir)../scripts/build-version.sh $(ProjectDir) $(TargetDir)" />
</Target>
Expand Down
13 changes: 13 additions & 0 deletions TrueVote.Api/Version.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace TrueVote.Api
{
public static class VersionInfo
{
public static string BuildInfo = @"
{
""Branch"": ""{{branch}}"",
""BuildTime"": ""{{buildtime}}"",
""LastTag"": ""{{lasttag}}"",
""Commit"": ""{{commit}}""
}";
}
}
13 changes: 13 additions & 0 deletions scripts/Version_Template.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace TrueVote.Api
{
public static class VersionInfo
{
public static string BuildInfo = @"
{
""Branch"": ""{{branch}}"",
""BuildTime"": ""{{buildtime}}"",
""LastTag"": ""{{lasttag}}"",
""Commit"": ""{{commit}}""
}";
}
}
26 changes: 24 additions & 2 deletions scripts/build-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,32 @@ echo $template

# Set the path to the output file
projectoutputfile=$DIR/../TrueVote.Api/version.json
echo "Version Output FilePath: " $projectoutputfile
echo "Version Output FilePath (.json): " $projectoutputfile

# Format the replaced JSON and output to version.json file
echo $template | grep -Eo '"[^"]*" *(: *([0-9]*|"[^"]*")[^{}\["]*|,)?|[^"\]\[\}\{]*|\{|\},?|\[|\],?|[0-9 ]*,?' | awk '{if ($0 ~ /^[}\]]/ ) offset-=2; printf "%*c%s\n", offset, " ", $0; if ($0 ~ /^[{\[]/) offset+=2}' > $projectoutputfile
#echo $template | grep -Eo '"[^"]*" *(: *([0-9]*|"[^"]*")[^{}\["]*|,)?|[^"\]\[\}\{]*|\{|\},?|\[|\],?|[0-9 ]*,?' | awk '{if ($0 ~ /^[}\]]/ ) offset-=2; printf "%*c%s\n", offset, " ", $0; if ($0 ~ /^[{\[]/) offset+=2}' > $projectoutputfile
echo $template | sed '$s/ *}$/}/' > $projectoutputfile

# Do the same for the Version.cs file
# Read the template .cs file into a variable for replacements
template2=`cat $DIR/Version_Template.cs`

template2=${template2//\{\{branch\}\}/$branchname}
template2=${template2//\{\{buildtime\}\}/$buildtime}
template2=${template2//\{\{lasttag\}\}/$lasttag}
template2=${template2//\{\{commit\}\}/$commit}
echo $template2

# Set the path to the C# output file
projectoutputfile2=$DIR/../TrueVote.Api/Version.cs
echo "Version Output FilePath (.cs): " $projectoutputfile2

# Output the replaced Version.cs file
echo $template2 > $projectoutputfile2

# Send a Git command to ignore these changes
ignoreversion=`git update-index --assume-unchanged $projectoutputfile2`
echo $ignoreversion

# If command args are passed, call another script
if [ "$1" != "" ]; then
Expand Down

0 comments on commit 27d2f11

Please sign in to comment.