Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions .github/workflows/stage-2-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ jobs:
- name: "Checkout code"
uses: actions/checkout@v4
with:
submodules: 'true'
fetch-depth: 0 # Full history is needed to improving relevancy of reporting
- name: "Perform static analysis"
uses: ./.github/actions/perform-static-analysis
Expand Down
5 changes: 5 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[submodule "src/dotnet-mesh-client"]
# path = src/Shared/dotnet-mesh-client
Comment thread
SamTyrrellNHS marked this conversation as resolved.
path = src/dotnet-mesh-client
url = https://github.com/NHSDigital/dotnet-mesh-client.git
branch = main
2 changes: 2 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"recommendations": [
"ms-azuretools.vscode-azurefunctions",
"ms-dotnettools.csharp",
"alefragnani.bookmarks",
"davidanson.vscode-markdownlint",
"dbaeumer.vscode-eslint",
Expand Down
22 changes: 22 additions & 0 deletions src/ServiceLayer.Mesh/Data/DesignTimeDbContextFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using ServiceLayer.Mesh.Data;

namespace ParticipantManager.API.Data;

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<ServiceLayerDbContext>
{
public ServiceLayerDbContext CreateDbContext(string[] args)
{
var connectionString = Environment.GetEnvironmentVariable("DatabaseConnectionString");
if (string.IsNullOrEmpty(connectionString))
{
throw new InvalidOperationException("Connection string 'DatabaseConnectionString' is not configured.");
}

var optionsBuilder = new DbContextOptionsBuilder<ServiceLayerDbContext>();
optionsBuilder.UseSqlServer(connectionString);

return new ServiceLayerDbContext(optionsBuilder.Options);
}
}
17 changes: 17 additions & 0 deletions src/ServiceLayer.Mesh/Data/ServiceLayerDbConext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using ServiceLayer.Mesh.Models;

namespace ServiceLayer.Mesh.Data;

public class ServiceLayerDbContext(DbContextOptions<ServiceLayerDbContext> options) : DbContext(options)
{
public DbSet<MeshFile> MeshFiles { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configure relationships, keys, etc.
modelBuilder.Entity<MeshFile>().HasKey(p => p.FileId);
modelBuilder.Entity<MeshFile>().Property(e => e.Status).HasConversion<string>();
modelBuilder.Entity<MeshFile>().Property(e => e.FileType).HasConversion<string>();
}
}
69 changes: 69 additions & 0 deletions src/ServiceLayer.Mesh/Functions/DiscoveryFunction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Azure.Storage.Queues;
using Microsoft.Azure.Functions.Worker;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using NHS.MESH.Client.Contracts.Services;
using ServiceLayer.Mesh.Data;
using ServiceLayer.Mesh.Models;

namespace ServiceLayer.Mesh.Functions
{
public class DiscoveryFunction
{
private readonly ILogger _logger;
private readonly IMeshInboxService _meshInboxService;
private readonly ServiceLayerDbContext _serviceLayerDbContext;
private readonly QueueClient _queueClient;

public DiscoveryFunction(ILogger<DiscoveryFunction> logger, IMeshInboxService meshInboxService, ServiceLayerDbContext serviceLayerDbContext, QueueClient queueClient)
{
_logger = logger;
_meshInboxService = meshInboxService;
_serviceLayerDbContext = serviceLayerDbContext;
_queueClient = queueClient;
}

[Function("DiscoveryFunction")]
public async Task Run([TimerTrigger("%DiscoveryTimerExpression%")] TimerInfo myTimer)
{
_logger.LogInformation($"DiscoveryFunction started at: {DateTime.Now}");

var mailboxId = Environment.GetEnvironmentVariable("BSSMailBox")
?? throw new InvalidOperationException($"Environment variable 'BSSMailBox' is not set or is empty.");

var response = await _meshInboxService.GetMessagesAsync(mailboxId);

_queueClient.CreateIfNotExists();

foreach (var messageId in response.Response.Messages)
{
using var transaction = await _serviceLayerDbContext.Database.BeginTransactionAsync();

var existing = await _serviceLayerDbContext.MeshFiles
.AnyAsync(f => f.FileId == messageId);

if (!existing)
{
_serviceLayerDbContext.MeshFiles.Add(new MeshFile
{
FileId = messageId,
FileType = MeshFileType.NbssAppointmentEvents,
MailboxId = mailboxId,
Status = MeshFileStatus.Discovered,
FirstSeenUtc = DateTime.UtcNow,
LastUpdatedUtc = DateTime.UtcNow
});

await _serviceLayerDbContext.SaveChangesAsync();
await transaction.CommitAsync();
Comment thread
SamTyrrellNHS marked this conversation as resolved.

_queueClient.SendMessage(messageId);
Comment thread
SamTyrrellNHS marked this conversation as resolved.
}
else
{
await transaction.RollbackAsync();
}
}
}
}
}

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

39 changes: 39 additions & 0 deletions src/ServiceLayer.Mesh/Migrations/20250512113115_InitialCreate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace ServiceLayer.Mesh.Migrations
{
/// <inheritdoc />
public partial class InitialCreate : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "MeshFiles",
columns: table => new
{
FileId = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: false),
FileType = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
MailboxId = table.Column<string>(type: "nvarchar(50)", maxLength: 50, nullable: false),
Status = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false),
BlobPath = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: true),
FirstSeenUtc = table.Column<DateTime>(type: "datetime2", nullable: false),
LastUpdatedUtc = table.Column<DateTime>(type: "datetime2", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MeshFiles", x => x.FileId);
});
}

/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MeshFiles");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using ServiceLayer.Mesh.Data;

#nullable disable

namespace ServiceLayer.Mesh.Migrations
{
[DbContext(typeof(ServiceLayerDbContext))]
partial class ServiceLayerDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 128);

SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);

modelBuilder.Entity("ServiceLayer.Mesh.Models.MeshFile", b =>
{
b.Property<string>("FileId")
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");

b.Property<string>("BlobPath")
.HasMaxLength(1024)
.HasColumnType("nvarchar(1024)");

b.Property<string>("FileType")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");

b.Property<DateTime>("FirstSeenUtc")
.HasColumnType("datetime2");

b.Property<DateTime>("LastUpdatedUtc")
.HasColumnType("datetime2");

b.Property<string>("MailboxId")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("nvarchar(50)");

b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("nvarchar(20)");

b.HasKey("FileId");

b.ToTable("MeshFiles");
});
#pragma warning restore 612, 618
}
}
}
20 changes: 20 additions & 0 deletions src/ServiceLayer.Mesh/Models/MeshFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace ServiceLayer.Mesh.Models;

public class MeshFile
{
[MaxLength(255)]
public required string FileId { get; set; }
[MaxLength(50)]
public required MeshFileType FileType { get; set; }
[MaxLength(50)]
public required string MailboxId { get; set; }
[MaxLength(20)]
public required MeshFileStatus Status { get; set; }
[MaxLength(1024)]
public string? BlobPath { get; set; }
public DateTime FirstSeenUtc { get; set; }
public DateTime LastUpdatedUtc { get; set; }
}
12 changes: 12 additions & 0 deletions src/ServiceLayer.Mesh/Models/MeshFileStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace ServiceLayer.Mesh.Models;

public enum MeshFileStatus
{
Discovered,
Extracting,
Extracted,
Transforming,
Transformed,
FailedExtract,
FailedTransform
}
6 changes: 6 additions & 0 deletions src/ServiceLayer.Mesh/Models/MeshFileType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ServiceLayer.Mesh.Models;

public enum MeshFileType
{
NbssAppointmentEvents
}
Loading
Loading