Skip to content

Commit

Permalink
Add route hash
Browse files Browse the repository at this point in the history
  • Loading branch information
sandermvanvliet committed Dec 10, 2023
1 parent 9fc2e53 commit 0ea8c96
Show file tree
Hide file tree
Showing 13 changed files with 486 additions and 13 deletions.
12 changes: 12 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.3",
"commands": [
"dotnet-ef"
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
.Property(r => r.Serialized)
.IsRequired();

modelBuilder
.Entity<Route>()
.Property(r => r.Hash)
.HasDefaultValue("(not yet calculated)")
.IsRequired();

modelBuilder
.Entity<Route>()
.Property(r => r.UserId)
Expand Down
1 change: 1 addition & 0 deletions src/RoadCaptain.App.Web/Adapters/EntityFramework/Route.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ public class Route
public bool IsLoop { get; set; }
public string? Serialized { get; set; }
public string? World { get; set; }
public string Hash { get; set; } = "(not yet calculated)";
}
}
16 changes: 4 additions & 12 deletions src/RoadCaptain.App.Web/Adapters/SqliteRouteStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under Artistic License 2.0
// See LICENSE or https://choosealicense.com/licenses/artistic-2.0/

using System.Security.Cryptography;
using Microsoft.EntityFrameworkCore;
using RoadCaptain.App.Web.Adapters.EntityFramework;
using RoadCaptain.App.Web.Models;
Expand Down Expand Up @@ -173,19 +172,11 @@ public Dictionary<string, long[]> FindDuplicates()
})
.ToList();

string HashIt(string serialized)
{
var serializedBytes = System.Text.Encoding.UTF8.GetBytes(serialized);
var hashBytes = SHA256.HashData(serializedBytes);

return Convert.ToHexString(hashBytes);
}

return allRoutes
.Select(x => new
{
x.Id,
Hash = HashIt(x.Serialized)
Hash = HashUtilities.HashAsHexString(x.Serialized ?? "null")
})
.GroupBy(x => x.Hash,
x => x.Id,
Expand Down Expand Up @@ -214,7 +205,7 @@ private static Models.RouteModel RouteModelFrom(Route route)
};
}

private Route RouteStorageModelFrom(CreateRouteModel createModel, User user)
private static Route RouteStorageModelFrom(CreateRouteModel createModel, User user)
{
return new Route
{
Expand All @@ -225,7 +216,8 @@ private Route RouteStorageModelFrom(CreateRouteModel createModel, User user)
Distance = createModel.Distance,
IsLoop = createModel.IsLoop,
ZwiftRouteName = createModel.ZwiftRouteName,
Serialized = createModel.Serialized
Serialized = createModel.Serialized,
Hash = HashUtilities.HashAsHexString(createModel.Serialized!)
};
}
}
Expand Down
20 changes: 20 additions & 0 deletions src/RoadCaptain.App.Web/HashUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Security.Cryptography;

namespace RoadCaptain.App.Web
{
internal class HashUtilities
{
public static string HashAsHexString(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentException("Input was empty", nameof(input));
}

var serializedBytes = System.Text.Encoding.UTF8.GetBytes(input);
var hashBytes = SHA256.HashData(serializedBytes);

return Convert.ToHexString(hashBytes);
}
}
}
21 changes: 20 additions & 1 deletion src/RoadCaptain.App.Web/MainModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See LICENSE or https://choosealicense.com/licenses/artistic-2.0/

using Autofac;
using Microsoft.EntityFrameworkCore;
using RoadCaptain.App.Web.Adapters;
using RoadCaptain.App.Web.Adapters.EntityFramework;
using RoadCaptain.App.Web.Ports;
Expand Down Expand Up @@ -30,7 +31,25 @@ protected override void Load(ContainerBuilder builder)
.RegisterType<RoadCaptainDataContext>()
.AsSelf()
.InstancePerLifetimeScope()
.OnActivated(args => args.Instance.Database.EnsureCreated());
.OnActivated(args =>
{
// If the database already exists we need to add the initial migration
// to it and pretend it has already run (which it did but I forgot to
// add migrations...)
try
{
args.Instance.Database.ExecuteSqlRaw(
@"INSERT INTO __EFMigrationsHistory
SELECT '20231210130249_InitialSchema', '7.0.3'
WHERE NOT EXISTS(SELECT 1 FROM __EFMigrationsHistory WHERE MigrationId = '20231210130249_InitialSchema')");
}
catch
{
//Nop
}
args.Instance.Database.Migrate();
});
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions src/RoadCaptain.App.Web/Migrations/20231210130249_InitialSchema.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace RoadCaptain.App.Web.Migrations
{
/// <inheritdoc />
public partial class InitialSchema : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<long>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
Name = table.Column<string>(type: "TEXT", nullable: false),
ZwiftSubject = table.Column<string>(type: "TEXT", nullable: false),
ZwiftProfileId = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});

migrationBuilder.CreateTable(
name: "Routes",
columns: table => new
{
Id = table.Column<long>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<long>(type: "INTEGER", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: true),
ZwiftRouteName = table.Column<string>(type: "TEXT", nullable: true),
Distance = table.Column<decimal>(type: "TEXT", nullable: false),
Ascent = table.Column<decimal>(type: "TEXT", nullable: false),
Descent = table.Column<decimal>(type: "TEXT", nullable: false),
IsLoop = table.Column<bool>(type: "INTEGER", nullable: false),
Serialized = table.Column<string>(type: "TEXT", nullable: false),
World = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Routes", x => x.Id);
table.ForeignKey(
name: "FK_Routes_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});

migrationBuilder.CreateIndex(
name: "IX_Routes_UserId",
table: "Routes",
column: "UserId");
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Routes");

migrationBuilder.DropTable(
name: "Users");
}
}
}
Loading

0 comments on commit 0ea8c96

Please sign in to comment.