diff --git a/.github/workflows/createrelease.yml b/.github/workflows/createrelease.yml
index 9c40669..b67e0ae 100644
--- a/.github/workflows/createrelease.yml
+++ b/.github/workflows/createrelease.yml
@@ -71,6 +71,47 @@ jobs:
name: "Deploy to Staging"
steps:
+ - uses: actions/checkout@v4
+
+ - name: Install scheduled tasks
+ shell: pwsh
+ env:
+ SCHEDULED_TASK_USER: ${{ secrets.SCHEDULED_TASK_USER }}
+ SCHEDULED_TASK_PASSWORD: ${{ secrets.SCHEDULED_TASK_PASSWORD }}
+ run: |
+ $taskScriptDir = 'C:\txnproc\transactionprocessing\powershell'
+ $scheduledTasksRoot = Join-Path $env:GITHUB_WORKSPACE 'ScheduledTasks'
+
+ New-Item -ItemType Directory -Path $taskScriptDir -Force | Out-Null
+
+ Get-ChildItem -Path $scheduledTasksRoot -Recurse -Filter *.ps1 |
+ Where-Object { $_.FullName -notmatch '\\Tests\\' } |
+ Copy-Item -Destination $taskScriptDir -Force
+
+ Get-ChildItem -Path $scheduledTasksRoot -Recurse -Filter *.xml |
+ Where-Object { $_.FullName -notmatch '\\Tests\\' } |
+ ForEach-Object {
+ [xml]$taskDefinition = Get-Content -Path $_.FullName -Raw
+ $taskName = [string]$taskDefinition.Task.RegistrationInfo.URI
+
+ if ([string]::IsNullOrWhiteSpace($taskName)) {
+ $taskName = $_.BaseName
+ }
+
+ $registerParams = @{
+ Xml = $taskDefinition.OuterXml
+ TaskName = $taskName.TrimStart('\')
+ TaskPath = '\'
+ Force = $true
+ }
+
+ if ($env:SCHEDULED_TASK_USER -and $env:SCHEDULED_TASK_PASSWORD) {
+ $registerParams.User = $env:SCHEDULED_TASK_USER
+ $registerParams.Password = $env:SCHEDULED_TASK_PASSWORD
+ }
+
+ Register-ScheduledTask @registerParams | Out-Null
+ }
- name: Download the artifact (Merchant Pos)
uses: actions/download-artifact@v4.1.8
@@ -131,6 +172,47 @@ jobs:
name: "Deploy to Production"
steps:
+ - uses: actions/checkout@v4
+
+ - name: Install scheduled tasks
+ shell: pwsh
+ env:
+ SCHEDULED_TASK_USER: ${{ secrets.SCHEDULED_TASK_USER }}
+ SCHEDULED_TASK_PASSWORD: ${{ secrets.SCHEDULED_TASK_PASSWORD }}
+ run: |
+ $taskScriptDir = 'C:\txnproc\transactionprocessing\powershell'
+ $scheduledTasksRoot = Join-Path $env:GITHUB_WORKSPACE 'ScheduledTasks'
+
+ New-Item -ItemType Directory -Path $taskScriptDir -Force | Out-Null
+
+ Get-ChildItem -Path $scheduledTasksRoot -Recurse -Filter *.ps1 |
+ Where-Object { $_.FullName -notmatch '\\Tests\\' } |
+ Copy-Item -Destination $taskScriptDir -Force
+
+ Get-ChildItem -Path $scheduledTasksRoot -Recurse -Filter *.xml |
+ Where-Object { $_.FullName -notmatch '\\Tests\\' } |
+ ForEach-Object {
+ [xml]$taskDefinition = Get-Content -Path $_.FullName -Raw
+ $taskName = [string]$taskDefinition.Task.RegistrationInfo.URI
+
+ if ([string]::IsNullOrWhiteSpace($taskName)) {
+ $taskName = $_.BaseName
+ }
+
+ $registerParams = @{
+ Xml = $taskDefinition.OuterXml
+ TaskName = $taskName.TrimStart('\')
+ TaskPath = '\'
+ Force = $true
+ }
+
+ if ($env:SCHEDULED_TASK_USER -and $env:SCHEDULED_TASK_PASSWORD) {
+ $registerParams.User = $env:SCHEDULED_TASK_USER
+ $registerParams.Password = $env:SCHEDULED_TASK_PASSWORD
+ }
+
+ Register-ScheduledTask @registerParams | Out-Null
+ }
- name: Remove existing Windows service (Merchant Pos)
run: |
diff --git a/.github/workflows/powershell_nightly.yml b/.github/workflows/powershell_nightly.yml
index f9887bd..4e091cb 100644
--- a/.github/workflows/powershell_nightly.yml
+++ b/.github/workflows/powershell_nightly.yml
@@ -24,4 +24,8 @@ jobs:
- name: Run Pester Tests
shell: pwsh
run: |
- Invoke-Pester -Path ./ScheduledTasks/SettlementProcessing/Scripts/Tests -CI
+ Invoke-Pester -Path @(
+ './ScheduledTasks/Tests'
+ './ScheduledTasks/ReplayParkedQueue/Tests'
+ './ScheduledTasks/SettlementProcessing/Tests'
+ ) -CI
diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml
index 33fab4d..40f6d6d 100644
--- a/.github/workflows/pullrequest.yml
+++ b/.github/workflows/pullrequest.yml
@@ -53,4 +53,8 @@ jobs:
- name: Run Pester Tests
shell: pwsh
run: |
- Invoke-Pester -Path ./ScheduledTasks/SettlementProcessing/Tests -CI
+ Invoke-Pester -Path @(
+ './ScheduledTasks/Tests'
+ './ScheduledTasks/ReplayParkedQueue/Tests'
+ './ScheduledTasks/SettlementProcessing/Tests'
+ ) -CI
diff --git a/NuGet.Config b/NuGet.Config
new file mode 100644
index 0000000..fa626cc
--- /dev/null
+++ b/NuGet.Config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Scripts/Scavenge.xml b/ScheduledTasks/AutoScavenge/Scavenge.xml
similarity index 100%
rename from Scripts/Scavenge.xml
rename to ScheduledTasks/AutoScavenge/Scavenge.xml
diff --git a/Scripts/scavenge.ps1 b/ScheduledTasks/AutoScavenge/scavenge.ps1
similarity index 100%
rename from Scripts/scavenge.ps1
rename to ScheduledTasks/AutoScavenge/scavenge.ps1
diff --git a/ScheduledTasks/ReplayParkedQueue/ReplayParkedQueue.ps1 b/ScheduledTasks/ReplayParkedQueue/ReplayParkedQueue.ps1
index 1120f78..dda6cf4 100644
--- a/ScheduledTasks/ReplayParkedQueue/ReplayParkedQueue.ps1
+++ b/ScheduledTasks/ReplayParkedQueue/ReplayParkedQueue.ps1
@@ -2,11 +2,8 @@
[Parameter(Mandatory = $true)]
[string]$BaseUrl,
- [Parameter]
- [string]$Username,
-
- [Parameter]
- [string]$Password,
+ [Parameter()]
+ [System.Management.Automation.PSCredential]$Credential,
[string]$LogDirectory = "C:\home\txnproc\trace",
[int]$LogRetentionDays = 7
@@ -63,10 +60,15 @@ Write-Trace "INFO" "Script started. BaseUrl=$BaseUrl"
# =========================
# Auth header
# =========================
-$AuthHeader = @{
- Authorization = "Basic " +
+$AuthHeader = @{}
+if ($null -ne $Credential) {
+ $networkCredential = $Credential.GetNetworkCredential()
+ $username = $networkCredential.UserName
+ $password = $networkCredential.Password
+
+ $AuthHeader.Authorization = "Basic " +
[Convert]::ToBase64String(
- [Text.Encoding]::ASCII.GetBytes("$Username`:$Password")
+ [Text.Encoding]::ASCII.GetBytes("$username`:$password")
)
}
@@ -140,4 +142,4 @@ foreach ($sub in $subscriptions) {
#}
}
-Write-Trace "INFO" "Script completed"
\ No newline at end of file
+Write-Trace "INFO" "Script completed"
diff --git a/ScheduledTasks/ReplayParkedQueue/Tests/ReplayParkedQueue.Tests.ps1 b/ScheduledTasks/ReplayParkedQueue/Tests/ReplayParkedQueue.Tests.ps1
new file mode 100644
index 0000000..f17cf70
--- /dev/null
+++ b/ScheduledTasks/ReplayParkedQueue/Tests/ReplayParkedQueue.Tests.ps1
@@ -0,0 +1,139 @@
+Describe 'ReplayParkedQueue.ps1' {
+ It 'replays parked messages for subscriptions with parked counts and encodes $all streams' {
+ $scriptUnderTest = (Resolve-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'ReplayParkedQueue.ps1')).Path
+ $logDirectory = Join-Path $TestDrive 'replay-logs-1'
+ $securePassword = [System.Security.SecureString]::new()
+ foreach ($code in 115, 101, 99, 114, 101, 116) {
+ $securePassword.AppendChar([char]$code)
+ }
+ $credential = [pscredential]::new('alice', $securePassword)
+ $subscriptions = @(
+ [pscustomobject]@{
+ eventStreamId = 'stream-1'
+ groupName = 'group-1'
+ },
+ [pscustomobject]@{
+ eventStreamId = '$all'
+ groupName = 'group-2'
+ }
+ )
+ $captured = [pscustomobject]@{
+ SubscriptionCalls = 0
+ InfoUris = [System.Collections.Generic.List[string]]::new()
+ ReplayUris = [System.Collections.Generic.List[string]]::new()
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.SubscriptionCalls++
+ $subscriptions
+ } -ParameterFilter {
+ $Method -eq 'GET' -and
+ $Uri -eq 'https://queue.example/subscriptions'
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.InfoUris.Add($Uri) | Out-Null
+ @{ parkedMessageCount = 2 }
+ } -ParameterFilter {
+ $Method -eq 'GET' -and
+ $Uri -eq 'https://queue.example/subscriptions/stream-1/group-1/info'
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.InfoUris.Add($Uri) | Out-Null
+ @{ parkedMessageCount = 0 }
+ } -ParameterFilter {
+ $Method -eq 'GET' -and
+ $Uri -eq 'https://queue.example/subscriptions/%24all/group-2/info'
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.ReplayUris.Add($Uri) | Out-Null
+ } -ParameterFilter {
+ $Method -eq 'POST' -and
+ $Uri -eq 'https://queue.example/subscriptions/stream-1/group-1/replayParked?from=0'
+ }
+
+ & $scriptUnderTest 'https://queue.example' -Credential $credential -LogDirectory $logDirectory
+
+ $captured.SubscriptionCalls | Should -Be 1
+ $captured.InfoUris.Count | Should -Be 2
+ $captured.InfoUris | Should -Be @(
+ 'https://queue.example/subscriptions/stream-1/group-1/info',
+ 'https://queue.example/subscriptions/%24all/group-2/info'
+ )
+ $captured.ReplayUris | Should -Be @(
+ 'https://queue.example/subscriptions/stream-1/group-1/replayParked?from=0'
+ )
+ }
+
+ It 'does not post a replay when no subscription has parked messages' {
+ $scriptUnderTest = (Resolve-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'ReplayParkedQueue.ps1')).Path
+ $logDirectory = Join-Path $TestDrive 'replay-logs-2'
+ $securePassword = [System.Security.SecureString]::new()
+ foreach ($code in 115, 101, 99, 114, 101, 116) {
+ $securePassword.AppendChar([char]$code)
+ }
+ $credential = [pscredential]::new('alice', $securePassword)
+ $subscriptions = @(
+ [pscustomobject]@{
+ eventStreamId = 'stream-1'
+ groupName = 'group-1'
+ }
+ )
+ $captured = [pscustomobject]@{
+ SubscriptionCalls = 0
+ InfoCalls = 0
+ ReplayCalls = 0
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.SubscriptionCalls++
+ $subscriptions
+ } -ParameterFilter {
+ $Method -eq 'GET' -and
+ $Uri -eq 'https://queue.example/subscriptions'
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.InfoCalls++
+ @{ parkedMessageCount = 0 }
+ } -ParameterFilter {
+ $Method -eq 'GET' -and
+ $Uri -eq 'https://queue.example/subscriptions/stream-1/group-1/info'
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.ReplayCalls++
+ } -ParameterFilter {
+ $Method -eq 'POST' -and
+ $Uri -like 'https://queue.example/subscriptions/*/*/replayParked?from=0'
+ }
+
+ & $scriptUnderTest 'https://queue.example' -Credential $credential -LogDirectory $logDirectory
+
+ $captured.SubscriptionCalls | Should -Be 1
+ $captured.InfoCalls | Should -Be 1
+ $captured.ReplayCalls | Should -Be 0
+ }
+
+ It 'does not add an Authorization header when no credential is supplied' {
+ $scriptUnderTest = (Resolve-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'ReplayParkedQueue.ps1')).Path
+ $logDirectory = Join-Path $TestDrive 'replay-logs-3'
+ $captured = [pscustomobject]@{
+ Headers = $null
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.Headers = $Headers
+ @()
+ } -ParameterFilter {
+ $Method -eq 'GET' -and
+ $Uri -eq 'https://queue.example/subscriptions'
+ }
+
+ & $scriptUnderTest 'https://queue.example' -LogDirectory $logDirectory
+
+ $captured.Headers.ContainsKey('Authorization') | Should -BeFalse
+ }
+}
diff --git a/ScheduledTasks/SettlementProcessing/Tests/Invoke-DailySettlementProcessing.Tests.ps1 b/ScheduledTasks/SettlementProcessing/Tests/Invoke-DailySettlementProcessing.Tests.ps1
index 813e246..7b0a537 100644
--- a/ScheduledTasks/SettlementProcessing/Tests/Invoke-DailySettlementProcessing.Tests.ps1
+++ b/ScheduledTasks/SettlementProcessing/Tests/Invoke-DailySettlementProcessing.Tests.ps1
@@ -1,10 +1,15 @@
Describe 'Invoke-DailySettlementProcessing.ps1' {
It 'gets merchants and posts a settlement for each merchant returned' {
+ $scriptUnderTest = (Resolve-Path (Join-Path (Join-Path $PSScriptRoot '..') 'Invoke-DailySettlementProcessing.ps1')).Path
$postedUris = [System.Collections.Generic.List[string]]::new()
$settlementDate = (Get-Date).ToString('yyyy-MM-dd')
- $scriptUnderTest = (Resolve-Path (Join-Path (Join-Path $PSScriptRoot '..') 'Invoke-DailySettlementProcessing.ps1')).Path
+ $captured = [pscustomobject]@{
+ TokenCalls = 0
+ MerchantCalls = 0
+ }
Mock Invoke-RestMethod {
+ $captured.TokenCalls++
@{
access_token = 'access-token'
}
@@ -18,6 +23,7 @@ Describe 'Invoke-DailySettlementProcessing.ps1' {
}
Mock Invoke-RestMethod {
+ $captured.MerchantCalls++
@{
merchants = @(
@{ merchantId = 'merchant-1' },
@@ -45,17 +51,61 @@ Describe 'Invoke-DailySettlementProcessing.ps1' {
-ClientId 'client-id' `
-ClientSecret 'client-secret'
- Assert-MockCalled Invoke-RestMethod -Times 1 -ParameterFilter {
- $Method -eq 'Post' -and $Uri -eq 'https://security.example/connect/token'
- }
-
- Assert-MockCalled Invoke-RestMethod -Times 1 -ParameterFilter {
- $Method -eq 'Get' -and $Uri -eq 'https://api.example/api/estates/estate-123/merchants'
- }
-
+ $captured.TokenCalls | Should -Be 1
+ $captured.MerchantCalls | Should -Be 1
$postedUris | Should -Be @(
"https://api.example/api/estates/estate-123/settlements/$settlementDate/merchants/merchant-1",
"https://api.example/api/estates/estate-123/settlements/$settlementDate/merchants/merchant-2"
)
}
+
+ It 'does not post settlements when no merchants are returned' {
+ $scriptUnderTest = (Resolve-Path (Join-Path (Join-Path $PSScriptRoot '..') 'Invoke-DailySettlementProcessing.ps1')).Path
+ $captured = [pscustomobject]@{
+ TokenCalls = 0
+ MerchantCalls = 0
+ SettlementCalls = 0
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.TokenCalls++
+ @{
+ access_token = 'access-token'
+ }
+ } -ParameterFilter {
+ $Method -eq 'Post' -and
+ $Uri -eq 'https://security.example/connect/token' -and
+ $ContentType -eq 'application/x-www-form-urlencoded' -and
+ $Body.grant_type -eq 'client_credentials' -and
+ $Body.client_id -eq 'client-id' -and
+ $Body.client_secret -eq 'client-secret'
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.MerchantCalls++
+ @()
+ } -ParameterFilter {
+ $Method -eq 'Get' -and
+ $Uri -eq 'https://api.example/api/estates/estate-123/merchants' -and
+ $Headers.Authorization -eq 'Bearer access-token'
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.SettlementCalls++
+ } -ParameterFilter {
+ $Method -eq 'Post' -and
+ $Uri -like 'https://api.example/api/estates/estate-123/settlements/*/merchants/*'
+ }
+
+ & $scriptUnderTest `
+ -EstateId 'estate-123' `
+ -BaseUrl 'https://api.example/' `
+ -SecurityServiceUrl 'https://security.example/' `
+ -ClientId 'client-id' `
+ -ClientSecret 'client-secret'
+
+ $captured.TokenCalls | Should -Be 1
+ $captured.MerchantCalls | Should -Be 1
+ $captured.SettlementCalls | Should -Be 0
+ }
}
diff --git a/Scripts/Tests/Scavenge.Tests.ps1 b/ScheduledTasks/Tests/Scavenge.Tests.ps1
similarity index 65%
rename from Scripts/Tests/Scavenge.Tests.ps1
rename to ScheduledTasks/Tests/Scavenge.Tests.ps1
index 47957be..ccedb10 100644
--- a/Scripts/Tests/Scavenge.Tests.ps1
+++ b/ScheduledTasks/Tests/Scavenge.Tests.ps1
@@ -1,6 +1,9 @@
-Describe 'scavenge.ps1' {
+BeforeAll {
+ $script:scriptUnderTest = (Resolve-Path (Join-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'AutoScavenge') 'scavenge.ps1')).Path
+}
+
+Describe 'AutoScavenge\scavenge.ps1' {
It 'posts to /admin/scavenge after trimming a trailing slash from BaseUrl' {
- $scriptUnderTest = (Resolve-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'scavenge.ps1')).Path
$expectedUri = 'http://localhost:2113/admin/scavenge'
$captured = [pscustomobject]@{
Count = 0
@@ -17,7 +20,7 @@ Describe 'scavenge.ps1' {
$Method -eq 'Post'
}
- & $scriptUnderTest -BaseUrl 'http://localhost:2113/'
+ & $script:scriptUnderTest -BaseUrl 'http://localhost:2113/'
$captured.Count | Should -Be 1
$captured.Uri | Should -Be $expectedUri
@@ -27,7 +30,6 @@ Describe 'scavenge.ps1' {
}
It 'adds a Basic Authorization header when username and password are both provided' {
- $scriptUnderTest = (Resolve-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'scavenge.ps1')).Path
$expectedUri = 'http://localhost:2113/admin/scavenge'
$expectedAuth = 'Basic ' + [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes('alice:secret'))
$captured = [pscustomobject]@{
@@ -45,7 +47,7 @@ Describe 'scavenge.ps1' {
$Method -eq 'Post'
}
- & $scriptUnderTest -BaseUrl 'http://localhost:2113' -Username 'alice' -Password 'secret'
+ & $script:scriptUnderTest -BaseUrl 'http://localhost:2113' -Username 'alice' -Password 'secret'
$captured.Count | Should -Be 1
$captured.Uri | Should -Be $expectedUri
@@ -53,7 +55,6 @@ Describe 'scavenge.ps1' {
}
It 'does not add an Authorization header when only one credential is supplied' {
- $scriptUnderTest = (Resolve-Path (Join-Path (Split-Path $PSScriptRoot -Parent) 'scavenge.ps1')).Path
$captured = [pscustomobject]@{
Count = 0
Uri = $null
@@ -69,7 +70,30 @@ Describe 'scavenge.ps1' {
$Method -eq 'Post'
}
- & $scriptUnderTest -BaseUrl 'http://localhost:2113' -Username 'alice'
+ & $script:scriptUnderTest -BaseUrl 'http://localhost:2113' -Username 'alice'
+
+ $captured.Count | Should -Be 1
+ $captured.Uri | Should -Be 'http://localhost:2113/admin/scavenge'
+ $captured.Headers.ContainsKey('Authorization') | Should -BeFalse
+ }
+
+ It 'does not add an Authorization header when only a password is supplied' {
+ $captured = [pscustomobject]@{
+ Count = 0
+ Uri = $null
+ Headers = $null
+ }
+
+ Mock Invoke-RestMethod {
+ $captured.Count++
+ $captured.Uri = $Uri
+ $captured.Headers = $Headers
+ @{ status = 'ok' }
+ } -ParameterFilter {
+ $Method -eq 'Post'
+ }
+
+ & $script:scriptUnderTest -BaseUrl 'http://localhost:2113' -Password 'secret'
$captured.Count | Should -Be 1
$captured.Uri | Should -Be 'http://localhost:2113/admin/scavenge'
diff --git a/TransactionProcessor.SystemSetupTool/EstateSetupFunctions.cs b/TransactionProcessor.SystemSetupTool/EstateSetupFunctions.cs
index 945ddd1..7fcdd37 100644
--- a/TransactionProcessor.SystemSetupTool/EstateSetupFunctions.cs
+++ b/TransactionProcessor.SystemSetupTool/EstateSetupFunctions.cs
@@ -53,9 +53,9 @@ public async Task SetupEstate(CancellationToken cancellationToken) {
return ResultHelpers.CreateFailure(createEstateResult);
this.EstateId = createEstateResult.Data;
- var createUserResult = await this.CreateEstateUser(cancellationToken);
- if (createUserResult.IsFailed)
- return ResultHelpers.CreateFailure(createUserResult);
+ //var createUserResult = await this.CreateEstateUser(cancellationToken);
+ //if (createUserResult.IsFailed)
+ // return ResultHelpers.CreateFailure(createUserResult);
var createOperatorsResult = await this.CreateOperators(cancellationToken);
if (createOperatorsResult.IsFailed)
@@ -336,6 +336,7 @@ public async Task CreateMerchant(Merchant merchant,
CreateMerchantRequest createMerchantRequest = new CreateMerchantRequest {
Address = new Address {
AddressLine1 = merchant.Address.AddressLine1, Country = merchant.Address.Country, Region = merchant.Address.Region, Town = merchant.Address.Town,
+ PostalCode = merchant.Address.Postcode
},
Name = merchant.Name,
Contact = new Contact { ContactName = merchant.Contact.ContactName, EmailAddress = merchant.Contact.EmailAddress },
diff --git a/TransactionProcessor.SystemSetupTool/EventStoreFunctions.cs b/TransactionProcessor.SystemSetupTool/EventStoreFunctions.cs
index 71693e6..5ee0b5d 100644
--- a/TransactionProcessor.SystemSetupTool/EventStoreFunctions.cs
+++ b/TransactionProcessor.SystemSetupTool/EventStoreFunctions.cs
@@ -45,6 +45,7 @@ private async Task SetupSubscriptions(CancellationToken cancellationToke
("$ce-FileAggregate", "Transaction Processor", 0),
("$ce-FileImportLogAggregate", "Transaction Processor", 0),
("$ce-OperatorAggregate", "Transaction Processor", 0),
+ ("$ce-MerchantBalanceArchive", "Transaction Processor", 0),
("$ce-TransactionAggregate", "Transaction Processor - Domain", 0),
("$ce-SettlementAggregate", "Transaction Processor - Domain", 0),
@@ -100,7 +101,6 @@ private async Task DeployProjections(CancellationToken cancellationToken
projection.Contains("TransactionProcessorSubscriptionStreamBuilder") ||
projection.Contains("EstateAggregator") ||
projection.Contains("MerchantAggregator") ||
- projection.Contains("MerchantBalanceAggregator") ||
projection.Contains("CallbackHandlerEnricher"))
{
continue;
diff --git a/TransactionProcessor.SystemSetupTool/Program.cs b/TransactionProcessor.SystemSetupTool/Program.cs
index 5ebd498..69aa60e 100644
--- a/TransactionProcessor.SystemSetupTool/Program.cs
+++ b/TransactionProcessor.SystemSetupTool/Program.cs
@@ -1,9 +1,13 @@
using System;
+using System.Data;
+using FileProcessor.Client;
+using FileProcessor.DataTransferObjects.Requests;
using KurrentDB.Client;
using SecurityService.DataTransferObjects;
using Shared.Results;
using Shared.Serialisation;
using SimpleResults;
+using TransactionProcessor.SystemSetupTool.fileprofileconfig;
namespace TransactionProcessor.SystemSetupTool
{
@@ -21,6 +25,7 @@ namespace TransactionProcessor.SystemSetupTool
class Program
{
private static TransactionProcessorClient TransactionProcessorClient;
+ private static FileProcessorClient FileProcessorClient;
private static SecurityServiceClient SecurityServiceClient;
@@ -43,6 +48,7 @@ static async Task Main(string[] args) {
Func securityResolver = s => { return ConfigurationReader.GetValue("SecurityServiceUri"); };
Func transactionProcessorResolver = s => { return ConfigurationReader.GetValue("TransactionProcessorApi"); };
+ Func fileProcessorResolver = s => { return ConfigurationReader.GetValue("FileProcessorApi"); };
HttpClientHandler handler = new() {
ServerCertificateCustomValidationCallback = (message,
cert,
@@ -56,14 +62,17 @@ static async Task Main(string[] args) {
Program.SecurityServiceClient = new SecurityServiceClient(securityResolver, client, Serialise, Deserialise);
Program.TransactionProcessorClient = new TransactionProcessorClient(transactionProcessorResolver, client, Serialise, Deserialise);
+ Program.FileProcessorClient = new FileProcessorClient(fileProcessorResolver, client, Serialise, Deserialise);
+
KurrentDBClientSettings settings = KurrentDBClientSettings.Create(ConfigurationReader.GetValue("EventStoreAddress"));
Program.ProjectionClient = new (settings);
Program.PersistentSubscriptionsClient = new (settings);
- Mode setupMode = Mode.EstateSetup;
+ Mode setupMode = Mode.FileProcessorSetup;
- String configFileName = "setupconfig.json";
+ String configFileName = "setupconfig.staging.json";
+ FileProcessingOptions fileProcessingOptions = await Program.GetFileProfileConfig(cancellationToken);
IdentityServerConfiguration identityServerConfiguration = await Program.GetIdentityServerConfig(cancellationToken);
IdentityServerFunctions identityServerFunctions = new(Program.SecurityServiceClient, identityServerConfiguration);
EventStoreFunctions eventStoreFunctions = new(Program.ProjectionClient, Program.PersistentSubscriptionsClient);
@@ -72,6 +81,7 @@ static async Task Main(string[] args) {
Mode.SecuritySetup => await identityServerFunctions.CreateConfig(cancellationToken),
Mode.EventStoreSetup => await eventStoreFunctions.SetupEventStore(cancellationToken),
Mode.EstateSetup => await SetupEstates(configFileName,cancellationToken),
+ Mode.FileProcessorSetup => await SetupFileProcessors(fileProcessingOptions, cancellationToken),
_ => Result.Invalid($"Invalid mode {setupMode}")
};
@@ -83,6 +93,29 @@ static async Task Main(string[] args) {
}
}
+ private static async Task SetupFileProcessors(FileProcessingOptions fileProcessingOptions,
+ CancellationToken cancellationToken) {
+ Result tokenResult = await SecurityServiceClient.GetToken("serviceClient", "d192cbc46d834d0da90e8a9d50ded543", CancellationToken.None);
+ if (tokenResult.IsFailed)
+ return ResultHelpers.CreateFailure(tokenResult);
+ foreach (FileProfile fileProfile in fileProcessingOptions.FileProfiles) {
+ CreateFileProfileRequest createFileProfileRequest = new()
+ {
+ FileProfileId = fileProfile.Id,
+ Name = fileProfile.Name,
+ ListeningDirectory = fileProfile.ListeningDirectory,
+ RequestType = fileProfile.RequestType,
+ OperatorName = fileProfile.OperatorName,
+ LineTerminator = Enum.Parse(fileProfile.LineTerminator),
+ FileFormatHandler = fileProfile.FileFormatHandler
+ };
+ Result result = await Program.FileProcessorClient.CreateFileProfile(tokenResult.Data.AccessToken, createFileProfileRequest, cancellationToken);
+ if (result.IsFailed)
+ return ResultHelpers.CreateFailure(result);
+ }
+ return Result.Success();
+ }
+
static String Serialise(Object arg)
{
return StringSerialiser.Serialise