Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit b1ee1bd

Browse files
committed
Patch changes from PR30
1 parent 60c1cc5 commit b1ee1bd

File tree

14 files changed

+567
-44
lines changed

14 files changed

+567
-44
lines changed

.azuredevops/pipelines/cd-infrastructure-dev-audit.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ resources:
1414
- repository: dtos-devops-templates
1515
type: github
1616
name: NHSDigital/dtos-devops-templates
17-
ref: 9673ee4ef9770e80d0714c3966a699414b7b43c7
17+
ref: cf5e22fe4614b7d077a22301d29883e86ac3defc
1818
endpoint: NHSDigital
1919

2020
variables:

compose.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ services:
131131
DATABASE_NAME: "${DATABASE_NAME}"
132132
DATABASE_USER: "${DATABASE_USER}"
133133
DATABASE_PASSWORD: "${DATABASE_PASSWORD}"
134-
env_file:
135-
- ./.env.example
136134
networks:
137135
- backend
138136

infrastructure/tf-core/environments/development.tfvars

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ function_apps = {
192192
static = {
193193
MeshApiBaseUrl = "https://msg.intspineservices.nhs.uk"
194194
FileDiscoveryTimerExpression = "0 */5 * * * *"
195-
MeshHandshakeTimerExpression = "0 0 0 * * * "
195+
MeshHandshakeTimerExpression = "0 0 0 * * *"
196196
FileRetryTimerExpression = "0 0 * * * *"
197197
FileExtractQueueName = "file-extract"
198198
FileTransformQueueName = "file-transform"
@@ -252,14 +252,14 @@ storage_accounts = {
252252
fnapp = {
253253
name_suffix = "fnappstor"
254254
account_tier = "Standard"
255-
replication_type = "ZRS"
255+
replication_type = "LRS"
256256
public_network_access_enabled = false
257257
containers = {}
258258
}
259259
mesh = {
260260
name_suffix = "meshstor"
261261
account_tier = "Standard"
262-
replication_type = "ZRS"
262+
replication_type = "LRS"
263263
public_network_access_enabled = true
264264
blob_properties_delete_retention_policy = 7
265265
blob_properties_versioning_enabled = false

infrastructure/tf-core/environments/integration.tfvars

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ function_apps = {
153153
app_service_logs_disk_quota_mb = 35
154154
app_service_logs_retention_period_days = 7
155155
always_on = true
156+
cont_registry_use_mi = false
156157
docker_env_tag = "integration"
157158
docker_img_prefix = "service-layer"
158159
enable_appsrv_storage = "false"
@@ -165,8 +166,48 @@ function_apps = {
165166

166167
function_app_config = {
167168

169+
ServiceLayerAPI = {
170+
name_suffix = "svclyr-api"
171+
function_endpoint_name = "ServiceLayerAPI"
172+
app_service_plan_key = "Default"
173+
env_vars = {
174+
static = {
175+
# env_var_name = value
176+
}
177+
from_key_vault = {
178+
# env_var_name = "key_vault_secret_name"
179+
}
180+
local_urls = {
181+
# %s becomes the environment and region prefix (e.g. dev-uks)
182+
}
183+
}
184+
}
168185

169-
186+
ServiceLayerMesh = {
187+
name_suffix = "svclyr-mesh-ingest"
188+
function_endpoint_name = "ServiceLayerMesh"
189+
app_service_plan_key = "Default"
190+
db_connection_string = "DatabaseConnectionString"
191+
env_vars = {
192+
static = {
193+
MeshApiBaseUrl = "https://msg.intspineservices.nhs.uk"
194+
FileDiscoveryTimerExpression = "0 */5 * * * *"
195+
MeshHandshakeTimerExpression = "0 0 0 * * *"
196+
FileRetryTimerExpression = "0 0 * * * *"
197+
FileExtractQueueName = "file-extract"
198+
FileTransformQueueName = "file-transform"
199+
StaleHours = "12"
200+
MeshBlobContainerName = "incoming-mesh-files"
201+
MeshBlobStorageUrl = "https://stsvclyrintuksmeshstor.blob.core.windows.net"
202+
MeshQueueStorageUrl = "https://stsvclyrintuksmeshstor.queue.core.windows.net"
203+
}
204+
from_key_vault = {
205+
MeshPassword = "MeshPassword"
206+
MeshSharedKey = "MeshSharedKey"
207+
NbssMailboxId = "NbssMailboxId"
208+
}
209+
}
210+
}
170211
}
171212
}
172213

@@ -192,9 +233,9 @@ sqlserver = {
192233
azure_services_access_enabled = true
193234
}
194235

195-
# parman database
236+
# svclyr database
196237
dbs = {
197-
parman = {
238+
svclyr = {
198239
db_name_suffix = "service_layer_database"
199240
collation = "SQL_Latin1_General_CP1_CI_AS"
200241
licence_type = "LicenseIncluded"
@@ -215,17 +256,23 @@ storage_accounts = {
215256
public_network_access_enabled = false
216257
containers = {}
217258
}
218-
# webapp = {
219-
# name_suffix = "webappstor"
220-
# account_tier = "Standard"
221-
# replication_type = "LRS"
222-
# public_network_access_enabled = true
223-
# blob_properties_delete_retention_policy = 7
224-
# blob_properties_versioning_enabled = false
225-
# containers = {
226-
# webapp = {
227-
# container_name = "webapp"
228-
# }
229-
# }
230-
# }
259+
mesh = {
260+
name_suffix = "meshstor"
261+
account_tier = "Standard"
262+
replication_type = "LRS"
263+
public_network_access_enabled = true
264+
blob_properties_delete_retention_policy = 7
265+
blob_properties_versioning_enabled = false
266+
containers = {
267+
incoming = {
268+
container_name = "incoming-mesh-files"
269+
}
270+
}
271+
queues = [
272+
"file-extract",
273+
"file-extract-poison",
274+
"file-transform",
275+
"file-transform-poison"
276+
]
277+
}
231278
}

infrastructure/tf-core/environments/nft.tfvars

Lines changed: 63 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ function_apps = {
153153
app_service_logs_disk_quota_mb = 35
154154
app_service_logs_retention_period_days = 7
155155
always_on = true
156+
cont_registry_use_mi = false
156157
docker_env_tag = "nft"
157158
docker_img_prefix = "service-layer"
158159
enable_appsrv_storage = "false"
@@ -165,8 +166,48 @@ function_apps = {
165166

166167
function_app_config = {
167168

169+
ServiceLayerAPI = {
170+
name_suffix = "svclyr-api"
171+
function_endpoint_name = "ServiceLayerAPI"
172+
app_service_plan_key = "Default"
173+
env_vars = {
174+
static = {
175+
# env_var_name = value
176+
}
177+
from_key_vault = {
178+
# env_var_name = "key_vault_secret_name"
179+
}
180+
local_urls = {
181+
# %s becomes the environment and region prefix (e.g. dev-uks)
182+
}
183+
}
184+
}
168185

169-
186+
ServiceLayerMesh = {
187+
name_suffix = "svclyr-mesh-ingest"
188+
function_endpoint_name = "ServiceLayerMesh"
189+
app_service_plan_key = "Default"
190+
db_connection_string = "DatabaseConnectionString"
191+
env_vars = {
192+
static = {
193+
MeshApiBaseUrl = "https://msg.intspineservices.nhs.uk"
194+
FileDiscoveryTimerExpression = "0 */5 * * * *"
195+
MeshHandshakeTimerExpression = "0 0 0 * * *"
196+
FileRetryTimerExpression = "0 0 * * * *"
197+
FileExtractQueueName = "file-extract"
198+
FileTransformQueueName = "file-transform"
199+
StaleHours = "12"
200+
MeshBlobContainerName = "incoming-mesh-files"
201+
MeshBlobStorageUrl = "https://stsvclyrnftuksmeshstor.blob.core.windows.net"
202+
MeshQueueStorageUrl = "https://stsvclyrnftuksmeshstor.queue.core.windows.net"
203+
}
204+
from_key_vault = {
205+
MeshPassword = "MeshPassword"
206+
MeshSharedKey = "MeshSharedKey"
207+
NbssMailboxId = "NbssMailboxId"
208+
}
209+
}
210+
}
170211
}
171212
}
172213

@@ -192,9 +233,9 @@ sqlserver = {
192233
azure_services_access_enabled = true
193234
}
194235

195-
# parman database
236+
# svclyr database
196237
dbs = {
197-
parman = {
238+
svclyr = {
198239
db_name_suffix = "service_layer_database"
199240
collation = "SQL_Latin1_General_CP1_CI_AS"
200241
licence_type = "LicenseIncluded"
@@ -215,17 +256,23 @@ storage_accounts = {
215256
public_network_access_enabled = false
216257
containers = {}
217258
}
218-
# webapp = {
219-
# name_suffix = "webappstor"
220-
# account_tier = "Standard"
221-
# replication_type = "LRS"
222-
# public_network_access_enabled = true
223-
# blob_properties_delete_retention_policy = 7
224-
# blob_properties_versioning_enabled = false
225-
# containers = {
226-
# webapp = {
227-
# container_name = "webapp"
228-
# }
229-
# }
230-
# }
259+
mesh = {
260+
name_suffix = "meshstor"
261+
account_tier = "Standard"
262+
replication_type = "LRS"
263+
public_network_access_enabled = true
264+
blob_properties_delete_retention_policy = 7
265+
blob_properties_versioning_enabled = false
266+
containers = {
267+
incoming = {
268+
container_name = "incoming-mesh-files"
269+
}
270+
}
271+
queues = [
272+
"file-extract",
273+
"file-extract-poison",
274+
"file-transform",
275+
"file-transform-poison"
276+
]
277+
}
231278
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System.Text.RegularExpressions;
2+
using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Models;
3+
4+
namespace ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation;
5+
6+
public partial class FileValidator : IFileValidator
7+
{
8+
private readonly HeaderFieldRegexValidator _headerExtractIdValidator = new(
9+
x => x.ExtractId, "Extract ID", ExtractIdRegex(),
10+
ErrorCodes.MissingExtractId, ErrorCodes.InvalidExtractId);
11+
12+
private readonly HeaderFieldRegexValidator _headerIdRecordCountValidator = new(
13+
x => x.RecordCount, "Record count", RecordCountRegex(),
14+
ErrorCodes.MissingRecordCount, ErrorCodes.InvalidRecordCount);
15+
16+
public IEnumerable<ValidationError> Validate(ParsedFile file)
17+
{
18+
return ValidateHeaderPresence(file)
19+
.Concat(ValidateTrailerPresence(file))
20+
.Concat(ValidateExtractId(file))
21+
.Concat(ValidateRecordCount(file));
22+
}
23+
24+
private static IEnumerable<ValidationError> ValidateHeaderPresence(ParsedFile file)
25+
{
26+
if (file.FileHeader == null)
27+
{
28+
yield return new ValidationError
29+
{
30+
Code = ErrorCodes.MissingHeader,
31+
Error = "Header is missing",
32+
Scope = ValidationErrorScope.File
33+
};
34+
}
35+
}
36+
37+
private static IEnumerable<ValidationError> ValidateTrailerPresence(ParsedFile file)
38+
{
39+
if (file.FileTrailer == null)
40+
{
41+
yield return new ValidationError
42+
{
43+
Code = ErrorCodes.MissingTrailer,
44+
Error = "Trailer is missing",
45+
Scope = ValidationErrorScope.File
46+
};
47+
}
48+
}
49+
50+
private IEnumerable<ValidationError> ValidateExtractId(ParsedFile file)
51+
{
52+
if (file.FileHeader == null) yield break;
53+
54+
foreach (var error in _headerExtractIdValidator.Validate(file))
55+
{
56+
yield return error;
57+
}
58+
59+
if (file.FileTrailer != null && file.FileHeader.ExtractId != file.FileTrailer.ExtractId)
60+
{
61+
yield return new ValidationError
62+
{
63+
Field = "Extract ID",
64+
Code = ErrorCodes.InconsistentExtractId,
65+
Error = "Extract ID does not match value in header",
66+
Scope = ValidationErrorScope.Trailer
67+
};
68+
}
69+
}
70+
71+
private IEnumerable<ValidationError> ValidateRecordCount(ParsedFile file)
72+
{
73+
if (file.FileHeader == null) yield break;
74+
75+
var headerRecordCountErrors = _headerIdRecordCountValidator.Validate(file).ToList();
76+
77+
foreach (var error in headerRecordCountErrors)
78+
{
79+
yield return error;
80+
}
81+
82+
if (file.FileTrailer != null && file.FileHeader.RecordCount != file.FileTrailer.RecordCount)
83+
{
84+
yield return new ValidationError
85+
{
86+
Field = "Record count",
87+
Code = ErrorCodes.InconsistentRecordCount,
88+
Error = "Record count does not match value in header",
89+
Scope = ValidationErrorScope.Trailer
90+
};
91+
}
92+
else if (headerRecordCountErrors.Count == 0 &&
93+
file.DataRecords.Count != int.Parse(file.FileHeader.RecordCount!))
94+
{
95+
yield return new ValidationError
96+
{
97+
Code = ErrorCodes.UnexpectedRecordCount,
98+
Error = "Record count does not match value in header and trailer",
99+
Scope = ValidationErrorScope.File
100+
};
101+
}
102+
}
103+
104+
[GeneratedRegex(@"^\d{8}$")]
105+
private static partial Regex ExtractIdRegex();
106+
107+
[GeneratedRegex(@"^(?!000000)\d{6}$")]
108+
private static partial Regex RecordCountRegex();
109+
}

0 commit comments

Comments
 (0)