diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..ec9a3f3
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,42 @@
+# .NET 9 SDK (use 8.0 if your solution targets 8)
+# Force amd64 so IBM i Access RPM can be installed even on Apple Silicon hosts.
+FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/sdk:9.0
+
+# Java runtime is required by Sonar
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ openjdk-17-jre-headless curl ca-certificates git unzip gnupg \
+ unixodbc unixodbc-dev alien \
+ && rm -rf /var/lib/apt/lists/*
+
+# Node.js 20.x is required by SonarLint for VS Code
+RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
+ && apt-get update \
+ && apt-get install -y --no-install-recommends nodejs \
+ && rm -rf /var/lib/apt/lists/*
+
+# Install IBM i Access ODBC driver when the RPM is present in the repo
+COPY drivers/ /tmp/drivers/
+RUN set -ex; \
+ if ls /tmp/drivers/ibm-iaccess-*.rpm.disabled >/dev/null 2>&1; then \
+ for f in /tmp/drivers/ibm-iaccess-*.rpm.disabled; do mv "$f" "${f%.disabled}"; done; \
+ fi
+RUN set -ex; \
+ if ls /tmp/drivers/ibm-iaccess-*.rpm >/dev/null 2>&1; then \
+ for f in /tmp/drivers/ibm-iaccess-*.rpm; do alien -i --scripts "$f"; done; \
+ else \
+ echo ">> IBM i Access ODBC RPM not found in /tmp/drivers. ODBC connections will fail until it is added."; \
+ fi
+
+# Register ODBC driver/DSN so unixODBC knows about the IBM driver
+COPY docker/odbc/odbcinst.ini /etc/odbcinst.ini
+COPY docker/odbc/odbc.ini /etc/odbc.ini
+
+# Global tools: dotnet-sonarscanner (+ coverlet for coverage)
+RUN dotnet tool install --global dotnet-sonarscanner \
+ && dotnet tool install --global coverlet.console
+ENV PATH="${PATH}:/root/.dotnet/tools"
+
+# Non-root user (Dev Containers convention)
+RUN useradd -ms /bin/bash vscode
+USER vscode
+WORKDIR /workspaces
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..008a3b5
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,52 @@
+{
+ "name": "AS400API Dev",
+ "build": {
+ "dockerfile": "./Dockerfile",
+ "context": ".."
+ },
+
+
+ // "remoteUser": "vscode",
+ "remoteEnv": {
+ "SONAR_HOST_URL": "http://host.docker.internal:9000"
+ },
+
+
+ "runArgs": ["--init"],
+
+ // Ensure IBM i Access shared libraries are on the loader path when running in devcontainer
+ "containerEnv": {
+ "LD_LIBRARY_PATH": "/opt/ibm/iaccess/lib64:/opt/ibm/iaccess/lib",
+ "AS400_DRIVER_NAME": "IBM i Access ODBC Driver",
+ "SONAR_TOKEN": "squ_ef2f0a2f495a32c33ed81afb16f3cdc98bf1336a"
+ },
+
+ // เมานท์โฟลเดอร์งานเข้า /workspaces/AS400API
+ "workspaceFolder": "/workspaces/AS400API",
+ "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/AS400API,type=bind,consistency=cached",
+
+ "forwardPorts": [8080],
+ "postCreateCommand": "apt-get update && apt-get install -y jq && dotnet --info && dotnet restore",
+
+ "settings": {
+ "terminal.integrated.defaultProfile.linux": "bash",
+ "dotnet.defaultSolution": "AS400API.sln",
+ "dotnet.projects.enableFileBasedPrograms": false
+ },
+
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ // "ms-dotnettools.csharp",
+ "ms-azuretools.vscode-docker",
+ "oderwat.indent-rainbow",
+ "streetsidesoftware.code-spell-checker",
+ "openai.chatgpt",
+ "ms-dotnettools.csdevkit",
+ "SonarSource.sonarlint-vscode"
+ ]
+ }
+ },
+
+ "remoteUser": "root"
+}
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..a301b6c
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,7 @@
+**/bin/
+**/obj/
+**/.vs/
+**/.vscode/
+**/.idea/
+**/.DS_Store
+*.zip
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b6e9060
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+bin
+Logs
+obj
diff --git a/.sonarqube/conf/0/FilesToAnalyze.txt b/.sonarqube/conf/0/FilesToAnalyze.txt
new file mode 100644
index 0000000..84c89a1
--- /dev/null
+++ b/.sonarqube/conf/0/FilesToAnalyze.txt
@@ -0,0 +1,35 @@
+/workspaces/AS400API/Auth/AuthPolicies.cs
+/workspaces/AS400API/Auth/DemoUser.cs
+/workspaces/AS400API/Auth/DemoUserStore.cs
+/workspaces/AS400API/Auth/LoginRequest.cs
+/workspaces/AS400API/Auth/LoginResponse.cs
+/workspaces/AS400API/Auth/PasswordHasher.cs
+/workspaces/AS400API/Auth/Roles.cs
+/workspaces/AS400API/Auth/TokenService.cs
+/workspaces/AS400API/Configuration/JwtOptions.cs
+/workspaces/AS400API/Configuration/OdbcOptions.cs
+/workspaces/AS400API/Endpoints/As400Endpoints.cs
+/workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs
+/workspaces/AS400API/Endpoints/AuthEndpoints.cs
+/workspaces/AS400API/Endpoints/SystemEndpoints.cs
+/workspaces/AS400API/Infrastructure/DatabaseRowFormatter.cs
+/workspaces/AS400API/Infrastructure/DataReaderExtensions.cs
+/workspaces/AS400API/Program.cs
+/workspaces/AS400API/obj/Debug/net9.0/AS400API.GlobalUsings.g.cs
+/workspaces/AS400API/obj/Debug/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs
+/workspaces/AS400API/obj/Debug/net9.0/AS400API.AssemblyInfo.cs
+/workspaces/AS400API/obj/Debug/net9.0/AS400API.MvcApplicationPartsAssemblyInfo.cs
+/workspaces/AS400API/appsettings.json
+/workspaces/AS400API/.dockerignore
+/workspaces/AS400API/docker-compose.yml
+/workspaces/AS400API/docker/odbc/odbc.ini
+/workspaces/AS400API/docker/odbc/odbcinst.ini
+/workspaces/AS400API/Dockerfile
+/workspaces/AS400API/drivers/ibm-iaccess-1.1.0.28-1.0.x86_64.rpm
+/workspaces/AS400API/Logs/as400-api-20251001.log
+/workspaces/AS400API/Logs/as400-api-20251002.log
+/workspaces/AS400API/Logs/as400-api-20251003.log
+/workspaces/AS400API/Logs/as400-api-20251004.log
+/workspaces/AS400API/README.md
+/workspaces/AS400API/scripts/run-sonar.sh
+/workspaces/AS400API/scripts/test-databases.sh
diff --git a/.sonarqube/conf/0/ProjectOutFolderPath.txt b/.sonarqube/conf/0/ProjectOutFolderPath.txt
new file mode 100644
index 0000000..219412b
--- /dev/null
+++ b/.sonarqube/conf/0/ProjectOutFolderPath.txt
@@ -0,0 +1 @@
+/workspaces/AS400API/.sonarqube/out/0
diff --git a/.sonarqube/conf/0/SonarProjectConfig.xml b/.sonarqube/conf/0/SonarProjectConfig.xml
new file mode 100644
index 0000000..9e3f838
--- /dev/null
+++ b/.sonarqube/conf/0/SonarProjectConfig.xml
@@ -0,0 +1,9 @@
+
+
+ /workspaces/AS400API/.sonarqube/conf/SonarQubeAnalysisConfig.xml
+ /workspaces/AS400API/AS400API.csproj
+ /workspaces/AS400API/.sonarqube/conf/0/FilesToAnalyze.txt
+ /workspaces/AS400API/.sonarqube/out/0
+ Product
+ net9.0
+
\ No newline at end of file
diff --git a/.sonarqube/conf/Sonar-cs-none.ruleset b/.sonarqube/conf/Sonar-cs-none.ruleset
new file mode 100644
index 0000000..c9e8431
--- /dev/null
+++ b/.sonarqube/conf/Sonar-cs-none.ruleset
@@ -0,0 +1,410 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.sonarqube/conf/Sonar-cs.ruleset b/.sonarqube/conf/Sonar-cs.ruleset
new file mode 100644
index 0000000..92ad81f
--- /dev/null
+++ b/.sonarqube/conf/Sonar-cs.ruleset
@@ -0,0 +1,410 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.sonarqube/conf/Sonar-vbnet-none.ruleset b/.sonarqube/conf/Sonar-vbnet-none.ruleset
new file mode 100644
index 0000000..2d27b76
--- /dev/null
+++ b/.sonarqube/conf/Sonar-vbnet-none.ruleset
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.sonarqube/conf/Sonar-vbnet.ruleset b/.sonarqube/conf/Sonar-vbnet.ruleset
new file mode 100644
index 0000000..bf6aca3
--- /dev/null
+++ b/.sonarqube/conf/Sonar-vbnet.ruleset
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.sonarqube/conf/SonarQubeAnalysisConfig.xml b/.sonarqube/conf/SonarQubeAnalysisConfig.xml
new file mode 100644
index 0000000..9bbceae
--- /dev/null
+++ b/.sonarqube/conf/SonarQubeAnalysisConfig.xml
@@ -0,0 +1,172 @@
+
+
+ /workspaces/AS400API/.sonarqube/conf
+ /workspaces/AS400API/.sonarqube/out
+ /workspaces/AS400API/.sonarqube/bin
+ /workspaces/AS400API
+ false
+ true
+ true
+ false
+ http://host.docker.internal:9000
+ 9.9.8.100196
+ as400api
+ AS400API
+
+
+
+
+
+
+
+
+
+ 8.51.0.59060
+ true
+ .ts,.tsx,.cts,.mts
+ SonarQube
+ false
+ false
+ **/vendor/**
+ .tf
+ true
+ 60
+ SonarAnalyzer.CSharp
+ false
+ false
+ .css,.less,.scss
+ .html,.xhtml,.cshtml,.vbhtml,.aspx,.ascx,.rhtml,.erb,.shtm,.shtml,.cmp,.twig
+ false
+ false
+ true
+ false
+ .scala
+ true
+ SonarAnalyzer.VisualBasic
+ AWSTemplateFormatVersion
+ true
+ 30
+ 52
+ https://secure.gravatar.com/avatar/{EMAIL_MD5}.jpg?s={SIZE}&d=identicon
+ 600
+ .jsp,.jspf,.jspx
+ 1000
+ amd,applescript,atomtest,browser,commonjs,couch,embertest,flow,greasemonkey,jasmine,jest,jquery,meteor,mocha,mongo,nashorn,node,phantomjs,prototypejs,protractor,qunit,rhino,serviceworker,shared-node-browser,shelljs,webextensions,worker,wsh,yui
+ false
+ **/vendor/**
+ false
+ .vb
+ 30
+ true
+ py
+ .cs
+ true
+ SonarAnalyzer-8.51.0.59060.zip
+ .java,.jav
+ .kt
+ php,php3,php4,php5,phtml,inc
+ .xml,.xsd,.xsl
+ 260
+ 3600000
+ true
+ false
+ coverage-reports/*coverage-*.xml
+ 8.51.0.59060
+ .go
+ sonarqube
+ 104
+ true
+ **/vendor/**
+ false
+ as
+ 20
+ false
+ https://api.github.com/
+ .rb
+ true
+ xunit-reports/xunit-result-*.xml
+ angular,goog,google,OenLayers,d3,dojo,dojox,dijit,Backbone,moment,casper,_,sap
+ 24
+ .yaml,.yml
+ false
+ true
+ https://github.com/
+ SonarAnalyzer.VisualBasic
+ noreply@nowhere
+ 8.51.0.59060
+ 4
+ SonarAnalyzer.VisualBasic
+ [SONARQUBE]
+ false
+ csharp
+ true
+ .json
+ SAML
+ coverage/.resultset.json
+ SonarAnalyzer-8.51.0.59060.zip
+ true
+ main
+ SonarAnalyzer.CSharp
+ false
+ SonarAnalyzer.CSharp
+ 0.05,0.1,0.2,0.5
+ false
+ true
+ 8.51.0.59060
+ false
+ .js,.jsx,.cjs,.mjs,.vue
+ false
+ NOT_ACCEPTED
+ https://gitlab.com
+ vbnet
+ https://update.sonarsource.org/update-center.properties
+ 147B411E-AZmtNwrV3lb8QGbhu0SU
+ 10/4/2025 3:17:58 AM
+
+
+ http://host.docker.internal:9000
+ **/coverage.opencover.xml
+ **/bin/**,**/obj/**,**/coverage.opencover.xml
+
+
+
+
+ cs
+ /workspaces/AS400API/.sonarqube/conf/Sonar-cs.ruleset
+ /workspaces/AS400API/.sonarqube/conf/Sonar-cs-none.ruleset
+
+
+
+ /tmp/.sonarqube/resources/0/SonarAnalyzer.CFG.dll
+ /tmp/.sonarqube/resources/0/Google.Protobuf.dll
+ /tmp/.sonarqube/resources/0/SonarAnalyzer.CSharp.dll
+ /tmp/.sonarqube/resources/0/SonarAnalyzer.dll
+ /tmp/.sonarqube/resources/0/THIRD-PARTY-NOTICES.txt
+
+
+
+
+ /workspaces/AS400API/.sonarqube/conf/cs/SonarLint.xml
+
+
+
+ vbnet
+ /workspaces/AS400API/.sonarqube/conf/Sonar-vbnet.ruleset
+ /workspaces/AS400API/.sonarqube/conf/Sonar-vbnet-none.ruleset
+
+
+
+ /tmp/.sonarqube/resources/1/SonarAnalyzer.CFG.dll
+ /tmp/.sonarqube/resources/1/SonarAnalyzer.VisualBasic.dll
+ /tmp/.sonarqube/resources/1/Google.Protobuf.dll
+ /tmp/.sonarqube/resources/1/SonarAnalyzer.dll
+ /tmp/.sonarqube/resources/1/THIRD-PARTY-NOTICES.txt
+
+
+
+
+ /workspaces/AS400API/.sonarqube/conf/vbnet/SonarLint.xml
+
+
+
+
\ No newline at end of file
diff --git a/.sonarqube/conf/cs/SonarLint.xml b/.sonarqube/conf/cs/SonarLint.xml
new file mode 100644
index 0000000..9783ca7
--- /dev/null
+++ b/.sonarqube/conf/cs/SonarLint.xml
@@ -0,0 +1,869 @@
+
+
+
+
+ sonar.cs.opencover.reportsPaths
+ **/coverage.opencover.xml
+
+
+ sonar.cs.ignoreHeaderComments
+ true
+
+
+ sonar.cs.analyzeGeneratedCode
+ false
+
+
+ sonar.cs.file.suffixes
+ .cs
+
+
+ sonar.cs.roslyn.ignoreIssues
+ false
+
+
+
+
+ S2225
+
+
+ S2346
+
+
+ S2223
+
+
+ S2344
+
+
+ S2589
+
+
+ S3433
+
+
+ S1134
+
+
+ S1135
+
+
+ S2345
+
+
+ S4524
+
+
+ S2342
+
+
+ format
+ ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
+
+
+ flagsAttributeFormat
+ ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?s$
+
+
+
+
+ S2222
+
+
+ S2583
+
+
+ S2115
+
+
+ S3447
+
+
+ S2234
+
+
+ S2479
+
+
+ S3444
+
+
+ S3445
+
+
+ S1144
+
+
+ S1264
+
+
+ S2114
+
+
+ S3442
+
+
+ S3440
+
+
+ S3443
+
+
+ S3329
+
+
+ S3449
+
+
+ S3655
+
+
+ S3776
+
+
+ propertyThreshold
+ 3
+
+
+ threshold
+ 15
+
+
+
+
+ S2688
+
+
+ S3897
+
+
+ S1110
+
+
+ S2201
+
+
+ S4502
+
+
+ S1118
+
+
+ S2328
+
+
+ S2681
+
+
+ S1117
+
+
+ S2326
+
+
+ S3415
+
+
+ S4507
+
+
+ S1116
+
+
+ S1479
+
+
+ maximum
+ 30
+
+
+
+
+ S2699
+
+
+ S1125
+
+
+ S2696
+
+
+ S4635
+
+
+ S1121
+
+
+ S1123
+
+
+ S2219
+
+
+ S2692
+
+
+ S1006
+
+
+ S1481
+
+
+ S3427
+
+
+ S3237
+
+
+ S3358
+
+
+ S3598
+
+
+ S3236
+
+
+ S5773
+
+
+ S2386
+
+
+ S3597
+
+
+ S4200
+
+
+ S4201
+
+
+ S1172
+
+
+ S4457
+
+
+ S5659
+
+
+ S3249
+
+
+ S4456
+
+
+ S3005
+
+
+ S3246
+
+
+ S3247
+
+
+ S4211
+
+
+ S5547
+
+
+ S3244
+
+
+ S5542
+
+
+ S1066
+
+
+ S1186
+
+
+ S4210
+
+
+ S1185
+
+
+ S2275
+
+
+ S3241
+
+
+ S2368
+
+
+ S3457
+
+
+ S4423
+
+
+ S1155
+
+
+ S2245
+
+
+ S3456
+
+
+ S3458
+
+
+ S4426
+
+
+ S2123
+
+
+ S2365
+
+
+ S2486
+
+
+ S3453
+
+
+ S3330
+
+
+ S3451
+
+
+ S5753
+
+
+ S3217
+
+
+ S3459
+
+
+ S4428
+
+
+ S3218
+
+
+ S927
+
+
+ S2259
+
+
+ S3450
+
+
+ S5766
+
+
+ S1048
+
+
+ S1168
+
+
+ S2257
+
+
+ S3466
+
+
+ S2376
+
+
+ S3343
+
+
+ S3346
+
+
+ S3464
+
+
+ S2252
+
+
+ S3220
+
+
+ S4433
+
+
+ S1163
+
+
+ S4790
+
+
+ S818
+
+
+ S2251
+
+
+ S2372
+
+
+ S4792
+
+
+ S2743
+
+
+ S1656
+
+
+ S907
+
+
+ S2995
+
+
+ S3600
+
+
+ S3963
+
+
+ S2996
+
+
+ S2757
+
+
+ S2755
+
+
+ S2997
+
+
+ S3604
+
+
+ S1751
+
+
+ S3603
+
+
+ S3966
+
+
+ S1643
+
+
+ S1871
+
+
+ S1764
+
+
+ S2737
+
+
+ S2971
+
+
+ S2612
+
+
+ S2857
+
+
+ S3875
+
+
+ S1210
+
+
+ S3871
+
+
+ S1450
+
+
+ S2306
+
+
+ S3877
+
+
+ S1104
+
+
+ S1215
+
+
+ S1699
+
+
+ S3998
+
+
+ S3400
+
+
+ S3884
+
+
+ S3887
+
+
+ S2551
+
+
+ S3885
+
+
+ S2436
+
+
+ maxMethod
+ 3
+
+
+ max
+ 2
+
+
+
+
+ S3881
+
+
+ S2437
+
+
+ S3889
+
+
+ S1313
+
+
+ S3610
+
+
+ S3972
+
+
+ S3973
+
+
+ S2761
+
+
+ S3971
+
+
+ S3984
+
+
+ S4830
+
+
+ S3981
+
+
+ S1206
+
+
+ S3626
+
+
+ S3869
+
+
+ S1940
+
+
+ S1944
+
+
+ S1905
+
+
+ S1939
+
+
+ S5034
+
+
+ S4061
+
+
+ S5042
+
+
+ S1854
+
+
+ S4070
+
+
+ S1862
+
+
+ S3927
+
+
+ S3928
+
+
+ S3925
+
+
+ S3926
+
+
+ S2953
+
+
+ S3923
+
+
+ S125
+
+
+ S1607
+
+
+ S1848
+
+
+ S2930
+
+
+ S3903
+
+
+ S3904
+
+
+ S2933
+
+
+ S2934
+
+
+ S6422
+
+
+ S110
+
+
+ max
+ 5
+
+
+
+
+ S2068
+
+
+ credentialWords
+ password, passwd, pwd, passphrase
+
+
+
+
+ S5332
+
+
+ S112
+
+
+ S2187
+
+
+ S4487
+
+
+ S6424
+
+
+ S3397
+
+
+ S2184
+
+
+ S5693
+
+
+ fileUploadSizeLimit
+ 8000000
+
+
+
+
+ S6420
+
+
+ S107
+
+
+ max
+ 7
+
+
+
+
+ S2183
+
+
+ S108
+
+
+ S4019
+
+
+ S3169
+
+
+ S3168
+
+
+ S4015
+
+
+ S4136
+
+
+ S2077
+
+
+ S2190
+
+
+ S1199
+
+
+ S3376
+
+
+ S1075
+
+
+ S3011
+
+
+ S3256
+
+
+ S4581
+
+
+ S4586
+
+
+ S3010
+
+
+ S3251
+
+
+ S4220
+
+
+ S4583
+
+
+ S5443
+
+
+ S2178
+
+
+ S3264
+
+
+ S3267
+
+
+ S101
+
+
+ S5445
+
+
+ S3262
+
+
+ S3265
+
+
+ S6419
+
+
+ S2053
+
+
+ S2292
+
+
+ S3263
+
+
+ S3260
+
+
+ S3261
+
+
+ S2290
+
+
+ S2291
+
+
+ S6444
+
+
+ S4144
+
+
+ S4143
+
+
+ S3172
+
+
+ S4159
+
+
+ S4260
+
+
+ S4036
+
+
+ S4277
+
+
+ S4035
+
+
+ S4158
+
+
+ S4275
+
+
+ S2092
+
+
+ S3060
+
+
+ S5122
+
+
+
+
+
diff --git a/.sonarqube/conf/vbnet/SonarLint.xml b/.sonarqube/conf/vbnet/SonarLint.xml
new file mode 100644
index 0000000..0521585
--- /dev/null
+++ b/.sonarqube/conf/vbnet/SonarLint.xml
@@ -0,0 +1,477 @@
+
+
+
+
+ sonar.vbnet.ignoreHeaderComments
+ true
+
+
+ sonar.vbnet.file.suffixes
+ .vb
+
+
+ sonar.vbnet.roslyn.ignoreIssues
+ false
+
+
+ sonar.vbnet.analyzeGeneratedCode
+ false
+
+
+
+
+ S1654
+
+
+ format
+ ^[a-z][a-z0-9]*([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
+
+
+
+
+ S2225
+
+
+ S1135
+
+
+ S2344
+
+
+ S2346
+
+
+ S2347
+
+
+ format
+ ^(([a-z][a-z0-9]*)?([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?_)?([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
+
+
+
+
+ S1134
+
+
+ S2342
+
+
+ flagsAttributeFormat
+ ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?s$
+
+
+ format
+ ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
+
+
+
+
+ S2345
+
+
+ S2068
+
+
+ credentialWords
+ password, passwd, pwd, passphrase
+
+
+
+
+ S2222
+
+
+ S6146
+
+
+ S112
+
+
+ S2340
+
+
+ S1656
+
+
+ S2349
+
+
+ S907
+
+
+ S6145
+
+
+ S5693
+
+
+ fileUploadSizeLimit
+ 8000000
+
+
+
+
+ S107
+
+
+ max
+ 7
+
+
+
+
+ S108
+
+
+ S1940
+
+
+ S2358
+
+
+ S1542
+
+
+ format
+ ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
+
+
+
+
+ S2234
+
+
+ S2355
+
+
+ S4136
+
+
+ S2077
+
+
+ S2352
+
+
+ S2359
+
+
+ S2757
+
+
+ S3449
+
+
+ S114
+
+
+ format
+ ^I([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
+
+
+
+
+ S117
+
+
+ format
+ ^[a-z][a-z0-9]*([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
+
+
+
+
+ S3603
+
+
+ S3776
+
+
+ propertyThreshold
+ 3
+
+
+ threshold
+ 15
+
+
+
+
+ S1110
+
+
+ S1751
+
+
+ S1871
+
+
+ S1075
+
+
+ S1197
+
+
+ S3011
+
+
+ S4586
+
+
+ S1479
+
+
+ maximum
+ 30
+
+
+
+
+ S4507
+
+
+ S4581
+
+
+ S1643
+
+
+ S4583
+
+
+ S1123
+
+
+ S1125
+
+
+ S1764
+
+
+ S101
+
+
+ format
+ ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?$
+
+
+
+
+ S2178
+
+
+ S5443
+
+
+ S5445
+
+
+ S2692
+
+
+ S2737
+
+
+ S3385
+
+
+ S1481
+
+
+ S1645
+
+
+ S2612
+
+
+ S3358
+
+
+ S5042
+
+
+ S2387
+
+
+ S3598
+
+
+ S3871
+
+
+ S4201
+
+
+ S1172
+
+
+ S2304
+
+
+ format
+ ^([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?(\.([A-Z]{1,3}[a-z0-9]+)*([A-Z]{2})?)*$
+
+
+
+
+ S3998
+
+
+ S5659
+
+
+ S1862
+
+
+ S2951
+
+
+ S5944
+
+
+ S3884
+
+
+ S5547
+
+
+ S1066
+
+
+ S2551
+
+
+ S4210
+
+
+ S5542
+
+
+ S1186
+
+
+ S3923
+
+
+ S3926
+
+
+ S3927
+
+
+ S2437
+
+
+ S3889
+
+
+ S1313
+
+
+ S2368
+
+
+ S4423
+
+
+ S1155
+
+
+ S6444
+
+
+ S2761
+
+
+ S3453
+
+
+ S2365
+
+
+ S4144
+
+
+ S5753
+
+
+ S4428
+
+
+ S927
+
+
+ S4143
+
+
+ S4260
+
+
+ S1048
+
+
+ S2259
+
+
+ S4159
+
+
+ S4830
+
+
+ S2257
+
+
+ S2375
+
+
+ minimumSeriesLength
+ 6
+
+
+
+
+ S3466
+
+
+ S4036
+
+
+ S3464
+
+
+ S3981
+
+
+ S4277
+
+
+ S2376
+
+
+ S1163
+
+
+ S3903
+
+
+ S3904
+
+
+ S2372
+
+
+ S3869
+
+
+ S4790
+
+
+ S4275
+
+
+ S4792
+
+
+
+
+
diff --git a/.sonarqube/out/.sonar/.sonar_lock b/.sonarqube/out/.sonar/.sonar_lock
new file mode 100644
index 0000000..e69de29
diff --git a/.sonarqube/out/.sonar/report-task.txt b/.sonarqube/out/.sonar/report-task.txt
new file mode 100644
index 0000000..5a5c515
--- /dev/null
+++ b/.sonarqube/out/.sonar/report-task.txt
@@ -0,0 +1,6 @@
+projectKey=as400api
+serverUrl=http://host.docker.internal:9000
+serverVersion=9.9.8.100196
+dashboardUrl=http://host.docker.internal:9000/dashboard?id=as400api
+ceTaskId=AZmtj01mSkXasGRcHif1
+ceTaskUrl=http://host.docker.internal:9000/api/ce/task?id=AZmtj01mSkXasGRcHif1
diff --git a/.sonarqube/out/0/Issues.json b/.sonarqube/out/0/Issues.json
new file mode 100644
index 0000000..c134e37
--- /dev/null
+++ b/.sonarqube/out/0/Issues.json
@@ -0,0 +1,625 @@
+{
+ "$schema": "http://json.schemastore.org/sarif-1.0.0",
+ "version": "1.0.0",
+ "runs": [
+ {
+ "tool": {
+ "name": "Microsoft (R) Visual C# Compiler",
+ "version": "4.14.0.0",
+ "fileVersion": "4.14.0-3.25413.5 (b828a8df)",
+ "semanticVersion": "4.14.0",
+ "language": "en-US"
+ },
+ "results": [
+ {
+ "ruleId": "S125",
+ "level": "warning",
+ "message": "Remove this commented out code.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 147,
+ "startColumn": 17,
+ "endLine": 147,
+ "endColumn": 97
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1
+ }
+ },
+ {
+ "ruleId": "S125",
+ "level": "warning",
+ "message": "Remove this commented out code.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Program.cs",
+ "region": {
+ "startLine": 11,
+ "startColumn": 1,
+ "endLine": 11,
+ "endColumn": 53
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1
+ }
+ },
+ {
+ "ruleId": "S2068",
+ "level": "warning",
+ "message": "\"password\" detected here, make sure this is not a hard-coded credential.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/appsettings.json",
+ "region": {
+ "startLine": 6,
+ "startColumn": 18,
+ "endLine": 6,
+ "endColumn": 24
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1
+ }
+ },
+ {
+ "ruleId": "S1144",
+ "level": "warning",
+ "message": "Remove the unused private field 'LibraryNamePattern'.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 152,
+ "startColumn": 5,
+ "endLine": 152,
+ "endColumn": 125
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1
+ }
+ },
+ {
+ "ruleId": "S101",
+ "level": "warning",
+ "message": "Rename class 'ORDUAGEndpoint' to match pascal case naming rules, consider using 'OrduagEndpoint'.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 15,
+ "startColumn": 21,
+ "endLine": 15,
+ "endColumn": 35
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1
+ }
+ },
+ {
+ "ruleId": "S6444",
+ "level": "warning",
+ "message": "Pass a timeout to limit the execution time.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 152,
+ "startColumn": 56,
+ "endLine": 152,
+ "endColumn": 124
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1
+ }
+ },
+ {
+ "ruleId": "SYSLIB1045",
+ "level": "note",
+ "message": "Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 152,
+ "startColumn": 56,
+ "endLine": 152,
+ "endColumn": 124
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1
+ }
+ },
+ {
+ "ruleId": "S3776",
+ "level": "warning",
+ "message": "Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 16,
+ "startColumn": 37,
+ "endLine": 16,
+ "endColumn": 54
+ }
+ }
+ }
+ ],
+ "relatedLocations": [
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 24,
+ "startColumn": 17,
+ "endLine": 24,
+ "endColumn": 19
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 36,
+ "startColumn": 13,
+ "endLine": 36,
+ "endColumn": 18
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 59,
+ "startColumn": 13,
+ "endLine": 59,
+ "endColumn": 18
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 74,
+ "startColumn": 17,
+ "endLine": 74,
+ "endColumn": 19
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 92,
+ "startColumn": 13,
+ "endLine": 92,
+ "endColumn": 18
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 107,
+ "startColumn": 17,
+ "endLine": 107,
+ "endColumn": 19
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 107,
+ "startColumn": 60,
+ "endLine": 107,
+ "endColumn": 62
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 135,
+ "startColumn": 21,
+ "endLine": 135,
+ "endColumn": 26
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 138,
+ "startColumn": 25,
+ "endLine": 138,
+ "endColumn": 28
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/As400Endpoints.cs",
+ "region": {
+ "startLine": 154,
+ "startColumn": 13,
+ "endLine": 154,
+ "endColumn": 18
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1,
+ "customProperties": {
+ "0": "+2 (incl 1 for nesting)",
+ "1": "+2 (incl 1 for nesting)",
+ "2": "+2 (incl 1 for nesting)",
+ "3": "+2 (incl 1 for nesting)",
+ "4": "+2 (incl 1 for nesting)",
+ "5": "+2 (incl 1 for nesting)",
+ "6": "+1",
+ "7": "+2 (incl 1 for nesting)",
+ "8": "+3 (incl 2 for nesting)",
+ "9": "+2 (incl 1 for nesting)"
+ }
+ }
+ },
+ {
+ "ruleId": "S3776",
+ "level": "warning",
+ "message": "Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed.",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 154,
+ "startColumn": 37,
+ "endLine": 154,
+ "endColumn": 55
+ }
+ }
+ }
+ ],
+ "relatedLocations": [
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 167,
+ "startColumn": 21,
+ "endLine": 167,
+ "endColumn": 23
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 177,
+ "startColumn": 21,
+ "endLine": 177,
+ "endColumn": 23
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 192,
+ "startColumn": 21,
+ "endLine": 192,
+ "endColumn": 23
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 198,
+ "startColumn": 21,
+ "endLine": 198,
+ "endColumn": 23
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 204,
+ "startColumn": 21,
+ "endLine": 204,
+ "endColumn": 23
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 219,
+ "startColumn": 21,
+ "endLine": 219,
+ "endColumn": 28
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 229,
+ "startColumn": 25,
+ "endLine": 229,
+ "endColumn": 30
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 232,
+ "startColumn": 29,
+ "endLine": 232,
+ "endColumn": 32
+ }
+ }
+ },
+ {
+ "physicalLocation": {
+ "uri": "file:///workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",
+ "region": {
+ "startLine": 254,
+ "startColumn": 17,
+ "endLine": 254,
+ "endColumn": 22
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1,
+ "customProperties": {
+ "0": "+2 (incl 1 for nesting)",
+ "1": "+2 (incl 1 for nesting)",
+ "2": "+2 (incl 1 for nesting)",
+ "3": "+2 (incl 1 for nesting)",
+ "4": "+2 (incl 1 for nesting)",
+ "5": "+2 (incl 1 for nesting)",
+ "6": "+2 (incl 1 for nesting)",
+ "7": "+3 (incl 2 for nesting)",
+ "8": "+2 (incl 1 for nesting)"
+ }
+ }
+ },
+ {
+ "ruleId": "ASP0025",
+ "level": "note",
+ "message": "Use AddAuthorizationBuilder to register authorization services and construct policies",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Program.cs",
+ "region": {
+ "startLine": 100,
+ "startColumn": 1,
+ "endLine": 106,
+ "endColumn": 3
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1
+ }
+ },
+ {
+ "ruleId": "CA1861",
+ "level": "note",
+ "message": "Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array",
+ "locations": [
+ {
+ "resultFile": {
+ "uri": "file:///workspaces/AS400API/Infrastructure/DatabaseRowFormatter.cs",
+ "region": {
+ "startLine": 48,
+ "startColumn": 20,
+ "endLine": 48,
+ "endColumn": 43
+ }
+ }
+ }
+ ],
+ "properties": {
+ "warningLevel": 1,
+ "customProperties": {
+ "paramName": "separator"
+ }
+ }
+ }
+ ],
+ "rules": {
+ "ASP0025": {
+ "id": "ASP0025",
+ "shortDescription": "Use AddAuthorizationBuilder",
+ "defaultLevel": "note",
+ "helpUri": "https://aka.ms/aspnet/analyzers",
+ "properties": {
+ "category": "Usage",
+ "isEnabledByDefault": true
+ }
+ },
+ "CA1861": {
+ "id": "CA1861",
+ "shortDescription": "Avoid constant arrays as arguments",
+ "fullDescription": "Constant arrays passed as arguments are not reused when called repeatedly, which implies a new array is created each time. Consider extracting them to 'static readonly' fields to improve performance if the passed array is not mutated within the called method.",
+ "defaultLevel": "note",
+ "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1861",
+ "properties": {
+ "category": "Performance",
+ "isEnabledByDefault": true,
+ "tags": [
+ "Telemetry",
+ "EnabledRuleInAggressiveMode"
+ ]
+ }
+ },
+ "S101": {
+ "id": "S101",
+ "shortDescription": "Types should be named in PascalCase",
+ "fullDescription": "Shared naming conventions allow teams to collaborate efficiently. This rule checks whether or not type names are using PascalCase. To reduce noise, two consecutive upper case characters are allowed unless they form the whole type name. So, MyXClass is compliant, but XC on its own is not.",
+ "defaultLevel": "warning",
+ "helpUri": "https://rules.sonarsource.com/csharp/RSPEC-101",
+ "properties": {
+ "category": "Minor Code Smell",
+ "isEnabledByDefault": true,
+ "tags": [
+ "C#",
+ "MainSourceScope",
+ "TestSourceScope",
+ "SonarWay"
+ ]
+ }
+ },
+ "S1144": {
+ "id": "S1144",
+ "shortDescription": "Unused private types or members should be removed",
+ "fullDescription": "private or internal types or private members that are never executed or referenced are dead code: unnecessary, inoperative code that should be removed. Cleaning out dead code decreases the size of the maintained codebase, making it easier to understand the program and preventing bugs from being introduced.",
+ "defaultLevel": "note",
+ "helpUri": "https://rules.sonarsource.com/csharp/RSPEC-1144",
+ "properties": {
+ "category": "Major Code Smell",
+ "isEnabledByDefault": true,
+ "tags": [
+ "C#",
+ "MainSourceScope",
+ "TestSourceScope",
+ "SonarWay",
+ "Unnecessary"
+ ]
+ }
+ },
+ "S125": {
+ "id": "S125",
+ "shortDescription": "Sections of code should not be commented out",
+ "fullDescription": "Programmers should not comment out code as it bloats programs and reduces readability.",
+ "defaultLevel": "warning",
+ "helpUri": "https://rules.sonarsource.com/csharp/RSPEC-125",
+ "properties": {
+ "category": "Major Code Smell",
+ "isEnabledByDefault": true,
+ "tags": [
+ "C#",
+ "MainSourceScope",
+ "TestSourceScope",
+ "SonarWay"
+ ]
+ }
+ },
+ "S2068": {
+ "id": "S2068",
+ "shortDescription": "Hard-coded credentials are security-sensitive",
+ "fullDescription": "Because it is easy to extract strings from an application source code or binary, credentials should not be hard-coded. This is particularly true for applications that are distributed or that are open-source.",
+ "defaultLevel": "warning",
+ "helpUri": "https://rules.sonarsource.com/csharp/RSPEC-2068",
+ "properties": {
+ "category": "Blocker Security Hotspot",
+ "isEnabledByDefault": true,
+ "tags": [
+ "C#",
+ "MainSourceScope",
+ "SonarWay"
+ ]
+ }
+ },
+ "S3776": {
+ "id": "S3776",
+ "shortDescription": "Cognitive Complexity of methods should not be too high",
+ "fullDescription": "Cognitive Complexity is a measure of how hard the control flow of a method is to understand. Methods with high Cognitive Complexity will be difficult to maintain.",
+ "defaultLevel": "warning",
+ "helpUri": "https://rules.sonarsource.com/csharp/RSPEC-3776",
+ "properties": {
+ "category": "Critical Code Smell",
+ "isEnabledByDefault": false,
+ "tags": [
+ "C#",
+ "MainSourceScope",
+ "TestSourceScope",
+ "SonarWay"
+ ]
+ }
+ },
+ "S6444": {
+ "id": "S6444",
+ "shortDescription": "Not specifying a timeout for regular expressions is security-sensitive",
+ "fullDescription": "Not specifying a timeout for regular expressions can lead to a Denial-of-Service attack. Pass a timeout when using System.Text.RegularExpressions to process untrusted input because a malicious user might craft a value for which the evaluation lasts excessively long.",
+ "defaultLevel": "warning",
+ "helpUri": "https://rules.sonarsource.com/csharp/RSPEC-6444",
+ "properties": {
+ "category": "Major Security Hotspot",
+ "isEnabledByDefault": true,
+ "tags": [
+ "C#",
+ "MainSourceScope",
+ "SonarWay"
+ ]
+ }
+ },
+ "SYSLIB1045": {
+ "id": "SYSLIB1045",
+ "shortDescription": "Convert to 'GeneratedRegexAttribute'.",
+ "defaultLevel": "note",
+ "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/syslib-diagnostics/syslib1045",
+ "properties": {
+ "category": "Performance",
+ "isEnabledByDefault": true
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.sonarqube/out/0/ProjectInfo.xml b/.sonarqube/out/0/ProjectInfo.xml
new file mode 100644
index 0000000..debfcca
--- /dev/null
+++ b/.sonarqube/out/0/ProjectInfo.xml
@@ -0,0 +1,20 @@
+
+
+ AS400API
+ C#
+ Product
+ 40cb5b53-77b8-b1fd-458c-805350cb09e8
+ /workspaces/AS400API/AS400API.csproj
+ false
+
+
+
+
+ /workspaces/AS400API/.sonarqube/out/0/Issues.json
+ /workspaces/AS400API/.sonarqube/out/0
+ /workspaces/AS400API/.sonarqube/out/0/Telemetry.json
+
+ Debug
+ AnyCPU
+ net9.0
+
\ No newline at end of file
diff --git a/.sonarqube/out/0/Telemetry.json b/.sonarqube/out/0/Telemetry.json
new file mode 100644
index 0000000..5d55dc1
--- /dev/null
+++ b/.sonarqube/out/0/Telemetry.json
@@ -0,0 +1 @@
+{"dotnetenterprise.s4net.build.target_framework_moniker":".NETCoreApp,Version=v9.0"}
diff --git a/.sonarqube/out/0/output-cs/file-metadata.pb b/.sonarqube/out/0/output-cs/file-metadata.pb
new file mode 100644
index 0000000..5d56cf3
--- /dev/null
+++ b/.sonarqube/out/0/output-cs/file-metadata.pb
@@ -0,0 +1,22 @@
+2
+)/workspaces/AS400API/Auth/AuthPolicies.csutf-8.
+%/workspaces/AS400API/Auth/DemoUser.csutf-83
+*/workspaces/AS400API/Auth/DemoUserStore.csutf-82
+)/workspaces/AS400API/Auth/LoginRequest.csutf-83
+*/workspaces/AS400API/Auth/LoginResponse.csutf-84
++/workspaces/AS400API/Auth/PasswordHasher.csutf-8+
+"/workspaces/AS400API/Auth/Roles.csutf-82
+)/workspaces/AS400API/Auth/TokenService.csutf-89
+0/workspaces/AS400API/Configuration/JwtOptions.csutf-8:
+1/workspaces/AS400API/Configuration/OdbcOptions.csutf-89
+0/workspaces/AS400API/Endpoints/As400Endpoints.csutf-8?
+6/workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.csutf-88
+//workspaces/AS400API/Endpoints/AuthEndpoints.csutf-8:
+1/workspaces/AS400API/Endpoints/SystemEndpoints.csutf-8D
+;/workspaces/AS400API/Infrastructure/DatabaseRowFormatter.csutf-8D
+;/workspaces/AS400API/Infrastructure/DataReaderExtensions.csutf-8(
+/workspaces/AS400API/Program.csutf-8K
+@/workspaces/AS400API/obj/Debug/net9.0/AS400API.GlobalUsings.g.csutf-8_
+T/workspaces/AS400API/obj/Debug/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.csutf-8I
+>/workspaces/AS400API/obj/Debug/net9.0/AS400API.AssemblyInfo.csutf-8\
+Q/workspaces/AS400API/obj/Debug/net9.0/AS400API.MvcApplicationPartsAssemblyInfo.csutf-8
\ No newline at end of file
diff --git a/.sonarqube/out/0/output-cs/log.pb b/.sonarqube/out/0/output-cs/log.pb
new file mode 100644
index 0000000..6159c8d
--- /dev/null
+++ b/.sonarqube/out/0/output-cs/log.pb
@@ -0,0 +1 @@
+Roslyn version: 4.14.0.0Language version: CSharp13!Concurrent execution: enabledgcFile '/workspaces/AS400API/obj/Debug/net9.0/AS400API.GlobalUsings.g.cs' was recognized as generated{wFile '/workspaces/AS400API/obj/Debug/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs' was recognized as generatedeaFile '/workspaces/AS400API/obj/Debug/net9.0/AS400API.AssemblyInfo.cs' was recognized as generatedxtFile '/workspaces/AS400API/obj/Debug/net9.0/AS400API.MvcApplicationPartsAssemblyInfo.cs' was recognized as generated
\ No newline at end of file
diff --git a/.sonarqube/out/0/output-cs/metrics.pb b/.sonarqube/out/0/output-cs/metrics.pb
new file mode 100644
index 0000000..5135524
--- /dev/null
+++ b/.sonarqube/out/0/output-cs/metrics.pb
@@ -0,0 +1,30 @@
+5
+)/workspaces/AS400API/Auth/AuthPolicies.csrH
+%/workspaces/AS400API/Auth/DemoUser.cs 8r
+
+]
+*/workspaces/AS400API/Auth/DemoUserStore.cs 8r
!"
1
+)/workspaces/AS400API/Auth/LoginRequest.csr3
+*/workspaces/AS400API/Auth/LoginResponse.csrX
++/workspaces/AS400API/Auth/PasswordHasher.cs 8r
+
.
+"/workspaces/AS400API/Auth/Roles.csrn
+)/workspaces/AS400API/Auth/TokenService.cs 8r(
"#$%'()*+,./0x"$()+.[
+0/workspaces/AS400API/Configuration/JwtOptions.cs 8r
+
xp
+1/workspaces/AS400API/Configuration/OdbcOptions.cs 8j
r
+ !"#$%&x !"#$
+0/workspaces/AS400API/Endpoints/As400Endpoints.csD 8 jr
+ !"#$%&'()*+,-/0123456789:;<=>?@ABDEFGHIJKLMNPQRSTUWXYZ[\]^_`abcefghijklmnoqrstuvwyz|}~xN !"&'*/146789=>ADFIJLMWXYZ^_begjkmnyz|}~
+6/workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs5 8jr
+
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~xhz
+//workspaces/AS400API/Endpoints/AuthEndpoints.cs
8 r*
!"#%&'()*+,./0x!"#%'().u
+1/workspaces/AS400API/Endpoints/SystemEndpoints.cs 8r&
+
!"#$%&')*+x
!%)
+;/workspaces/AS400API/Infrastructure/DatabaseRowFormatter.cs 8rK
+
!"#%&()*+,-/01245679:;<=>?ABDEFGHIJKLNOPQRSUVWx!"%*,/01469<>AEFIKNOQUo
+;/workspaces/AS400API/Infrastructure/DataReaderExtensions.cs 8r
+
x
+
+
+/workspaces/AS400API/Program.csQ8j*rj
!#$%&'(+,-/012345689:;<=>?@ABCDEGHIJKLMNOPQSTUVWXYZ[\]^_`abdefghijlnopqrtvwy{|}~x;
!#$%'+,-/02<=BGHJMNOPQSTUWZ\`dfghilnpqtvwy{|}~
\ No newline at end of file
diff --git a/.sonarqube/out/0/output-cs/symrefs.pb b/.sonarqube/out/0/output-cs/symrefs.pb
new file mode 100644
index 0000000..8f59c53
--- /dev/null
+++ b/.sonarqube/out/0/output-cs/symrefs.pb
@@ -0,0 +1,448 @@
+O
+)/workspaces/AS400API/Auth/AuthPolicies.cs
+
+
+
+ '
+
+ $
+%/workspaces/AS400API/Auth/DemoUser.cs
+
+
+ #
+, 8
+
+ #
+A M #
+k p
+
+
+
+
+
+' ,
+*/workspaces/AS400API/Auth/DemoUserStore.cs
+
+ !
+ 2 8
+
+ /
+7 ? #
+- 1$ (
+
+ #
+- 1( ,; ?
+: B &
+ & " %
+. 6 $
+? G7 ?
+e j 2 7
+
& *
+ , 0O
+)/workspaces/AS400API/Auth/LoginRequest.cs
+
+ !
+
+) 1
+
+: Bh
+*/workspaces/AS400API/Auth/LoginResponse.cs
+
+ "
+
+* 5
+
+; D
+
+M V
+
+t y
++/workspaces/AS400API/Auth/PasswordHasher.cs
+
+ "
+ 2 :
+
+
+ f mo v
+ Z dc m
+
+
- 9
+
A I- 5
+ 7 ;J N
+ ' 0
+
+
+% -1 9
+6 @3 =
+I S1 ;
+ ; D
+ D Q
+ 7 BH
+"/workspaces/AS400API/Auth/Roles.cs
+
+
+
+
+
+
+)/workspaces/AS400API/Auth/TokenService.cs
+
+ <
+
( < D(( )) ++0 8
+- :..
+# *
+
+ (
+' +- 1! %"" !
+ ,, 2
+ $$ **
+'' ..( -
+0/workspaces/AS400API/Configuration/JwtOptions.cs
+
+
+
+ #
+
+
+
+
+
+
+ & )I L
+
) &
+
+
+1/workspaces/AS400API/Configuration/OdbcOptions.cs
+
+
+
+ #' 7X h
+
+
+ ' +? C
+ ' /C K
+ ' - D J
+ !!' 0!!J S
+ ""' 3""Q ]
+ ##
+
+ $
+ "Z
+ : ?. 32 7 0 5!!3 8""6 ;## $$ %
+
+0/workspaces/AS400API/Endpoints/As400Endpoints.cs
+
+ "
+
+$ 5>
+M R
//
DD
ee
+ (
+6 9. 1Y \2 5
+J N " &
+_ l &2
+ !! &&
+ $
+ !!O X""" +
+//B F44 66% )
+//W d11 &
+11 88 ==
+55 %665 =
+66 77 '
+77 88H Q99" +2
+DD7 BJJ. 9WWD OXXk v^^Z e
+DDS WII WW$ (
+DDh uFF &(
+FF LL XX ^^
+PP %WW4 <
+WW XX] cYY &
+YY ZZ" +6
+ee@ Kkk. 9}}) 4
+f q
+T _8
+eeT ]kkX a
+' 0
+s |
+a j
+een rjj yy* .
+
+ee gg &,
+gg mm
+
+
+qq %zz& .L
+yy 'zz ||' .~~
+% ,
+
+0 7
+|| $}} ~~' 7$
+
+ "
+
+' 50
+
+
+
+W ^
+ '<
+
+! '
+! '
+, 2
+ &
+5 ;$
+
+
+
+$ '<
+
+! "
+( )
+? @
+/ 0
+O P
+
+
+" +
+
+/ 3
+7 ;
+6/workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs
+
+ "
+% 0
+= H
+
+" 4
+
+$ 6$
+
+N S
+
+
+
++ 0
+ $
+X ]
+# (
+3 8
+, 1
+3 8
+, 1
+3 8
+, 1
+
+
+ $
+
+ #
+
+. 2
+
+ ,
+ *0
+
+
+
+
+ <
+
+
+
+
+, 0
+ H
+
+
+
+* 2
+8 @
+( 0
+ $
+
+ "
+ "
+& 0
+
+
+( .
+
+ "
+
+
+
+
+ "
+ "
+ "
+
+
+* 4
+
+P V
+a gT
+
+ '
+ '
+ '
+ '
+ #
+ #
+* 9<
+
+$ +
+
+( /
+
+4 ;$
+
+ %
+ !
+/ 80
+
+
+
+
+$ (<
+
+% +
+% +
+0 6
+, 2
+$ *$
+
+ #
+ #
+% (<
+
+% &
+, -
+C D
+F G
+3 4
+
+$ )
+9 >
+
+ !
+& /
+
+0 4
+8 <
+
+ #
+ *0
+
+ !
+9 C
+2 <
+ (0
+
+ "
+9 D
+2 =
+ )0
+
+
+9 B
+2 ;
+ '$
+
+
+% )
+^ b
+
+
+) 1
+//workspaces/AS400API/Endpoints/AuthEndpoints.cs
+
+ !
+
+
$ 4(
+
L Q
%%
.. <
+J Q = Dl s7 >E L
+a j &! *
+ y *
+
+ = G2
+ ? C7 ;s w
+ 0 ;(
+%%6 :'' ''2 6((
+'' ))$ ,
+(( )). 3
+((= >((B C
+1/workspaces/AS400API/Endpoints/SystemEndpoints.cs
+
+ #
+
+( 8
+T W
+
+$ 6
+N S
))
+9 = $
+ ) ,
+ "
+ S T
+;/workspaces/AS400API/Infrastructure/DatabaseRowFormatter.cs
+
+ (
+
+
+
+< S
+
+
+n r
+3 7 !
+
+/ D
+f l "
+ "" %%
+!! ""
+(( %!!E P2
+((- 2**! &,, // 66 F
+// 44 99 ;; "EE FF $HH "
+11 11 2
+;; << >> AA) 0AA7 >2
+DD EE
+NN QQ UU (
+FF FF FF- .HH# $2
+HH II NN, 3OO QQ !
+;/workspaces/AS400API/Infrastructure/DataReaderExtensions.cs
+
+ (
+
+ ,(
+? E
+
+ %(
+K R
+
+ #$ +6 =
+
+ ' "(
+ & 2
+/workspaces/AS400API/Program.cs
+
+ 6v
+
++ // 00 MM NN OO PP QQ SS dd ll
+
+ '
+
+" *
+, ? n
+ . 2 !! ## "$$ "%% "MM "NNC G:
+++ ,,
+--A KOO (ZZ $\\ &
+-- ^^ )(
+00 &22 GG HH (
+88 GG" 0GG? MJJ
+
+
+NN+ ,
+UU WW
+dd" )ff hh
+ff4 :gg
+hh1 7ii \
+ll nn pp qq tt vv ww yy {{
+
*
+{{ || }} ~~
\ No newline at end of file
diff --git a/.sonarqube/out/0/output-cs/token-cpd.pb b/.sonarqube/out/0/output-cs/token-cpd.pb
new file mode 100644
index 0000000..c17bae0
--- /dev/null
+++ b/.sonarqube/out/0/output-cs/token-cpd.pb
@@ -0,0 +1,5928 @@
+
+)/workspaces/AS400API/Auth/AuthPolicies.cs
+ namespace
+AS400API
+
+.
+Auth
+;
+public
+static
+class
+AuthPolicies
+{
+public
+
+const
+string
+RequireOperator '
+=( )
+$str* ;
+;; <
+public
+
+const
+string
+RequireAdmin $
+=% &
+$str' 5
+;5 6
+}
+%/workspaces/AS400API/Auth/DemoUser.cs
+ namespace
+AS400API
+
+.
+Auth
+;
+public
+sealed
+class
+DemoUser
+{
+public
+
+DemoUser
+(
+string
+username #
+,# $
+string% +
+passwordHash, 8
+,8 9
+string: @
+passwordSaltA M
+,M N
+IReadOnlyCollectionO b
+<b c
+stringc i
+>i j
+rolesk p
+)p q
+{
+Username
+=
+username
+;
+PasswordHash
+
+
+=
+
+
+passwordHash
+
+ #
+;
+
+# $
+PasswordSalt
+=
+passwordSalt #
+;# $
+Roles
+=
+roles
+;
+}
+public
+
+string
+Username
+{
+get
+; !
+}" #
+public
+
+string
+PasswordHash
+{
+get! $
+;$ %
+}& '
+public
+
+string
+PasswordSalt
+{
+get! $
+;$ %
+}& '
+public
+
+IReadOnlyCollection
+<
+string %
+>% &
+Roles' ,
+{- .
+get/ 2
+;2 3
+}4 5
+}
+*/workspaces/AS400API/Auth/DemoUserStore.cs
+ namespace
+AS400API
+
+.
+Auth
+;
+public
+sealed
+class
+
DemoUserStore !
+{
+private
+readonly
+
+Dictionary
+<
+string &
+, & '
+DemoUser ( 0
+> 0 1
+_users 2 8
+; 8 9
+public
+
+
DemoUserStore
+(
+)
+{
+_users
+=
+new
+
+Dictionary
+<
+string
&
+,
& '
+DemoUser
( 0
+>
0 1
+(
1 2
+StringComparer
2 @
+.
@ A
+OrdinalIgnoreCase
A R
+)
R S
+{
+[
+$str
+]
+=
+
+CreateUser "
+(" #
+$str# *
+,* +
+$str, 6
+,6 7
+new8 ;
+[; <
+]< =
+{> ?
+Roles@ E
+.E F
+AdminF K
+,K L
+RolesM R
+.R S
+OperatorS [
+}\ ]
+)] ^
+,^ _
+[
+$str
+]
+=
+
+CreateUser %
+(% &
+$str& 0
+,0 1
+$str2 <
+,< =
+new> A
+[A B
+]B C
+{D E
+RolesF K
+.K L
+OperatorL T
+}U V
+)V W
+}
+;
+
+}
+public
+
+ ValueTask
+<
+DemoUser
+?
+>
+FindByNameAsync /
+(/ 0
+string0 6
+username7 ?
+)? @
+{
+_users
+.
+TryGetValue
+(
+username #
+,# $
+out% (
+var) ,
+user- 1
+)1 2
+;2 3
+return
+ ValueTask
+.
+
+FromResult #
+(# $
+user$ (
+)( )
+;) *
+}
+public
+
+bool
+ValidateCredentials #
+(# $
+DemoUser$ ,
+user- 1
+,1 2
+string3 9
+password: B
+)B C
+=>D F
+PasswordHasher
+.
+Verify
+(
+password &
+,& '
+user( ,
+., -
+PasswordHash- 9
+,9 :
+user; ?
+.? @
+PasswordSalt@ L
+)L M
+;M N
+private
+static
+DemoUser
+
+CreateUser &
+(& '
+string' -
+username. 6
+,6 7
+string8 >
+password? G
+,G H
+IReadOnlyCollectionI \
+<\ ]
+string] c
+>c d
+rolese j
+)j k
+{
+var
+(
+hash
+,
+salt
+)
+=
+PasswordHasher )
+.) *
+HashPassword* 6
+(6 7
+password7 ?
+)? @
+;@ A
+return
+new
+DemoUser
+(
+username $
+, $ %
+hash & *
+, * +
+salt , 0
+, 0 1
+roles 2 7
+) 7 8
+; 8 9
+}!!
+}""
+)/workspaces/AS400API/Auth/LoginRequest.cs
+ namespace
+AS400API
+
+.
+Auth
+;
+public
+sealed
+record
+LoginRequest !
+(! "
+string" (
+Username) 1
+,1 2
+string3 9
+Password: B
+)B C
+;C D
+*/workspaces/AS400API/Auth/LoginResponse.cs
+ namespace
+AS400API
+
+.
+Auth
+;
+public
+sealed
+record
+
LoginResponse "
+(" #
+string# )
+AccessToken* 5
+,5 6
+int7 :
+ ExpiresIn; D
+,D E
+stringF L
+ TokenTypeM V
+,V W
+IReadOnlyCollectionX k
+<k l
+stringl r
+>r s
+Rolest y
+)y z
+;z {
++/workspaces/AS400API/Auth/PasswordHasher.cs
+ namespace
+AS400API
+
+.
+Auth
+;
+public
+static
+class
+PasswordHasher "
+{
+private
+const
+int
+SaltSize
+=
+$num ! #
+; # $
+private
+
+
+const
+
+
+int
+
+
+KeySize
+
+
+=
+
+
+$num
+
+ "
+;
+
+" #
+private
+const
+int
+
+Iterations
+=! "
+$num# *
+;* +
+public
+
+static
+(
+string
+Hash
+,
+string
&
+Salt
' +
+)
+ ,
+HashPassword
- 9
+(
9 :
+string
: @
+password
A I
+)
I J
+{
+var
+salt
+= !
+RandomNumberGenerator (
+.( )
+GetBytes) 1
+(1 2
+SaltSize2 :
+): ;
+;; <
+var
+ hashBytes
+=
+
KeyDerivation %
+.% &
+Pbkdf2& ,
+(, -
+password- 5
+,5 6
+salt7 ;
+,; <
+KeyDerivationPrf= M
+.M N
+
+HMACSHA256N X
+,X Y
+
+IterationsZ d
+,d e
+KeySizef m
+)m n
+;n o
+return
+(
+Convert
+.
+ToBase64String &
+(& '
+ hashBytes' 0
+)0 1
+,1 2
+Convert3 :
+.: ;
+ToBase64String; I
+(I J
+saltJ N
+)N O
+)O P
+;P Q
+}
+public
+
+static
+bool
+Verify
+(
+string $
+password% -
+,- .
+string/ 5
+
+storedHash6 @
+,@ A
+stringB H
+
+storedSaltI S
+)S T
+{
+var
+ saltBytes
+=
+Convert
+.
+FromBase64String 0
+(0 1
+
+storedSalt1 ;
+); <
+;< =
+var
+
computedBytes
+=
+
KeyDerivation )
+.) *
+Pbkdf2* 0
+(0 1
+password1 9
+,9 :
+ saltBytes; D
+,D E
+KeyDerivationPrfF V
+.V W
+
+HMACSHA256W a
+,a b
+
+Iterationsc m
+,m n
+KeySizeo v
+)v w
+;w x
+var
+storedBytes
+=
+Convert !
+.! "
+FromBase64String" 2
+(2 3
+
+storedHash3 =
+)= >
+;> ?
+return #
+CryptographicOperations &
+.& '
+FixedTimeEquals' 6
+(6 7
+storedBytes7 B
+,B C
+
computedBytesD Q
+)Q R
+;R S
+}
+}
+"/workspaces/AS400API/Auth/Roles.cs
+ namespace
+AS400API
+
+.
+Auth
+;
+public
+static
+class
+Roles
+{
+public
+
+const
+string
+Admin
+=
+$str '
+;' (
+public
+
+const
+string
+Operator
+=! "
+$str# -
+;- .
+}
+)/workspaces/AS400API/Auth/TokenService.cs
+ namespace
+AS400API
+
+.
+Auth
+;
+public
+sealed
+class
+TokenService
+{
+private
+readonly
+
+JwtOptions
+_options
(
+;
( )
+private
+readonly #
+JwtSecurityTokenHandler ,
+
_tokenHandler- :
+=; <
+new= @
+(@ A
+)A B
+;B C
+public
+
+TokenService
+(
+
+JwtOptions "
+options# *
+)* +
+{
+_options
+=
+options
+;
+}
+public
+
+string
+CreateToken
+(
+DemoUser &
+user' +
+)+ ,
+{
+var
+signingCredentials
+=
+new! $
+SigningCredentials% 7
+(7 8
+new
+SymmetricSecurityKey $
+($ %
+Encoding% -
+.- .
+UTF8. 2
+.2 3
+GetBytes3 ;
+(; <
+_options< D
+.D E
+KeyE H
+)H I
+)I J
+,J K
+SecurityAlgorithms
+.
+
+HmacSha256 )
+)) *
+;* +
+var
+claims
+=
+new
+List
+<
+Claim #
+># $
+{
+new
+( #
+JwtRegisteredClaimNames '
+.' (
+Sub( +
+,+ ,
+user- 1
+.1 2
+Username2 :
+): ;
+,; <
+new
+( #
+JwtRegisteredClaimNames '
+.' (
+Jti( +
+,+ ,
+Guid- 1
+.1 2
+NewGuid2 9
+(9 :
+): ;
+.; <
+ToString< D
+(D E
+)E F
+)F G
+,G H
+new
+(
+
+ClaimTypes
+.
+Name
+,
+user! %
+.% &
+Username& .
+). /
+}
+;
+
+foreach""
+(""
+var""
+role""
+in""
+user"" !
+.""! "
+Roles""" '
+)""' (
+{##
+claims$$
+.$$
+Add$$
+($$
+new$$
+Claim$$
+($$ !
+
+ClaimTypes$$! +
+.$$+ ,
+Role$$, 0
+,$$0 1
+role$$2 6
+)$$6 7
+)$$7 8
+;$$8 9
+}%%
+var''
+token''
+=''
+new''
+JwtSecurityToken'' (
+(''( )
+issuer((
+:((
+_options((
+.((
+Issuer(( #
+,((# $
+audience))
+:))
+_options))
+.))
+Audience)) '
+,))' (
+claims**
+:**
+claims**
+,**
+expires++
+:++
+DateTime++
+.++
+UtcNow++ $
+.++$ %
+
+AddMinutes++% /
+(++/ 0
+_options++0 8
+.++8 9&
+AccessTokenLifetimeMinutes++9 S
+)++S T
+,++T U
+signingCredentials,,
+:,,
+signingCredentials,, 2
+),,2 3
+;,,3 4
+return..
+
_tokenHandler..
+...
+
+WriteToken.. '
+(..' (
+token..( -
+)..- .
+;... /
+}//
+}00
+0/workspaces/AS400API/Configuration/JwtOptions.cs
+ namespace
+AS400API
+
+.
+
Configuration
+; !
+public
+sealed
+class
+
+JwtOptions
+{
+public
+
+const
+string
+SectionName #
+=$ %
+$str& +
+;+ ,
+public
+
+
+
+string
+
+
+Issuer
+
+
+{
+
+
+get
+
+
+;
+
+
+set
+
+ #
+;
+
+# $
+}
+
+% &
+=
+
+' (
+$str
+
+) 3
+;
+
+3 4
+public
+
+string
+Audience
+{
+get
+; !
+set" %
+;% &
+}' (
+=) *
+$str+ =
+;= >
+public
+
+string
+Key
+{
+get
+;
+set
+; !
+}" #
+=$ %
+$str& I
+;I J
+public
+
+int
&
+AccessTokenLifetimeMinutes
)
+{
* +
+get
, /
+;
/ 0
+set
1 4
+;
4 5
+}
6 7
+=
8 9
+$num
: <
+;
< =
+public
+
+void
+
EnsureIsValid
+(
+)
+{
+if
+
+(
+string
+.
+IsNullOrWhiteSpace %
+(% &
+Key& )
+)) *
+||+ -
+Encoding. 6
+.6 7
+UTF87 ;
+.; <
+GetByteCount< H
+(H I
+KeyI L
+)L M
+<N O
+$numP R
+)R S
+{
+throw
+new %
+InvalidOperationException /
+(/ 0
+$str0
+)
+;
+
+}
+if
+
+( &
+AccessTokenLifetimeMinutes &
+<=' )
+$num* +
+)+ ,
+{
+throw
+new %
+InvalidOperationException /
+(/ 0
+$str0 k
+)k l
+;l m
+}
+}
+} (
+1/workspaces/AS400API/Configuration/OdbcOptions.cs
+ namespace
+AS400API
+
+.
+
Configuration
+; !
+public
+sealed
+class
+OdbcOptions
+{
+public
+
+string
+?
+System
+{
+get
+;
+set! $
+;$ %
+}& '
+public
+
+string
+?
+DefaultLibraries #
+{ $ %
+get & )
+; ) *
+set + .
+; . /
+} 0 1
+public
+
+
+
+string
+
+
+?
+
+
+User
+
+
+{
+
+
+get
+
+
+;
+
+
+set
+
+ "
+;
+
+" #
+}
+
+$ %
+public
+
+string
+?
+Password
+{
+get !
+;! "
+set# &
+;& '
+}( )
+public
+
+string
+?
+Naming
+{
+get
+;
+set! $
+;$ %
+}& '
+public
+
+string
+?
+ Translate
+{
+get "
+;" #
+set$ '
+;' (
+}) *
+=+ ,
+$str- 0
+;0 1
+public
+
+string
+?
+ClientLocale
+{ !
+get" %
+;% &
+set' *
+;* +
+}, -
+=. /
+$str0 7
+;7 8
+public
+
+bool
+Pooling
+{
+get
+;
+set "
+;" #
+}$ %
+=& '
+true( ,
+;, -
+public
+
+string
+ToConnectionString $
+($ %
+)% &
+{
+var
+
+driverName
+=
+Environment $
+.$ %"
+GetEnvironmentVariable% ;
+(; <
+$str< O
+)O P
+??Q S
+$strT n
+;n o
+var
+parts
+=
+new
+List
+<
+string #
+># $
+{
+$"
+$str
+{
+
+driverName "
+}" #
+$str# %
+"% &
+,& '
+$"
+$str
+{
+System
+}
+"
+}
+;
+
+if
+
+(
+!
+string
+.
+IsNullOrWhiteSpace &
+(& '
+DefaultLibraries' 7
+)7 8
+)8 9
+parts: ?
+.? @
+Add@ C
+(C D
+$"D F
+$strF W
+{W X
+DefaultLibrariesX h
+}h i
+"i j
+)j k
+;k l
+if
+
+(
+!
+string
+.
+IsNullOrWhiteSpace &
+(& '
+User' +
+)+ ,
+), -
+parts. 3
+.3 4
+Add4 7
+(7 8
+$"8 :
+$str: >
+{> ?
+User? C
+}C D
+"D E
+)E F
+;F G
+if
+
+(
+!
+string
+.
+IsNullOrWhiteSpace &
+(& '
+Password' /
+)/ 0
+)0 1
+parts2 7
+.7 8
+Add8 ;
+(; <
+$"< >
+$str> B
+{B C
+PasswordC K
+}K L
+"L M
+)M N
+;N O
+if
+
+(
+!
+string
+.
+IsNullOrWhiteSpace &
+( & '
+Naming ' -
+) - .
+) . /
+parts 0 5
+. 5 6
+Add 6 9
+( 9 :
+$" : <
+$str < C
+{ C D
+Naming D J
+} J K
+" K L
+) L M
+; M N
+if!!
+
+(!!
+!!!
+string!!
+.!!
+IsNullOrWhiteSpace!! &
+(!!& '
+ Translate!!' 0
+)!!0 1
+)!!1 2
+parts!!3 8
+.!!8 9
+Add!!9 <
+(!!< =
+$"!!= ?
+$str!!? I
+{!!I J
+ Translate!!J S
+}!!S T
+"!!T U
+)!!U V
+;!!V W
+if""
+
+(""
+!""
+string""
+.""
+IsNullOrWhiteSpace"" &
+(""& '
+ClientLocale""' 3
+)""3 4
+)""4 5
+parts""6 ;
+.""; <
+Add""< ?
+(""? @
+$"""@ B
+$str""B P
+{""P Q
+ClientLocale""Q ]
+}""] ^
+"""^ _
+)""_ `
+;""` a
+if##
+
+(##
+Pooling##
+)##
+parts##
+.##
+Add##
+(##
+$str## -
+)##- .
+;##. /
+return$$
+string$$
+.$$
+Join$$
+($$
+$str$$
+,$$
+parts$$ %
+)$$% &
+;$$& '
+}%%
+}&& }
+0/workspaces/AS400API/Endpoints/As400Endpoints.cs
+ namespace
+AS400API
+
+.
+ Endpoints
+;
+public
+static
+class
+As400Endpoints "
+{
+public
+
+static
+RouteGroupBuilder #
+MapAs400Endpoints$ 5
+(5 6
+this6 :
+RouteGroupBuilder; L
+groupM R
+)R S
+{
+group
+.
+MapGet
+(
+$str &
+,& '
+async( -
+(. /
+string/ 5
+sql6 9
+,9 :
+OdbcConnection; I
+connJ N
+,N O
+ILoggerFactoryP ^
+
loggerFactory_ l
+)l m
+=>n p
+{
+var
+logger
+=
+
loggerFactory &
+.& '
+CreateLogger' 3
+(3 4
+$str4 H
+)H I
+;I J
+try
+{
+await
+conn
+.
+ OpenAsync $
+($ %
+)% &
+;& '
+if
+(
+string
+.
+IsNullOrWhiteSpace -
+(- .
+sql. 1
+)1 2
+)2 3
+{
+logger
+.
+
+LogWarning %
+(% &
+$str& _
+)_ `
+;` a
+return
+Results "
+." #
+
+BadRequest# -
+(- .
+new. 1
+{2 3
+error4 9
+=: ;
+$str< `
+}a b
+)b c
+;c d
+}
+logger
+.
+LogInformation %
+(% &
+$str& W
+,W X
+sqlY \
+?\ ]
+.] ^
+Length^ d
+??e g
+$numh i
+)i j
+;j k
+var
+rows
+=
+(
+await !
+conn" &
+.& '
+
+QueryAsync' 1
+(1 2
+sql2 5
+!5 6
+)6 7
+)7 8
+.8 9
+ToList9 ?
+(? @
+)@ A
+;A B
+var
+ formatted
+=
+rows $
+. $ %#
+ToCamelCaseDictionaries % <
+( < =
+) = >
+. > ?
+ToList ? E
+( E F
+) F G
+; G H
+logger!!
+.!!
+LogInformation!! %
+(!!% &
+$str!!& M
+,!!M N
+ formatted!!O X
+.!!X Y
+Count!!Y ^
+)!!^ _
+;!!_ `
+return""
+Results""
+.""
+Ok"" !
+(""! "
+ formatted""" +
+)""+ ,
+;"", -
+}##
+catch$$
+($$
+ Exception$$
+ex$$
+)$$
+{%%
+logger&&
+.&&
+LogError&&
+(&&
+ex&& "
+,&&" #
+$str&&$ 9
+)&&9 :
+;&&: ;
+return''
+Results''
+.''
+Problem'' &
+(''& '
+$"''' )
+$str'') 7
+{''7 8
+ex''8 :
+.'': ;
+Message''; B
+}''B C
+"''C D
+)''D E
+;''E F
+}((
+}))
+)))
+
+.**
+RequireAuthorization**
+(**
+AuthPolicies** *
+.*** +
+RequireAdmin**+ 7
+)**7 8
+.++
+WithSummary++
+(++
+$str++ Q
+)++Q R
+.,,
+Produces,,
+(,,
+$num,,
+),,
+.--
+ProducesProblem--
+(--
+$num--
+)--
+;--
+group//
+.//
+MapGet//
+(//
+$str// *
+,//* +
+async//, 1
+(//2 3
+OdbcConnection//3 A
+conn//B F
+,//F G
+ILoggerFactory//H V
+
loggerFactory//W d
+)//d e
+=>//f h
+{00
+var11
+logger11
+=11
+
loggerFactory11 &
+.11& '
+CreateLogger11' 3
+(113 4
+$str114 L
+)11L M
+;11M N
+try22
+{33
+await44
+conn44
+.44
+ OpenAsync44 $
+(44$ %
+)44% &
+;44& '
+const55
+string55
+sqlQuery55 %
+=55& '
+$str55( x
+;55x y
+var66
+schemas66
+=66
+(66
+await66 $
+conn66% )
+.66) *
+
+QueryAsync66* 4
+(664 5
+sqlQuery665 =
+)66= >
+)66> ?
+.66? @
+ToList66@ F
+(66F G
+)66G H
+;66H I
+var77
+ formatted77
+=77
+schemas77 '
+.77' (#
+ToCamelCaseDictionaries77( ?
+(77? @
+)77@ A
+.77A B
+ToList77B H
+(77H I
+)77I J
+;77J K
+logger88
+.88
+LogInformation88 %
+(88% &
+$str88& F
+,88F G
+ formatted88H Q
+.88Q R
+Count88R W
+)88W X
+;88X Y
+return99
+Results99
+.99
+Ok99 !
+(99! "
+ formatted99" +
+)99+ ,
+;99, -
+}::
+catch;;
+(;;
+ Exception;;
+ex;;
+);;
+{<<
+logger==
+.==
+LogError==
+(==
+ex== "
+,==" #
+$str==$ C
+)==C D
+;==D E
+return>>
+Results>>
+.>>
+Problem>> &
+(>>& '
+$">>' )
+$str>>) A
+{>>A B
+ex>>B D
+.>>D E
+Message>>E L
+}>>L M
+">>M N
+)>>N O
+;>>O P
+}??
+}@@
+)@@
+
+.AA
+RequireAuthorizationAA
+(AA
+AuthPoliciesAA *
+.AA* +
+RequireOperatorAA+ :
+)AA: ;
+.BB
+WithSummaryBB
+(BB
+$strBB K
+)BBK L
+;BBL M
+groupDD
+.DD
+MapGetDD
+(DD
+$strDD '
+,DD' (
+asyncDD) .
+(DD/ 0
+stringDD0 6
+libraryNameDD7 B
+,DDB C
+OdbcConnectionDDD R
+connDDS W
+,DDW X
+ILoggerFactoryDDY g
+
loggerFactoryDDh u
+)DDu v
+=>DDw y
+{EE
+varFF
+loggerFF
+=FF
+
loggerFactoryFF &
+.FF& '
+CreateLoggerFF' 3
+(FF3 4
+$strFF4 I
+)FFI J
+;FFJ K
+tryGG
+{HH
+awaitII
+connII
+.II
+ OpenAsyncII $
+(II$ %
+)II% &
+;II& '
+ifJJ
+(JJ
+stringJJ
+.JJ
+IsNullOrWhiteSpaceJJ -
+(JJ- .
+libraryNameJJ. 9
+)JJ9 :
+)JJ: ;
+{KK
+loggerLL
+.LL
+
+LogWarningLL %
+(LL% &
+$strLL& _
+)LL_ `
+;LL` a
+returnMM
+ResultsMM "
+.MM" #
+
+BadRequestMM# -
+(MM- .
+newMM. 1
+{MM2 3
+errorMM4 9
+=MM: ;
+$strMM< h
+}MMi j
+)MMj k
+;MMk l
+}NN
+constPP
+stringPP
+sqlQueryPP %
+=PP& '
+$strPU(
+;UU
+varWW
+tablesWW
+=WW
+(WW
+awaitWW #
+connWW$ (
+.WW( )
+
+QueryAsyncWW) 3
+(WW3 4
+sqlQueryWW4 <
+,WW< =
+newWW> A
+{WWB C
+libraryNameWWD O
+}WWP Q
+)WWQ R
+)WWR S
+.WWS T
+ToListWWT Z
+(WWZ [
+)WW[ \
+;WW\ ]
+loggerXX
+.XX
+LogInformationXX %
+(XX% &
+$strXX& [
+,XX[ \
+tablesXX] c
+.XXc d
+CountXXd i
+,XXi j
+libraryNameXXk v
+)XXv w
+;XXw x
+varYY
+ formattedYY
+=YY
+tablesYY &
+.YY& '#
+ToCamelCaseDictionariesYY' >
+(YY> ?
+)YY? @
+.YY@ A
+ToListYYA G
+(YYG H
+)YYH I
+;YYI J
+returnZZ
+ResultsZZ
+.ZZ
+OkZZ !
+(ZZ! "
+ formattedZZ" +
+)ZZ+ ,
+;ZZ, -
+}[[
+catch\\
+(\\
+ Exception\\
+ex\\
+)\\
+{]]
+logger^^
+.^^
+LogError^^
+(^^
+ex^^ "
+,^^" #
+$str^^$ X
+,^^X Y
+libraryName^^Z e
+)^^e f
+;^^f g
+return__
+Results__
+.__
+Problem__ &
+(__& '
+$"__' )
+$str__) @
+{__@ A
+ex__A C
+.__C D
+Message__D K
+}__K L
+"__L M
+)__M N
+;__N O
+}``
+}aa
+)aa
+
+.bb
+RequireAuthorizationbb
+(bb
+AuthPoliciesbb *
+.bb* +
+RequireOperatorbb+ :
+)bb: ;
+.cc
+WithSummarycc
+(cc
+$strcc I
+)ccI J
+;ccJ K
+groupee
+.ee
+MapGetee
+(ee
+$stree 0
+,ee0 1
+asyncee2 7
+(ee8 9
+stringee9 ?
+libraryNameee@ K
+,eeK L
+stringeeM S
+ tableNameeeT ]
+,ee] ^
+OdbcConnectionee_ m
+conneen r
+,eer s
+ILoggerFactory eet
+
loggerFactory
+ee
+)
+ee
+=>
+ee
+{ff
+vargg
+loggergg
+=gg
+
loggerFactorygg &
+.gg& '
+CreateLoggergg' 3
+(gg3 4
+$strgg4 Q
+)ggQ R
+;ggR S
+tryhh
+{ii
+awaitjj
+connjj
+.jj
+ OpenAsyncjj $
+(jj$ %
+)jj% &
+;jj& '
+ifkk
+(kk
+stringkk
+.kk
+IsNullOrWhiteSpacekk -
+(kk- .
+libraryNamekk. 9
+)kk9 :
+||kk; =
+stringkk> D
+.kkD E
+IsNullOrWhiteSpacekkE W
+(kkW X
+ tableNamekkX a
+)kka b
+)kkb c
+{ll
+loggermm
+.mm
+
+LogWarningmm %
+(mm% &
+$strmm& x
+)mmx y
+;mmy z
+returnnn
+Resultsnn "
+.nn" #
+
+BadRequestnn# -
+(nn- .
+newnn. 1
+{nn2 3
+errornn4 9
+=nn: ;
+$strnn< z
+}nn{ |
+)nn| }
+;nn} ~
+}oo
+constqq
+stringqq
+sqlQueryqq %
+=qq& '
+$strqw(
+;ww
+awaityy
+usingyy
+varyy
+commandyy '
+=yy( )
+connyy* .
+.yy. /
+
CreateCommandyy/ <
+(yy< =
+)yy= >
+;yy> ?
+commandzz
+.zz
+CommandTextzz #
+=zz$ %
+sqlQueryzz& .
+;zz. /
+var||
+libraryParameter|| $
+=||% &
+command||' .
+.||. /
+CreateParameter||/ >
+(||> ?
+)||? @
+;||@ A
+libraryParameter}}
+.}} !
+Value}}! &
+=}}' (
+libraryName}}) 4
+.}}4 5
+ToUpper}}5 <
+(}}< =
+)}}= >
+;}}> ?
+command~~
+.~~
+
+Parameters~~ "
+.~~" #
+Add~~# &
+(~~& '
+libraryParameter~~' 7
+)~~7 8
+;~~8 9
+var
+
+tableParameter
+ "
+=
+# $
+command
+% ,
+.
+, -
+CreateParameter
+- <
+(
+< =
+)
+= >
+;
+> ?
+tableParameter
+
+.
+
+Value
+ $
+=
+% &
+ tableName
+' 0
+.
+0 1
+ToUpper
+1 8
+(
+8 9
+)
+9 :
+;
+: ;
+command
+
+.
+
+
+Parameters
+ "
+.
+" #
+Add
+# &
+(
+& '
+tableParameter
+' 5
+)
+5 6
+;
+6 7
+var
+
+columns
+
+=
+
+new
+ !
+List
+" &
+<
+& '
+IDictionary
+' 2
+<
+2 3
+string
+3 9
+,
+9 :
+object
+; A
+>
+A B
+>
+B C
+(
+C D
+)
+D E
+;
+E F
+await
+
+using
+
+(
+
+var
+
+reader
+! '
+=
+( )
+await
+* /
+command
+0 7
+.
+7 8
+ExecuteReaderAsync
+8 J
+(
+J K
+)
+K L
+)
+L M
+{
+
+while
+
+(
+
+await
+
+reader
+! '
+.
+' (
+ ReadAsync
+( 1
+(
+1 2
+)
+2 3
+)
+3 4
+{
+
+var
+
+row
+
+=
+ !
+new
+" %
+
+Dictionary
+& 0
+<
+0 1
+string
+1 7
+,
+7 8
+object
+9 ?
+>
+? @
+(
+@ A
+StringComparer
+A O
+.
+O P
+OrdinalIgnoreCase
+P a
+)
+a b
+;
+b c
+for
+
+(
+
+var
+
+i
+! "
+=
+# $
+$num
+% &
+;
+& '
+i
+( )
+<
+* +
+reader
+, 2
+.
+2 3
+
+FieldCount
+3 =
+;
+= >
+i
+? @
+++
+@ B
+)
+B C
+{
+
+row
+
+[
+
+reader
+ &
+.
+& '
+GetName
+' .
+(
+. /
+i
+/ 0
+)
+0 1
+]
+1 2
+=
+3 4
+reader
+5 ;
+.
+; <
+GetNormalizedValue
+< N
+(
+N O
+i
+O P
+)
+P Q
+!
+Q R
+;
+R S
+}
+
+columns
+
+.
+
+Add
+ #
+(
+# $
+row
+$ '
+)
+' (
+;
+( )
+}
+
+}
+
+logger
+
+.
+
+LogInformation
+ %
+(
+% &
+$str
+& U
+,
+U V
+columns
+W ^
+.
+^ _
+Count
+_ d
+,
+d e
+libraryName
+f q
+,
+q r
+ tableName
+s |
+)
+| }
+;
+} ~
+var
+
+ formatted
+
+=
+
+columns
+ '
+.
+' (
+Select
+( .
+(
+. /
+dict
+/ 3
+=>
+4 6
+dict
+7 ;
+.
+; <#
+ToCamelCaseDictionary
+< Q
+(
+Q R
+)
+R S
+)
+S T
+.
+T U
+ToList
+U [
+(
+[ \
+)
+\ ]
+;
+] ^
+return
+
+Results
+
+.
+
+Ok
+ !
+(
+! "
+ formatted
+" +
+)
++ ,
+;
+, -
+}
+
+catch
+
+(
+
+ Exception
+
+ex
+
+)
+
+{
+
+logger
+
+.
+
+LogError
+
+(
+
+ex
+ "
+,
+" #
+$str
+$ R
+,
+R S
+libraryName
+T _
+,
+_ `
+ tableName
+a j
+)
+j k
+;
+k l
+return
+
+Results
+
+.
+
+Problem
+ &
+(
+& '
+$"
+' )
+$str
+) A
+{
+A B
+ex
+B D
+.
+D E
+Message
+E L
+}
+L M
+"
+M N
+)
+N O
+;
+O P
+}
+
+}
+
+)
+
+
+.
+
+WithSummary
+
+(
+
+$str
+ X
+)
+X Y
+;
+Y Z
+return
+
+group
+
+;
+
+}
+
+}
+6/workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs
+ namespace
+AS400API
+
+.
+ Endpoints
+;
+public
+static
+class
+ORDUAGEndpoint "
+{
+private
+static
+readonly
+string "
+[" #
+]# $
+ColumnNames% 0
+=1 2
+[
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str
+,
+$str!!
+,!!
+$str""
+,""
+$str##
+,##
+$str$$
+,$$
+$str%%
+,%%
+$str&&
+,&&
+$str''
+,''
+$str((
+,((
+$str))
+,))
+$str**
+,**
+$str++
+,++
+$str,,
+,,,
+$str--
+,--
+$str..
+,..
+$str//
+,//
+$str00
+,00
+$str11
+,11
+$str22 #
+,22# $
+$str33
+,33 !
+$str44
+,44
+$str55
+,55
+$str66
+,66
+$str77
+,77
+$str88
+,88
+$str99
+,99
+$str::
+,::
+$str;;
+,;;
+$str<< #
+,<<# $
+$str==
+,==
+$str>>
+,>>
+$str??
+,??
+$str@@
+,@@
+$strAA
+,AA
+$strBB
+,BB
+$strCC
+,CC
+$strDD
+,DD
+$strEE
+,EE
+$strFF
+,FF
+$strGG
+,GG
+$strHH
+,HH
+$strII
+,II
+$strJJ
+,JJ
+$strKK "
+,KK" #
+$strLL "
+,LL" #
+$strMM "
+,MM" #
+$strNN "
+,NN" #
+$strOO "
+,OO" #
+$strPP "
+,PP" #
+$strQQ "
+,QQ" #
+$strRR "
+,RR" #
+$strSS "
+,SS" #
+$strTT #
+,TT# $
+$strUU #
+,UU# $
+$strVV #
+,VV# $
+$strWW "
+,WW" #
+$strXX
+,XX
+$strYY $
+,YY$ %
+$strZZ !
+,ZZ! "
+$str[[ $
+,[[$ %
+$str\\ !
+,\\! "
+$str]] "
+,]]" #
+$str^^
+,^^
+$str__ !
+,__! "
+$str``
+,``
+$straa !
+,aa! "
+$strbb
+,bb
+$strcc
+,cc
+$strdd
+,dd
+$stree $
+,ee$ %
+$strff !
+,ff! "
+$strgg %
+,gg% &
+$strhh "
+,hh" #
+$strii
+,ii
+$strjj
+,jj
+$strkk
+,kk
+$strll
+,ll
+$strmm
+,mm
+$strnn
+,nn
+$stroo
+,oo
+$strpp
+,pp
+$strqq
+,qq
+$strrr
+,rr
+$strss
+,ss
+$strtt
+,tt
+$struu
+,uu
+$strvv
+,vv
+$strww
+,ww
+$strxx
+,xx
+$stryy
+,yy
+$strzz
+,zz
+$str{{
+,{{
+$str||
+,||
+$str}}
+,}} !
+$str~~
+,~~ !
+$str
+, !
+$str
+
+,
+ !
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+,
+
+$str
+
+]
+
+;
+
+private
+
+static
+
+readonly
+
+Regex
+ !
+LibraryNamePattern
+" 4
+=
+5 6
+new
+7 :
+(
+: ;
+$str
+; I
+,
+I J
+RegexOptions
+K W
+.
+W X
+Compiled
+X `
+|
+a b
+RegexOptions
+c o
+.
+o p
+
+IgnoreCase
+p z
+)
+z {
+;
+{ |
+public
+
+
+static
+
+RouteGroupBuilder
+ #
+MapORDUAGEndpoints
+$ 6
+(
+6 7
+this
+7 ;
+RouteGroupBuilder
+< M
+group
+N S
+)
+S T
+{
+
+group
+
+.
+
+MapGet
+
+(
+
+$str
+ '
+,
+' (
+async
+) .
+(
+/ 0
+[
+
+AsParameters
+
+]
+
+OrduagQuery
+ *
+query
++ 0
+,
+0 1
+OdbcConnection
+
+conn
+ #
+,
+# $
+ILoggerFactory
+
+
loggerFactory
+ ,
+)
+, -
+=>
+. 0
+{
+
+var
+
+logger
+
+=
+
+
loggerFactory
+ *
+.
+* +
+CreateLogger
++ 7
+(
+7 8
+$str
+8 H
+)
+H I
+;
+I J
+try
+
+{
+
+await
+
+conn
+
+.
+
+ OpenAsync
+ (
+(
+( )
+)
+) *
+;
+* +
+var
+
+page
+
+=
+
+query
+ $
+.
+$ %
+Page
+% )
+.
+) *
+GetValueOrDefault
+* ;
+(
+; <
+$num
+< =
+)
+= >
+;
+> ?
+if
+
+(
+
+page
+
+<
+
+$num
+
+)
+ !
+{
+
+logger
+
+.
+
+
+LogWarning
+ )
+(
+) *
+$str
+* V
+,
+V W
+query
+X ]
+.
+] ^
+Page
+^ b
+)
+b c
+;
+c d
+page
+
+=
+
+$num
+
+;
+ !
+}
+
+var
+
+pageSize
+
+=
+! "
+query
+# (
+.
+( )
+PageSize
+) 1
+.
+1 2
+GetValueOrDefault
+2 C
+(
+C D
+$num
+D F
+)
+F G
+;
+G H
+pageSize
+
+=
+
+Math
+ #
+.
+# $
+Clamp
+$ )
+(
+) *
+pageSize
+* 2
+,
+2 3
+$num
+4 5
+,
+5 6
+$num
+7 :
+)
+: ;
+;
+; <
+var
+
+
+offsetLong
+ "
+=
+# $
+(
+% &
+long
+& *
+)
+* +
+(
++ ,
+page
+, 0
+-
+1 2
+$num
+3 4
+)
+4 5
+*
+6 7
+pageSize
+8 @
+;
+@ A
+if
+
+(
+
+
+offsetLong
+ "
+>
+# $
+int
+% (
+.
+( )
+MaxValue
+) 1
+)
+1 2
+{
+
+return
+
+Results
+ &
+.
+& '
+
+BadRequest
+' 1
+(
+1 2
+new
+2 5
+{
+6 7
+error
+8 =
+=
+> ?
+$str
+@ ^
+}
+_ `
+)
+` a
+;
+a b
+}
+
+var
+
+offset
+
+=
+
+(
+! "
+int
+" %
+)
+% &
+
+offsetLong
+& 0
+;
+0 1
+var
+
+
+sqlBuilder
+ "
+=
+# $
+new
+% (
+
StringBuilder
+) 6
+(
+6 7
+)
+7 8
+;
+8 9
+
+sqlBuilder
+
+.
+
+
+AppendLine
+ )
+(
+) *
+$str
+* 2
+)
+2 3
+;
+3 4
+
+sqlBuilder
+
+.
+
+
+AppendLine
+ )
+(
+) *
+string
+* 0
+.
+0 1
+Join
+1 5
+(
+5 6
+$str
+6 ;
+,
+; <
+ColumnNames
+= H
+.
+H I
+Select
+I O
+(
+O P
+column
+P V
+=>
+W Y
+$"
+Z \
+$str
+\ `
+{
+` a
+column
+a g
+}
+g h
+"
+h i
+)
+i j
+)
+j k
+)
+k l
+;
+l m
+
+sqlBuilder
+
+.
+
+
+AppendLine
+ )
+(
+) *
+$str
+* ?
+)
+? @
+;
+@ A
+
+sqlBuilder
+
+.
+
+
+AppendLine
+ )
+(
+) *
+$str
+* 7
+)
+7 8
+;
+8 9
+var
+
+parameterValues
+ '
+=
+( )
+new
+* -
+List
+. 2
+<
+2 3
+object
+3 9
+?
+9 :
+>
+: ;
+(
+; <
+)
+< =
+;
+= >
+if
+
+(
+
+!
+
+string
+
+.
+
+IsNullOrWhiteSpace
+ 2
+(
+2 3
+query
+3 8
+.
+8 9
+
+CodeNumber
+9 C
+)
+C D
+)
+D E
+{
+
+
+sqlBuilder
+ "
+.
+" #
+
+AppendLine
+# -
+(
+- .
+$str
+. H
+)
+H I
+;
+I J
+parameterValues
+ '
+.
+' (
+Add
+( +
+(
++ ,
+query
+, 1
+.
+1 2
+
+CodeNumber
+2 <
+.
+< =
+Trim
+= A
+(
+A B
+)
+B C
+)
+C D
+;
+D E
+}
+
+if
+
+(
+
+!
+
+string
+
+.
+
+IsNullOrWhiteSpace
+ 2
+(
+2 3
+query
+3 8
+.
+8 9
+AgentAgency
+9 D
+)
+D E
+)
+E F
+{
+
+
+sqlBuilder
+ "
+.
+" #
+
+AppendLine
+# -
+(
+- .
+$str
+. F
+)
+F G
+;
+G H
+parameterValues
+ '
+.
+' (
+Add
+( +
+(
++ ,
+query
+, 1
+.
+1 2
+AgentAgency
+2 =
+.
+= >
+Trim
+> B
+(
+B C
+)
+C D
+)
+D E
+;
+E F
+}
+
+if
+
+(
+
+!
+
+string
+
+.
+
+IsNullOrWhiteSpace
+ 2
+(
+2 3
+query
+3 8
+.
+8 9
+ AgentStat
+9 B
+)
+B C
+)
+C D
+{
+
+
+sqlBuilder
+ "
+.
+" #
+
+AppendLine
+# -
+(
+- .
+$str
+. D
+)
+D E
+;
+E F
+parameterValues
+ '
+.
+' (
+Add
+( +
+(
++ ,
+query
+, 1
+.
+1 2
+ AgentStat
+2 ;
+.
+; <
+Trim
+< @
+(
+@ A
+)
+A B
+)
+B C
+;
+C D
+}
+
+
+sqlBuilder
+
+.
+
+
+AppendLine
+ )
+(
+) *
+$str
+* C
+)
+C D
+;
+D E
+
+sqlBuilder
+
+.
+
+
+AppendLine
+ )
+(
+) *
+$str
+* P
+)
+P Q
+;
+Q R
+parameterValues
+ #
+.
+# $
+Add
+$ '
+(
+' (
+offset
+( .
+)
+. /
+;
+/ 0
+parameterValues
+ #
+.
+# $
+Add
+$ '
+(
+' (
+pageSize
+( 0
+)
+0 1
+;
+1 2
+await
+
+using
+
+var
+ #
+command
+$ +
+=
+, -
+conn
+. 2
+.
+2 3
+
CreateCommand
+3 @
+(
+@ A
+)
+A B
+;
+B C
+command
+
+.
+
+CommandText
+ '
+=
+( )
+
+sqlBuilder
+* 4
+.
+4 5
+ToString
+5 =
+(
+= >
+)
+> ?
+;
+? @
+foreach
+
+(
+
+var
+
+value
+! &
+in
+' )
+parameterValues
+* 9
+)
+9 :
+{
+
+var
+
+ parameter
+ %
+=
+& '
+command
+( /
+.
+/ 0
+CreateParameter
+0 ?
+(
+? @
+)
+@ A
+;
+A B
+ parameter
+ !
+.
+! "
+Value
+" '
+=
+( )
+value
+* /
+;
+/ 0
+command
+
+.
+
+
+Parameters
+ *
+.
+* +
+Add
++ .
+(
+. /
+ parameter
+/ 8
+)
+8 9
+;
+9 :
+}
+
+var
+
+rows
+
+=
+
+new
+ "
+List
+# '
+<
+' (
+IDictionary
+( 3
+<
+3 4
+string
+4 :
+,
+: ;
+object
+< B
+>
+B C
+>
+C D
+(
+D E
+)
+E F
+;
+F G
+await
+
+using
+
+(
+ !
+var
+! $
+reader
+% +
+=
+, -
+await
+. 3
+command
+4 ;
+.
+; <
+ExecuteReaderAsync
+< N
+(
+N O
+)
+O P
+)
+P Q
+{
+
+while
+
+(
+
+await
+ $
+reader
+% +
+.
++ ,
+ ReadAsync
+, 5
+(
+5 6
+)
+6 7
+)
+7 8
+{
+
+var
+
+row
+ #
+=
+$ %
+new
+& )
+
+Dictionary
+* 4
+<
+4 5
+string
+5 ;
+,
+; <
+object
+= C
+>
+C D
+(
+D E
+StringComparer
+E S
+.
+S T
+OrdinalIgnoreCase
+T e
+)
+e f
+;
+f g
+for
+
+(
+ !
+var
+! $
+i
+% &
+=
+' (
+$num
+) *
+;
+* +
+i
+, -
+<
+. /
+reader
+0 6
+.
+6 7
+
+FieldCount
+7 A
+;
+A B
+i
+C D
+++
+D F
+)
+F G
+{
+
+var
+ #
+value
+$ )
+=
+* +
+reader
+, 2
+.
+2 3
+GetNormalizedValue
+3 E
+(
+E F
+i
+F G
+)
+G H
+;
+H I
+row
+ #
+[
+# $
+reader
+$ *
+.
+* +
+GetName
++ 2
+(
+2 3
+i
+3 4
+)
+4 5
+]
+5 6
+=
+7 8
+value
+9 >
+!
+> ?
+;
+? @
+}
+
+rows
+
+.
+ !
+Add
+! $
+(
+$ %
+row
+% (
+)
+( )
+;
+) *
+}
+
+}
+
+logger
+
+.
+
+LogInformation
+ )
+(
+) *
+$str
+,
+rows
+
+.
+
+Count
+ "
+,
+" #
+query
+
+.
+
+
+CodeNumber
+ (
+,
+( )
+query
+
+.
+
+AgentAgency
+ )
+,
+) *
+query
+
+.
+
+ AgentStat
+ '
+,
+' (
+page
+
+,
+
+pageSize
+
+)
+ !
+;
+! "
+var
+
+ formatted
+ !
+=
+" #
+rows
+$ (
+.
+( )
+Select
+) /
+(
+/ 0
+dict
+0 4
+=>
+5 7
+dict
+8 <
+.
+< =#
+ToCamelCaseDictionary
+= R
+(
+R S
+)
+S T
+)
+T U
+.
+U V
+ToList
+V \
+(
+\ ]
+)
+] ^
+;
+^ _
+return
+
+Results
+ "
+.
+" #
+Ok
+# %
+(
+% &
+ formatted
+& /
+)
+/ 0
+;
+0 1
+}
+
+catch
+
+(
+
+ Exception
+
+ex
+! #
+)
+# $
+{
+
+logger
+
+.
+
+LogError
+ #
+(
+# $
+ex
+$ &
+,
+& '
+$str
+( Q
+)
+Q R
+;
+R S
+return
+
+Results
+ "
+.
+" #
+Problem
+# *
+(
+* +
+$"
++ -
+$str
+- D
+{
+D E
+ex
+E G
+.
+G H
+Message
+H O
+}
+O P
+"
+P Q
+)
+Q R
+;
+R S
+}
+
+}
+
+)
+
+.
+
+WithSummary
+
+(
+
+$str
+ M
+)
+M N
+;
+N O
+return
+
+group
+
+;
+
+}
+
+public
+
+
+sealed
+
+class
+
+OrduagQuery
+ #
+{
+
+public
+
+string
+
+?
+
+
+CodeNumber
+ !
+{
+" #
+get
+$ '
+;
+' (
+init
+) -
+;
+- .
+}
+/ 0
+public
+
+string
+
+?
+
+AgentAgency
+ "
+{
+# $
+get
+% (
+;
+( )
+init
+* .
+;
+. /
+}
+0 1
+public
+
+string
+
+?
+
+ AgentStat
+
+{
+! "
+get
+# &
+;
+& '
+init
+( ,
+;
+, -
+}
+. /
+public
+
+int
+
+?
+
+Page
+
+{
+
+get
+
+;
+
+init
+ $
+;
+$ %
+}
+& '
+public
+
+int
+
+?
+
+PageSize
+
+{
+
+get
+ "
+;
+" #
+init
+$ (
+;
+( )
+}
+* +
+}
+
+} (
+//workspaces/AS400API/Endpoints/AuthEndpoints.cs
+ namespace
+AS400API
+
+.
+ Endpoints
+;
+public
+static
+class
+
AuthEndpoints !
+{
+public
+
+static
+RouteGroupBuilder
#
+MapAuthEndpoints
$ 4
+(
4 5
+this
5 9
+RouteGroupBuilder
: K
+group
L Q
+)
Q R
+{
+group
+.
+MapPost
+(
+$str &
+,& '
+async( -
+Task. 2
+<2 3
+IResult3 :
+>: ;
+(< =
+LoginRequest= I
+requestJ Q
+,Q R
+
DemoUserStoreS `
+ userStorea j
+,j k
+TokenServicel x
+tokenService y
+,
+
+
+JwtOptions
+
+
+jwtOptions
+
+)
+
+=>
+
+{
+if
+(
+request
+is
+null
+|| "
+string# )
+.) *
+IsNullOrWhiteSpace* <
+(< =
+request= D
+.D E
+UsernameE M
+)M N
+||O Q
+stringR X
+.X Y
+IsNullOrWhiteSpaceY k
+(k l
+requestl s
+.s t
+Passwordt |
+)| }
+)} ~
+{
+return
+Results
+.
+
+BadRequest )
+() *
+new* -
+{. /
+error0 5
+=6 7
+$str8 ]
+}^ _
+)_ `
+;` a
+}
+var
+user
+=
+await
+ userStore &
+.& '
+FindByNameAsync' 6
+(6 7
+request7 >
+.> ?
+Username? G
+)G H
+;H I
+if
+(
+user
+is
+null
+||
+! !
+ userStore! *
+.* +
+ValidateCredentials+ >
+(> ?
+user? C
+,C D
+requestE L
+.L M
+PasswordM U
+)U V
+)V W
+{
+return
+Results
+.
+Unauthorized +
+(+ ,
+), -
+;- .
+}
+var
+accessToken
+=
+tokenService *
+.* +
+CreateToken+ 6
+(6 7
+user7 ;
+); <
+;< =
+return
+Results
+.
+Ok
+(
+new !
+
LoginResponse" /
+(/ 0
+accessToken0 ;
+,; <
+
+jwtOptions= G
+.G H&
+AccessTokenLifetimeMinutesH b
+*c d
+$nume g
+,g h
+$stri q
+,q r
+users w
+.w x
+Rolesx }
+)} ~
+)~
+;
+}
+)
+
+.
+AllowAnonymous
+(
+)
+.
+WithSummary
+(
+$str B
+) B C
+.!!
+Produces!!
+<!!
+
LoginResponse!!
+>!!
+(!! !
+StatusCodes!!! ,
+.!!, -
+Status200OK!!- 8
+)!!8 9
+.""
+Produces""
+(""
+StatusCodes""
+.""
+Status400BadRequest"" 1
+)""1 2
+.##
+Produces##
+(##
+StatusCodes##
+.## !
+Status401Unauthorized## 3
+)##3 4
+;##4 5
+group%%
+.%%
+MapGet%%
+(%%
+$str%% #
+,%%# $
+(%%% &
+ClaimsPrincipal%%& 5
+user%%6 :
+)%%: ;
+=>%%< >
+{&&
+var''
+username''
+=''
+user''
+.''
+Identity'' (
+?''( )
+.'') *
+Name''* .
+??''/ 1
+user''2 6
+.''6 7
+FindFirstValue''7 E
+(''E F#
+JwtRegisteredClaimNames''F ]
+.''] ^
+Sub''^ a
+)''a b
+??''c e
+$str''f o
+;''o p
+var((
+roles((
+=((
+user((
+.((
+FindAll(( $
+((($ %
+
+ClaimTypes((% /
+.((/ 0
+Role((0 4
+)((4 5
+.((5 6
+Select((6 <
+(((< =
+r((= >
+=>((? A
+r((B C
+.((C D
+Value((D I
+)((I J
+.((J K
+ToArray((K R
+(((R S
+)((S T
+;((T U
+return))
+Results))
+.))
+Ok))
+())
+new)) !
+{))" #
+username))$ ,
+,)), -
+roles)). 3
+}))4 5
+)))5 6
+;))6 7
+}**
+)**
+
+.++
+RequireAuthorization++
+(++
+)++
+.,,
+WithSummary,,
+(,,
+$str,, O
+),,O P
+;,,P Q
+return..
+group..
+;..
+}//
+}00
+1/workspaces/AS400API/Endpoints/SystemEndpoints.cs
+ namespace
+AS400API
+
+.
+ Endpoints
+;
+public
+static
+class
+SystemEndpoints #
+{
+
+
+public
+
+static !
+IEndpointRouteBuilder '
+MapRootEndpoints( 8
+(8 9
+this9 =!
+IEndpointRouteBuilder> S
+appT W
+)W X
+{
+app
+.
+MapGet
+(
+$str
+,
+(
+)
+=>
+Results
%
+.
% &
+Ok
& (
+(
( )
+new
) ,
+{
- .
+name
/ 3
+=
4 5
+$str
6 @
+,
@ A
+status
B H
+=
I J
+$str
K O
+}
P Q
+)
Q R
+)
R S
+;
S T
+return
+app
+;
+}
+public
+
+static
+RouteGroupBuilder #
+MapSystemEndpoints$ 6
+(6 7
+this7 ;
+RouteGroupBuilder< M
+groupN S
+)S T
+{
+group
+.
+MapGet
+(
+$str !
+,! "
+async# (
+() *
+OdbcConnection* 8
+conn9 =
+)= >
+=>? A
+{
+try
+{
+await
+conn
+.
+ OpenAsync $
+($ %
+)% &
+;& '
+using
+var
+cmd
+=
+conn $
+.$ %
+
CreateCommand% 2
+(2 3
+)3 4
+;4 5
+cmd
+.
+CommandText
+= !
+$str" Y
+;Y Z
+using
+var
+reader
+=! "
+await# (
+cmd) ,
+., -
+ExecuteReaderAsync- ?
+(? @
+)@ A
+;A B
+if
+(
+await
+reader
+. !
+ ReadAsync! *
+(* +
+)+ ,
+), -
+{
+var
+d
+=
+reader "
+." #
+GetDateTime# .
+(. /
+$num/ 0
+)0 1
+;1 2
+return
+Results "
+." #
+Ok# %
+(% &
+new& )
+{* +
+AS400, 1
+=2 3
+$str4 <
+,< =
+currentDateOnAS400> P
+=Q R
+dS T
+.T U
+ToStringU ]
+(] ^
+$str^ j
+)j k
+,k l
+ timestampm v
+=w x
+DateTime y
+.
+
+UtcNow
+
+}
+
+)
+
+;
+
+}
+return!!
+Results!!
+.!!
+Ok!! !
+(!!! "
+new!!" %
+{!!& '
+AS400!!( -
+=!!. /
+$str!!0 8
+,!!8 9
+note!!: >
+=!!? @
+$str!!A J
+}!!K L
+)!!L M
+;!!M N
+}""
+catch##
+(##
+ Exception##
+ex##
+)##
+{$$
+return%%
+Results%%
+.%%
+Problem%% &
+(%%& '
+$"%%' )
+$str%%) B
+{%%B C
+ex%%C E
+.%%E F
+Message%%F M
+}%%M N
+"%%N O
+)%%O P
+;%%P Q
+}&&
+}''
+)''
+
+.''
+
+AllowAnonymous''
+(''
+)''
+;''
+return))
+group))
+;))
+}**
+}++ 6
+;/workspaces/AS400API/Infrastructure/DatabaseRowFormatter.cs
+ namespace
+AS400API
+
+.
+Infrastructure !
+;! "
+public
+static
+class
+DatabaseRowFormatter (
+{
+public
+
+
+
+static
+
+
+IEnumerable
+
+
+<
+
+
+IDictionary
+
+ )
+<
+
+) *
+string
+
+* 0
+,
+
+0 1
+object
+
+2 8
+?
+
+8 9
+>
+
+9 :
+>
+
+: ;#
+ToCamelCaseDictionaries
+
+< S
+(
+
+S T
+this
+
+T X
+IEnumerable
+
+Y d
+<
+
+d e
+dynamic
+
+e l
+>
+
+l m
+rows
+
+n r
+)
+
+r s
+{
+foreach
+(
+var
+row
+in
+rows
+) !
+{
+if
+(
+row
+is
+IDictionary "
+<" #
+string# )
+,) *
+object+ 1
+>1 2
+dict3 7
+)7 8
+{
+yield
+return
+dict !
+.! "!
+ToCamelCaseDictionary" 7
+(7 8
+)8 9
+;9 :
+}
+else
+{
+yield
+return
+new
+
+Dictionary! +
+<+ ,
+string, 2
+,2 3
+object4 :
+?: ;
+>; <
+{
+[
+$str
+]
+=
+row #
+}
+;
+}
+}
+}
+public
+
+static
+IDictionary
+<
+string $
+,$ %
+object& ,
+?, -
+>- .!
+ToCamelCaseDictionary/ D
+(D E
+thisE I
+IDictionaryJ U
+<U V
+stringV \
+,\ ]
+object^ d
+>d e
+sourcef l
+)l m
+{
+var
+result
+=
+new
+
+Dictionary #
+<# $
+string$ *
+,* +
+object, 2
+?2 3
+>3 4
+(4 5
+StringComparer5 C
+.C D
+OrdinalIgnoreCaseD U
+)U V
+;V W
+foreach
+(
+var
+kvp
+in
+source "
+)" #
+{
+var!!
+key!!
+=!!
+string!!
+.!!
+IsNullOrWhiteSpace!! /
+(!!/ 0
+kvp!!0 3
+.!!3 4
+Key!!4 7
+)!!7 8
+?!!9 :
+kvp!!; >
+.!!> ?
+Key!!? B
+:!!C D
+ToCamelCase!!E P
+(!!P Q
+kvp!!Q T
+.!!T U
+Key!!U X
+)!!X Y
+;!!Y Z
+result""
+[""
+key""
+]""
+=""
+kvp""
+.""
+Value"" #
+;""# $
+}##
+return%%
+result%%
+;%%
+}&&
+private((
+static((
+string((
+ToCamelCase(( %
+(((% &
+string((& ,
+input((- 2
+)((2 3
+{))
+if**
+
+(**
+string**
+.**
+
IsNullOrEmpty**
+(** !
+input**! &
+)**& '
+)**' (
+{++
+return,,
+input,,
+;,,
+}--
+var//
+segments//
+=//
+input//
+.00
+Split00
+(00
+new00
+[00
+]00
+{00
+$char00
+,00
+$char00 #
+,00# $
+$char00% (
+}00) *
+,00* +
+StringSplitOptions00, >
+.00> ?
+RemoveEmptyEntries00? Q
+)00Q R
+.11
+Select11
+(11
+s11
+=>11
+s11
+.11
+ToLowerInvariant11 +
+(11+ ,
+)11, -
+)11- .
+.22
+ToArray22
+(22
+)22
+;22
+if44
+
+(44
+segments44
+.44
+Length44
+==44
+$num44
+)44 !
+{55
+return66
+input66
+;66
+}77
+if99
+
+(99
+segments99
+.99
+Length99
+==99
+$num99
+)99 !
+{::
+var;;
+segment;;
+=;;
+segments;; "
+[;;" #
+$num;;# $
+];;$ %
+;;;% &
+if<<
+(<<
+segment<<
+.<<
+Length<<
+==<< !
+$num<<" #
+)<<# $
+{==
+return>>
+segment>>
+.>>
+ToLowerInvariant>> /
+(>>/ 0
+)>>0 1
+;>>1 2
+}??
+returnAA
+charAA
+.AA
+ToLowerInvariantAA (
+(AA( )
+segmentAA) 0
+[AA0 1
+$numAA1 2
+]AA2 3
+)AA3 4
++AA5 6
+segmentAA7 >
+[AA> ?
+$numAA? @
+..AA@ B
+]AAB C
+;AAC D
+}BB
+varDD
+sbDD
+=DD
+newDD
+
StringBuilderDD "
+(DD" #
+)DD# $
+;DD$ %
+sbEE
+
+.EE
+
+AppendEE
+(EE
+segmentsEE
+[EE
+$numEE
+]EE
+)EE
+;EE
+forFF
+(FF
+varFF
+iFF
+=FF
+$numFF
+;FF
+iFF
+<FF
+segmentsFF $
+.FF$ %
+LengthFF% +
+;FF+ ,
+iFF- .
+++FF. 0
+)FF0 1
+{GG
+varHH
+segmentHH
+=HH
+segmentsHH "
+[HH" #
+iHH# $
+]HH$ %
+;HH% &
+ifII
+(II
+segmentII
+.II
+LengthII
+==II !
+$numII" #
+)II# $
+{JJ
+continueKK
+;KK
+}LL
+sbNN
+.NN
+AppendNN
+(NN
+charNN
+.NN
+ToUpperInvariantNN +
+(NN+ ,
+segmentNN, 3
+[NN3 4
+$numNN4 5
+]NN5 6
+)NN6 7
+)NN7 8
+;NN8 9
+ifOO
+(OO
+segmentOO
+.OO
+LengthOO
+>OO
+$numOO! "
+)OO" #
+{PP
+sbQQ
+.QQ
+AppendQQ
+(QQ
+segmentQQ !
+[QQ! "
+$numQQ" #
+..QQ# %
+]QQ% &
+)QQ& '
+;QQ' (
+}RR
+}SS
+returnUU
+sbUU
+.UU
+ToStringUU
+(UU
+)UU
+;UU
+}VV
+}WW
+;/workspaces/AS400API/Infrastructure/DataReaderExtensions.cs
+ namespace
+AS400API
+
+.
+Infrastructure !
+;! "
+public
+static
+class
+DataReaderExtensions (
+{
+public
+
+static
+object
+?
+GetNormalizedValue ,
+(, -
+this- 1
+DbDataReader2 >
+reader? E
+,E F
+intG J
+ordinalK R
+)R S
+{
+if
+
+
+
+(
+
+
+reader
+
+
+.
+
+
+IsDBNull
+
+
+(
+
+
+ordinal
+
+ #
+)
+
+# $
+)
+
+$ %
+{
+return
+null
+;
+}
+var
+value
+=
+reader
+.
+GetValue #
+(# $
+ordinal$ +
+)+ ,
+;, -
+if
+
+(
+value
+is
+string
+stringValue '
+)' (
+{
+var
+dataTypeName
+=
+reader %
+.% &
+GetDataTypeName& 5
+(5 6
+ordinal6 =
+)= >
+?> ?
+.? @
+Trim@ D
+(D E
+)E F
+;F G
+if
+(
+!
+string
+.
+
IsNullOrEmpty %
+(% &
+dataTypeName& 2
+)2 3
+&&
+dataTypeName
+.
+
+StartsWith *
+(* +
+$str+ 1
+,1 2
+StringComparison3 C
+.C D
+OrdinalIgnoreCaseD U
+)U V
+&&
+!
+dataTypeName
+. !
+Contains! )
+() *
+$str* 3
+,3 4
+StringComparison5 E
+.E F
+OrdinalIgnoreCaseF W
+)W X
+)X Y
+{
+return
+stringValue "
+." #
+TrimEnd# *
+(* +
+)+ ,
+;, -
+}
+}
+return
+value
+;
+}
+} Z
+/workspaces/AS400API/Program.cs
+var
+builderArgs
+=
+args
+??
+Array
+.
+Empty %
+<% &
+string& ,
+>, -
+(- .
+). /
+;/ 0
+var
+builder
+=
+WebApplication
+.
+
CreateBuilder
*
+(
* +
+builderArgs
+ 6
+)
6 7
+;
7 8
+builder
+.
+Host
+.
+
+UseSerilog
+(
+(
+context
+, !
+services" *
+,* +
+loggerConfiguration, ?
+)? @
+=>A C
+{
+loggerConfiguration
+.
+ReadFrom
+.
+
Configuration
+(
+context '
+.' (
+
Configuration( 5
+)5 6
+.
+Enrich
+.
+FromLogContext
+(
+)
+; !
+}
+)
+;
+var
+odbc
+=
+
+new
+OdbcOptions
+(
+)
+;
+builder
+.
+
Configuration
+.
+
+GetSection
+( !
+$str! '
+)' (
+.( )
+Bind) -
+(- .
+odbc. 2
+)2 3
+;3 4
+odbc
+.
+System
+??=
+Environment
+. "
+GetEnvironmentVariable 2
+(2 3
+$str3 A
+)A B
+;B C
+odbc
+.
+DefaultLibraries
+??=
+Environment %
+.% &"
+GetEnvironmentVariable& <
+(< =
+$str= V
+)V W
+;W X
+odbc
+.
+User
+??=
+
+Environment
+. "
+GetEnvironmentVariable 0
+(0 1
+$str1 =
+)= >
+;> ?
+odbc
+.
+Password
+??=
+Environment
+. "
+GetEnvironmentVariable 4
+( 4 5
+$str 5 E
+) E F
+; F G
+odbc!!
+.!!
+Naming!!
+??=!!
+Environment!!
+.!! "
+GetEnvironmentVariable!! 2
+(!!2 3
+$str!!3 A
+)!!A B
+??!!C E
+$str!!F I
+;!!I J
+if##
+(##
+string##
+
+.##
+
+IsNullOrWhiteSpace##
+(##
+odbc## "
+.##" #
+System### )
+)##) *
+||##+ -
+string$$
+
+.$$
+
+IsNullOrWhiteSpace$$
+($$
+odbc$$ "
+.$$" #
+User$$# '
+)$$' (
+||$$) +
+string%%
+
+.%%
+
+IsNullOrWhiteSpace%%
+(%%
+odbc%% "
+.%%" #
+Password%%# +
+)%%+ ,
+)%%, -
+{&&
+Console''
+.''
+ WriteLine''
+(''
+$str'' o
+)''o p
+;''p q
+}((
+var++
+
+jwtOptions++
+=++
+builder++
+.++
+
Configuration++ &
+.++& '
+
+GetSection++' 1
+(++1 2
+
+JwtOptions++2 <
+.++< =
+SectionName++= H
+)++H I
+.++I J
+Get++J M
+<++M N
+
+JwtOptions++N X
+>++X Y
+(++Y Z
+)++Z [
+??++\ ^
+new++_ b
+
+JwtOptions++c m
+(++m n
+)++n o
+;++o p
+
+jwtOptions,,
+
+.,,
+
+
EnsureIsValid,,
+(,,
+),,
+;,,
+var--
+
+signingKey--
+=--
+new--
+SymmetricSecurityKey-- )
+(--) *
+Encoding--* 2
+.--2 3
+UTF8--3 7
+.--7 8
+GetBytes--8 @
+(--@ A
+
+jwtOptions--A K
+.--K L
+Key--L O
+)--O P
+)--P Q
+;--Q R
+builder//
+.//
+Services//
+.// #
+AddEndpointsApiExplorer// (
+(//( )
+)//) *
+;//* +
+builder00
+.00
+Services00
+.00
+
AddSwaggerGen00
+(00
+options00 &
+=>00' )
+{11
+options22
+.22
+
+SwaggerDoc22
+(22
+$str22
+,22
+new22
+OpenApiInfo22! ,
+{33
+Title44
+=44
+$str44
+,44
+Version55
+=55
+$str55
+}66
+)66
+;66
+var88
+securityScheme88
+=88
+new88 !
+OpenApiSecurityScheme88 2
+{99
+Name::
+=::
+$str::
+,::
+Description;;
+=;;
+$str;; I
+,;;I J
+In<<
+
+=<<
+ParameterLocation<<
+.<<
+Header<< %
+,<<% &
+Type==
+===
+SecuritySchemeType== !
+.==! "
+Http==" &
+,==& '
+Scheme>>
+=>>
+$str>>
+,>>
+BearerFormat??
+=??
+$str??
+,??
+ Reference@@
+=@@
+new@@
+OpenApiReference@@ (
+{AA
+TypeBB
+=BB
+
ReferenceTypeBB
+.BB !
+SecuritySchemeBB! /
+,BB/ 0
+IdCC
+=CC
+$strCC
+}DD
+}EE
+;EE
+optionsGG
+.GG !
+AddSecurityDefinitionGG !
+(GG! "
+securitySchemeGG" 0
+.GG0 1
+ ReferenceGG1 :
+.GG: ;
+IdGG; =
+,GG= >
+securitySchemeGG? M
+)GGM N
+;GGN O
+optionsHH
+.HH "
+AddSecurityRequirementHH "
+(HH" #
+newHH# &&
+OpenApiSecurityRequirementHH' A
+{II
+{JJ
+securitySchemeJJ
+
+,JJ
+ArrayJJ
+.JJ
+EmptyJJ %
+<JJ% &
+stringJJ& ,
+>JJ, -
+(JJ- .
+)JJ. /
+}JJ0 1
+}KK
+)KK
+;KK
+}LL
+)LL
+;LL
+builderMM
+.MM
+ServicesMM
+.MM
+AddSingletonMM
+(MM
+odbcMM "
+)MM" #
+;MM# $
+builderNN
+.NN
+ServicesNN
+.NN
+ AddScopedNN
+<NN
+OdbcConnectionNN )
+>NN) *
+(NN* +
+_NN+ ,
+=>NN- /
+newNN0 3
+OdbcConnectionNN4 B
+(NNB C
+odbcNNC G
+.NNG H
+ToConnectionStringNNH Z
+(NNZ [
+)NN[ \
+)NN\ ]
+)NN] ^
+;NN^ _
+builderOO
+.OO
+ServicesOO
+.OO
+AddSingletonOO
+(OO
+
+jwtOptionsOO (
+)OO( )
+;OO) *
+builderPP
+.PP
+ServicesPP
+.PP
+AddSingletonPP
+<PP
+TokenServicePP *
+>PP* +
+(PP+ ,
+)PP, -
+;PP- .
+builderQQ
+.QQ
+ServicesQQ
+.QQ
+AddSingletonQQ
+<QQ
+
DemoUserStoreQQ +
+>QQ+ ,
+(QQ, -
+)QQ- .
+;QQ. /
+builderSS
+.SS
+ServicesSS
+.TT
+AddAuthenticationTT
+(TT
+JwtBearerDefaultsTT (
+.TT( )
+AuthenticationSchemeTT) =
+)TT= >
+.UU
+AddJwtBearerUU
+(UU
+optionsUU
+=>UU
+{VV
+optionsWW
+.WW %
+TokenValidationParametersWW )
+=WW* +
+newWW, /%
+TokenValidationParametersWW0 I
+{XX
+ValidateIssuerYY
+=YY
+trueYY !
+,YY! "
+ValidIssuerZZ
+=ZZ
+
+jwtOptionsZZ $
+.ZZ$ %
+IssuerZZ% +
+,ZZ+ ,
+ValidateAudience[[
+=[[
+true[[ #
+,[[# $
+
ValidAudience\\
+=\\
+
+jwtOptions\\ &
+.\\& '
+Audience\\' /
+,\\/ 0$
+ValidateIssuerSigningKey]] $
+=]]% &
+true]]' +
+,]]+ ,
+IssuerSigningKey^^
+=^^
+
+signingKey^^ )
+,^^) *
+ValidateLifetime__
+=__
+true__ #
+,__# $
+ ClockSkew``
+=``
+TimeSpan``
+.`` !
+FromMinutes``! ,
+(``, -
+$num``- .
+)``. /
+}aa
+;aa
+
+}bb
+)bb
+;bb
+builderdd
+.dd
+Servicesdd
+.dd
+AddAuthorizationdd !
+(dd! "
+optionsdd" )
+=>dd* ,
+{ee
+optionsff
+.ff
+ AddPolicyff
+(ff
+AuthPoliciesff "
+.ff" #
+RequireOperatorff# 2
+,ff2 3
+policyff4 :
+=>ff; =
+policygg
+.gg
+RequireRolegg
+(gg
+Rolesgg
+.gg !
+Admingg! &
+,gg& '
+Rolesgg( -
+.gg- .
+Operatorgg. 6
+)gg6 7
+)gg7 8
+;gg8 9
+optionshh
+.hh
+ AddPolicyhh
+(hh
+AuthPolicieshh "
+.hh" #
+RequireAdminhh# /
+,hh/ 0
+policyhh1 7
+=>hh8 :
+policyii
+.ii
+RequireRoleii
+(ii
+Rolesii
+.ii !
+Adminii! &
+)ii& '
+)ii' (
+;ii( )
+}jj
+)jj
+;jj
+varll
+appll
+=ll
+builderll
+
+.ll
+Buildll
+(ll
+)ll
+;ll
+ifnn
+(nn
+appnn
+.nn
+Environmentnn
+.nn
+
IsDevelopmentnn !
+(nn! "
+)nn" #
+)nn# $
+{oo
+apppp
+.pp
+
+UseSwaggerpp
+(pp
+)pp
+;pp
+appqq
+.qq
+UseSwaggerUIqq
+(qq
+)qq
+;qq
+}rr
+apptt
+.tt $
+UseSerilogRequestLoggingtt
+(tt
+)tt
+;tt
+appvv
+.vv
+UseAuthenticationvv
+(vv
+)vv
+;vv
+appww
+.ww
+UseAuthorizationww
+(ww
+)ww
+;ww
+appyy
+.yy
+MapRootEndpointsyy
+(yy
+)yy
+;yy
+var{{
+api{{
+={{
+app{{
+
+.{{
+MapGroup{{
+({{
+$str{{
+){{
+;{{
+api||
+.||
+MapSystemEndpoints||
+(||
+)||
+;||
+api}}
+.}}
+MapAuthEndpoints}}
+(}}
+)}}
+;}}
+api~~
+.~~
+MapAs400Endpoints~~
+(~~
+)~~
+;~~
+api
+.
+MapORDUAGEndpoints
+(
+)
+;
+app
+.
+
+Run
+
+(
+
+)
+
+;
+
diff --git a/.sonarqube/out/0/output-cs/token-type.pb b/.sonarqube/out/0/output-cs/token-type.pb
new file mode 100644
index 0000000..f7db817
--- /dev/null
+++ b/.sonarqube/out/0/output-cs/token-type.pb
@@ -0,0 +1,437 @@
+
+)/workspaces/AS400API/Auth/AuthPolicies.cs
+
+
+ * ;
+ ' 5
+%/workspaces/AS400API/Auth/DemoUser.cs
+
+
+
+ % +: @O bc i
+
+ ! $
+ ! $
+ %/ 2
+*/workspaces/AS400API/Auth/DemoUserStore.cs
+
+
+
+
+
! & ( 0
+
&
( 0
2 @
# *, 68 ;@ EM R
& 02 <> AF K
+ 0 6% () ,
+ $ ,3 9 ' -8 >I \] c )
+)/workspaces/AS400API/Auth/LoginRequest.cs
+
+
!" (3 9
+*/workspaces/AS400API/Auth/LoginResponse.cs
+
+
+
"# )7 :F LX kl r
++/workspaces/AS400API/Auth/PasswordHasher.cs
+
+
+
+
+
" ! #
+
+
+
+
+
+
+
+ " # *
+
&
: @ ( %= M 3 :
+ $/ 5B H )F V ! &
+"/workspaces/AS400API/Auth/Roles.cs
+
+
+ '
+ # -
+)/workspaces/AS400API/Auth/TokenService.cs
+
+
+
+
+
+
+
+
+
,= @
+ "
+ & ! $% 7 $% - # ' '- 1 "" "" "" $$ $$ $$! +'' '' '' (++ ..
+0/workspaces/AS400API/Configuration/JwtOptions.cs
+
+
+
+
+ & +
+
+
+
+
+
+
+
+
+ #
+
+) 3
+ " %+ =
+ & I
+
, /
1 4
: <
+
+ . 6P R /0
+* + /0 k
+1/workspaces/AS400API/Configuration/OdbcOptions.cs
+
+
+
+
+ ! $
+ & ) + .
+
+
+
+
+
+
+
+
+ "
+ !# &
Z
+ ! $
+ "$ '- 0
+ " %' *0 7
+ "( ,
+ @ $< OT n # # %% &
+
D FF Wi j
+
8 :: >D E
+
< >> BL M
+
: < < C K L!!
+!!
!!= ?!!? I!!T U""
+""
""@ B""B P""^ _##
+## -$$ $$ $$
+0/workspaces/AS400API/Endpoints/As400Endpoints.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
"
+ #6 :; L &( -/ 5; IP ^ 4 H & _ ". 1< `& Wh i ! !!& M"" "" $$ $$ &&$ 9'' '' ''' )'') 7''C D** *++ Q,, -- // *//, 1//3 A//H V11 114 L22 44 55 55 55( x66 66 $77 88& F99 99 ;; ;; ==$ C>> >> >>' )>>) A>>M NAA *BB KDD 'DD) .DD0 6DDD RDDY gFF FF4 IGG II JJ JJ LL& _MM MM "MM. 1MM< hPP PP PU( WW WW #WW> AXX& [YY ZZ ZZ \\ \\ ^^$ X__ __ __' )__) @__L Mbb *cc Iee 0ee2 7ee9 ?eeM See_ m
eet gg gg4 Qhh jj kk kk kk> Dmm& xnn nn "nn. 1nn< zqq qq qw( yy yy yy ||
+
+
+ !
+" &
+' 2
+3 9
+; A
+
+
+
+* /
+
+
+
+" %
+& 0
+1 7
+9 ?
+A O
+
+
+% &
+ `
+ 3
+& U
+
+
+
+
+
+$ R
+
+
+' )
+) A
+M N
+ >
+ X
+ "
+6/workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
" " !! "" ## $$ %% && '' (( )) ** ++ ,, -- .. // 00 11 22 #33 44 55 66 77 88 99 :: ;; << #== >> ?? @@ AA BB CC DD EE FF GG HH II JJ KK "LL "MM "NN "OO "PP "QQ "RR "SS "TT #UU #VV #WW "XX YY $ZZ ![[ $\\ !]] "^^ __ !`` aa !bb cc dd ee $ff !gg %hh "ii jj kk ll mm nn oo pp qq rr ss tt uu vv ww xx yy zz {{ || }} ~~
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ !
+7 :
+; I
+K W
+c o
+
+
+
+ #
+7 ;
+< M
+ '
+) .
+
+ *
+
+
+
+8 H
+
+
+
+< =
+
+
+* V
+
+
+D F
+ #
+4 5
+7 :
+
+& *
+3 4
+
+% (
+
+ &
+2 5
+@ ^
+
+" %
+
+% (
+) 6
+* 2
+* 0
+6 ;
+Z \
+h i
+* ?
+* 7
+
+* -
+. 2
+3 9
+
+
+. H
+
+
+. F
+
+
+. D
+* C
+* P
+
+
+ #
+
+
+' )
+
+
+ "
+# '
+( 3
+4 :
+< B
+
+
+! $
+. 3
+
+ $
+
+& )
+* 4
+5 ;
+= C
+E S
+
+! $
+) *
+ #
+
+
+ "
+
+
+( Q
+
+ "
++ -
+- D
+P Q
+ B
+ M
+
+
+
+
+
+ #
+
+
+$ '
+) -
+
+
+% (
+* .
+
+
+# &
+( ,
+
+
+
+ $
+
+
+ "
+$ (
+//workspaces/AS400API/Endpoints/AuthEndpoints.cs
+
+
+
+
+
+
+
+
+
!
+
#
5 9
: K &( -. 23 := IS `l x
+ # )R X * -8 ] !" /e gi q B!! !!! ,"" ## %% #%%& 5'' ''F ]''f o(( ((% /)) )) )) !,, O..
+1/workspaces/AS400API/Endpoints/SystemEndpoints.cs
+
+
+
+
+
+
+
#
+ '9 => S
%
) ,
6 @
K O
+ #7 ;< M !# (* 8 " Y # ( / 0 "& )4 <^ j
y !! !! !!" %!!0 8!!A J## ## %% %% %%' )%%) B%%N O))
+;/workspaces/AS400API/Infrastructure/DatabaseRowFormatter.cs
+
+
+
+
+
+
(
+
+
+
+
+
+
+
+
+ )
+
+* 0
+
+2 8
+
+T X
+
+Y d
+
+e l "# )+ 1 ! +, 24 :
+ $& ,E IJ UV \^ d #$ *, 25 C !! !! %% (( (( (( ((& ,**
+** ,, // 00 00 00 #00% (00, >44
+44 66 99
+99 ;; ;;# $<< <<" #>> AA AA AA1 2AA? @DD DD DD "EE FF FF
FF HH II II" #KK NN NN4 5OO OO! "QQ" #UU
+;/workspaces/AS400API/Infrastructure/DataReaderExtensions.cs
+
+
+
+
(
+ - 12 >G J
+
+
+
+ + 13 C* 35 E
+
+/workspaces/AS400API/Program.cs
+
+
+
+
+
+
+
+
+
+ 4
+ & ,
+
+
+ ! '
+ , 3 A %= V 1 = 5 E!! !!3 A!!F I
+## ##
+$$
+%%
+'' '' o
+**
+++ ++2 <++N X++_ b++c m
+-- -- -- )--* 222 22 22! ,44 55 88 88 88 2:: ;; I<<
== !>> ?? @@ @@ (BB CC HH# &HH' AJJ JJ& ,NN )NN0 3NN4 BPP *QQ +TT (WW, /WW0 IYY ![[ #]]' +__ #`` ``- .ff "gg gg( -hh "ii
+ll
+nn
+{{ {{ 1 F
\ No newline at end of file
diff --git a/.sonarqube/out/ProjectInfo.log b/.sonarqube/out/ProjectInfo.log
new file mode 100644
index 0000000..c6b7312
--- /dev/null
+++ b/.sonarqube/out/ProjectInfo.log
@@ -0,0 +1,24 @@
+Product projects
+---------------------------------------
+/workspaces/AS400API/AS400API.csproj
+
+
+Test projects
+---------------------------------------
+
+
+Invalid projects
+---------------------------------------
+{none}
+
+
+Skipped projects
+---------------------------------------
+{none}
+
+
+Excluded projects
+---------------------------------------
+{none}
+
+
diff --git a/.sonarqube/out/ScannerEngineInput.json b/.sonarqube/out/ScannerEngineInput.json
new file mode 100644
index 0000000..0a10269
--- /dev/null
+++ b/.sonarqube/out/ScannerEngineInput.json
@@ -0,0 +1,104 @@
+{
+ "scannerProperties": [
+ {
+ "key": "sonar.scanner.app",
+ "value": "ScannerMSBuild"
+ },
+ {
+ "key": "sonar.scanner.appVersion",
+ "value": "10.4.1"
+ },
+ {
+ "key": "sonar.projectKey",
+ "value": "as400api"
+ },
+ {
+ "key": "sonar.projectName",
+ "value": "AS400API"
+ },
+ {
+ "key": "sonar.working.directory",
+ "value": "/workspaces/AS400API/.sonarqube/out/.sonar"
+ },
+ {
+ "key": "sonar.projectBaseDir",
+ "value": "/workspaces/AS400API"
+ },
+ {
+ "key": "sonar.pullrequest.cache.basepath",
+ "value": "/workspaces/AS400API"
+ },
+ {
+ "key": "sonar.sources",
+ "value": ""
+ },
+ {
+ "key": "sonar.tests",
+ "value": ""
+ },
+ {
+ "key": "sonar.modules",
+ "value": "40CB5B53-77B8-B1FD-458C-805350CB09E8"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.projectKey",
+ "value": "as400api:40CB5B53-77B8-B1FD-458C-805350CB09E8"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.projectName",
+ "value": "AS400API"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.projectBaseDir",
+ "value": "/workspaces/AS400API"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.working.directory",
+ "value": "/workspaces/AS400API/.sonarqube/out/.sonar/mod0"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.sourceEncoding",
+ "value": "utf-8"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.tests",
+ "value": ""
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.sources",
+ "value": "/workspaces/AS400API/Auth/AuthPolicies.cs,/workspaces/AS400API/Auth/DemoUser.cs,/workspaces/AS400API/Auth/DemoUserStore.cs,/workspaces/AS400API/Auth/LoginRequest.cs,/workspaces/AS400API/Auth/LoginResponse.cs,/workspaces/AS400API/Auth/PasswordHasher.cs,/workspaces/AS400API/Auth/Roles.cs,/workspaces/AS400API/Auth/TokenService.cs,/workspaces/AS400API/Configuration/JwtOptions.cs,/workspaces/AS400API/Configuration/OdbcOptions.cs,/workspaces/AS400API/Endpoints/As400Endpoints.cs,/workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs,/workspaces/AS400API/Endpoints/AuthEndpoints.cs,/workspaces/AS400API/Endpoints/SystemEndpoints.cs,/workspaces/AS400API/Infrastructure/DatabaseRowFormatter.cs,/workspaces/AS400API/Infrastructure/DataReaderExtensions.cs,/workspaces/AS400API/Program.cs,/workspaces/AS400API/obj/Debug/net9.0/AS400API.GlobalUsings.g.cs,\"/workspaces/AS400API/obj/Debug/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs\",/workspaces/AS400API/obj/Debug/net9.0/AS400API.AssemblyInfo.cs,/workspaces/AS400API/obj/Debug/net9.0/AS400API.MvcApplicationPartsAssemblyInfo.cs,/workspaces/AS400API/appsettings.json,/workspaces/AS400API/.dockerignore,/workspaces/AS400API/docker-compose.yml,/workspaces/AS400API/docker/odbc/odbc.ini,/workspaces/AS400API/docker/odbc/odbcinst.ini,/workspaces/AS400API/Dockerfile,/workspaces/AS400API/drivers/ibm-iaccess-1.1.0.28-1.0.x86_64.rpm,/workspaces/AS400API/Logs/as400-api-20251001.log,/workspaces/AS400API/Logs/as400-api-20251002.log,/workspaces/AS400API/Logs/as400-api-20251003.log,/workspaces/AS400API/Logs/as400-api-20251004.log,/workspaces/AS400API/README.md,/workspaces/AS400API/scripts/run-sonar.sh,/workspaces/AS400API/scripts/test-databases.sh,/workspaces/AS400API/obj/AS400API.csproj.nuget.dgspec.json,/workspaces/AS400API/obj/project.assets.json,/workspaces/AS400API/.devcontainer/Dockerfile,/workspaces/AS400API/.devcontainer/devcontainer.json,/workspaces/AS400API/.vscode/launch.json,/workspaces/AS400API/.vscode/tasks.json,/workspaces/AS400API/obj/Debug/net9.0/staticwebassets.build.json,/workspaces/AS400API/obj/Debug/net9.0/staticwebassets.build.endpoints.json,/workspaces/AS400API/obj/Debug/net9.0/rjsmrazor.dswa.cache.json,/workspaces/AS400API/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json,/workspaces/AS400API/obj/Debug/net9.0/rpswa.dswa.cache.json,/workspaces/AS400API/bin/Debug/net9.0/AS400API.deps.json,/workspaces/AS400API/bin/Debug/net9.0/appsettings.json,/workspaces/AS400API/bin/Debug/net9.0/AS400API.runtimeconfig.json,/workspaces/AS400API/bin/Debug/net9.0/AS400API.staticwebassets.endpoints.json"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.cs.analyzer.projectOutPaths",
+ "value": "/workspaces/AS400API/.sonarqube/out/0"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.cs.roslyn.reportFilePaths",
+ "value": "/workspaces/AS400API/.sonarqube/out/0/Issues.json"
+ },
+ {
+ "key": "40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.cs.scanner.telemetry",
+ "value": "/workspaces/AS400API/.sonarqube/out/0/Telemetry.json"
+ },
+ {
+ "key": "sonar.login",
+ "value": "***"
+ },
+ {
+ "key": "sonar.host.url",
+ "value": "http://host.docker.internal:9000"
+ },
+ {
+ "key": "sonar.cs.opencover.reportsPaths",
+ "value": "**/coverage.opencover.xml"
+ },
+ {
+ "key": "sonar.exclusions",
+ "value": "**/bin/**,**/obj/**,**/coverage.opencover.xml"
+ },
+ {
+ "key": "sonar.visualstudio.enable",
+ "value": "false"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.sonarqube/out/Telemetry.S4NET.json b/.sonarqube/out/Telemetry.S4NET.json
new file mode 100644
index 0000000..a86cab7
--- /dev/null
+++ b/.sonarqube/out/Telemetry.S4NET.json
@@ -0,0 +1,7 @@
+{"dotnetenterprise.s4net.params.sonar_cs_opencover_reportspaths.source":"CLI"}
+{"dotnetenterprise.s4net.params.sonar_exclusions.source":"CLI"}
+{"dotnetenterprise.s4net.serverInfo.product":"SQ_Server"}
+{"dotnetenterprise.s4net.serverInfo.serverUrl":"custom_url"}
+{"dotnetenterprise.s4net.serverInfo.version":"9.9.8.100196"}
+{"dotnetenterprise.s4net.jre.bootstrapping":"UnsupportedByServer"}
+{"dotnetenterprise.s4net.scannerEngine.bootstrapping":"Unsupported"}
diff --git a/.sonarqube/out/Telemetry.Targets.S4NET.json b/.sonarqube/out/Telemetry.Targets.S4NET.json
new file mode 100644
index 0000000..a9a50df
--- /dev/null
+++ b/.sonarqube/out/Telemetry.Targets.S4NET.json
@@ -0,0 +1,2 @@
+{"dotnetenterprise.s4net.build.visual_studio_version":"17.0"}
+{"dotnetenterprise.s4net.build.msbuild_version":"17.14.21"}
diff --git a/.sonarqube/out/sonar-project.properties b/.sonarqube/out/sonar-project.properties
new file mode 100644
index 0000000..313a873
--- /dev/null
+++ b/.sonarqube/out/sonar-project.properties
@@ -0,0 +1,78 @@
+sonar.projectKey=as400api
+sonar.projectName=AS400API
+sonar.working.directory=/workspaces/AS400API/.sonarqube/out/.sonar
+sonar.projectBaseDir=/workspaces/AS400API
+sonar.pullrequest.cache.basepath=/workspaces/AS400API
+
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.projectKey=as400api:40CB5B53-77B8-B1FD-458C-805350CB09E8
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.projectName=AS400API
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.projectBaseDir=/workspaces/AS400API
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.sourceEncoding=utf-8
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.tests=
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.sources=\
+"/workspaces/AS400API/Auth/AuthPolicies.cs",\
+"/workspaces/AS400API/Auth/DemoUser.cs",\
+"/workspaces/AS400API/Auth/DemoUserStore.cs",\
+"/workspaces/AS400API/Auth/LoginRequest.cs",\
+"/workspaces/AS400API/Auth/LoginResponse.cs",\
+"/workspaces/AS400API/Auth/PasswordHasher.cs",\
+"/workspaces/AS400API/Auth/Roles.cs",\
+"/workspaces/AS400API/Auth/TokenService.cs",\
+"/workspaces/AS400API/Configuration/JwtOptions.cs",\
+"/workspaces/AS400API/Configuration/OdbcOptions.cs",\
+"/workspaces/AS400API/Endpoints/As400Endpoints.cs",\
+"/workspaces/AS400API/Endpoints/AS400_CP3FPRD/ORDUAG.cs",\
+"/workspaces/AS400API/Endpoints/AuthEndpoints.cs",\
+"/workspaces/AS400API/Endpoints/SystemEndpoints.cs",\
+"/workspaces/AS400API/Infrastructure/DatabaseRowFormatter.cs",\
+"/workspaces/AS400API/Infrastructure/DataReaderExtensions.cs",\
+"/workspaces/AS400API/Program.cs",\
+"/workspaces/AS400API/obj/Debug/net9.0/AS400API.GlobalUsings.g.cs",\
+"/workspaces/AS400API/obj/Debug/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs",\
+"/workspaces/AS400API/obj/Debug/net9.0/AS400API.AssemblyInfo.cs",\
+"/workspaces/AS400API/obj/Debug/net9.0/AS400API.MvcApplicationPartsAssemblyInfo.cs",\
+"/workspaces/AS400API/appsettings.json",\
+"/workspaces/AS400API/.dockerignore",\
+"/workspaces/AS400API/docker-compose.yml",\
+"/workspaces/AS400API/docker/odbc/odbc.ini",\
+"/workspaces/AS400API/docker/odbc/odbcinst.ini",\
+"/workspaces/AS400API/Dockerfile",\
+"/workspaces/AS400API/drivers/ibm-iaccess-1.1.0.28-1.0.x86_64.rpm",\
+"/workspaces/AS400API/Logs/as400-api-20251001.log",\
+"/workspaces/AS400API/Logs/as400-api-20251002.log",\
+"/workspaces/AS400API/Logs/as400-api-20251003.log",\
+"/workspaces/AS400API/Logs/as400-api-20251004.log",\
+"/workspaces/AS400API/README.md",\
+"/workspaces/AS400API/scripts/run-sonar.sh",\
+"/workspaces/AS400API/scripts/test-databases.sh",\
+"/workspaces/AS400API/obj/AS400API.csproj.nuget.dgspec.json",\
+"/workspaces/AS400API/obj/project.assets.json",\
+"/workspaces/AS400API/.devcontainer/Dockerfile",\
+"/workspaces/AS400API/.devcontainer/devcontainer.json",\
+"/workspaces/AS400API/.vscode/launch.json",\
+"/workspaces/AS400API/.vscode/tasks.json",\
+"/workspaces/AS400API/obj/Debug/net9.0/staticwebassets.build.json",\
+"/workspaces/AS400API/obj/Debug/net9.0/staticwebassets.build.endpoints.json",\
+"/workspaces/AS400API/obj/Debug/net9.0/rjsmrazor.dswa.cache.json",\
+"/workspaces/AS400API/obj/Debug/net9.0/rjsmcshtml.dswa.cache.json",\
+"/workspaces/AS400API/obj/Debug/net9.0/rpswa.dswa.cache.json",\
+"/workspaces/AS400API/bin/Debug/net9.0/AS400API.deps.json",\
+"/workspaces/AS400API/bin/Debug/net9.0/appsettings.json",\
+"/workspaces/AS400API/bin/Debug/net9.0/AS400API.runtimeconfig.json",\
+"/workspaces/AS400API/bin/Debug/net9.0/AS400API.staticwebassets.endpoints.json"
+
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.cs.analyzer.projectOutPaths=\
+"/workspaces/AS400API/.sonarqube/out/0"
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.cs.roslyn.reportFilePaths=\
+"/workspaces/AS400API/.sonarqube/out/0/Issues.json"
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.cs.scanner.telemetry=\
+"/workspaces/AS400API/.sonarqube/out/0/Telemetry.json"
+
+40CB5B53-77B8-B1FD-458C-805350CB09E8.sonar.working.directory=/workspaces/AS400API/.sonarqube/out/.sonar/mod0
+sonar.host.url=http://host.docker.internal:9000
+sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml
+sonar.exclusions=**/bin/**,**/obj/**,**/coverage.opencover.xml
+sonar.visualstudio.enable=false
+
+sonar.modules=40CB5B53-77B8-B1FD-458C-805350CB09E8
+
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..f3b3ed8
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,31 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+
+ {
+
+ "name": "AS400API: Launch",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ "program": "dotnet",
+ "args": [ "run", "--project", "${workspaceFolder}/AS400API.csproj" ],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ "serverReadyAction": {
+ "action": "openExternally",
+ "pattern": "\\bNow listening on:\\s+(https?://[^\\s]+)"
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "DOTNET_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://0.0.0.0:5080"
+ }
+ },
+ {
+ "name": "AS400API: Attach",
+ "type": "coreclr",
+ "request": "attach"
+ }
+ ]
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..7e220b6
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,32 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/AS400API.csproj"
+ ],
+ "problemMatcher": "\u0024msCompile",
+ "group": "build",
+ "presentation": {
+ "reveal": "silent"
+ }
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "--project",
+ "${workspaceFolder}/AS400API.csproj"
+ ],
+ "isBackground": true,
+ "problemMatcher": "\u0024msCompile"
+ }
+ ]
+}
diff --git a/AS400API.csproj b/AS400API.csproj
new file mode 100644
index 0000000..96f19c1
--- /dev/null
+++ b/AS400API.csproj
@@ -0,0 +1,17 @@
+
+
+ net9.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AS400API.sln b/AS400API.sln
new file mode 100644
index 0000000..53f84e0
--- /dev/null
+++ b/AS400API.sln
@@ -0,0 +1,24 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.2.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AS400API", "AS400API.csproj", "{40CB5B53-77B8-B1FD-458C-805350CB09E8}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {40CB5B53-77B8-B1FD-458C-805350CB09E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {40CB5B53-77B8-B1FD-458C-805350CB09E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {40CB5B53-77B8-B1FD-458C-805350CB09E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {40CB5B53-77B8-B1FD-458C-805350CB09E8}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {766AFEF9-A58F-4ED4-87F7-701B37E1F580}
+ EndGlobalSection
+EndGlobal
diff --git a/Auth/AuthPolicies.cs b/Auth/AuthPolicies.cs
new file mode 100644
index 0000000..f915af5
--- /dev/null
+++ b/Auth/AuthPolicies.cs
@@ -0,0 +1,7 @@
+namespace AS400API.Auth;
+
+public static class AuthPolicies
+{
+ public const string RequireOperator = "RequireOperator";
+ public const string RequireAdmin = "RequireAdmin";
+}
diff --git a/Auth/DemoUser.cs b/Auth/DemoUser.cs
new file mode 100644
index 0000000..013d9b5
--- /dev/null
+++ b/Auth/DemoUser.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+
+namespace AS400API.Auth;
+
+public sealed class DemoUser
+{
+ public DemoUser(string username, string passwordHash, string passwordSalt, IReadOnlyCollection roles)
+ {
+ Username = username;
+ PasswordHash = passwordHash;
+ PasswordSalt = passwordSalt;
+ Roles = roles;
+ }
+
+ public string Username { get; }
+ public string PasswordHash { get; }
+ public string PasswordSalt { get; }
+ public IReadOnlyCollection Roles { get; }
+}
diff --git a/Auth/DemoUserStore.cs b/Auth/DemoUserStore.cs
new file mode 100644
index 0000000..118be2b
--- /dev/null
+++ b/Auth/DemoUserStore.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace AS400API.Auth;
+
+public sealed class DemoUserStore
+{
+ private readonly Dictionary _users;
+
+ public DemoUserStore()
+ {
+ _users = new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ ["admin"] = CreateUser("admin", "Pass@123", new[] { Roles.Admin, Roles.Operator }),
+ ["operator"] = CreateUser("operator", "Pass@123", new[] { Roles.Operator })
+ };
+ }
+
+ public ValueTask FindByNameAsync(string username)
+ {
+ _users.TryGetValue(username, out var user);
+ return ValueTask.FromResult(user);
+ }
+
+ public bool ValidateCredentials(DemoUser user, string password) =>
+ PasswordHasher.Verify(password, user.PasswordHash, user.PasswordSalt);
+
+ private static DemoUser CreateUser(string username, string password, IReadOnlyCollection roles)
+ {
+ var (hash, salt) = PasswordHasher.HashPassword(password);
+ return new DemoUser(username, hash, salt, roles);
+ }
+}
diff --git a/Auth/LoginRequest.cs b/Auth/LoginRequest.cs
new file mode 100644
index 0000000..07cb4cb
--- /dev/null
+++ b/Auth/LoginRequest.cs
@@ -0,0 +1,3 @@
+namespace AS400API.Auth;
+
+public sealed record LoginRequest(string Username, string Password);
diff --git a/Auth/LoginResponse.cs b/Auth/LoginResponse.cs
new file mode 100644
index 0000000..30fe262
--- /dev/null
+++ b/Auth/LoginResponse.cs
@@ -0,0 +1,5 @@
+using System.Collections.Generic;
+
+namespace AS400API.Auth;
+
+public sealed record LoginResponse(string AccessToken, int ExpiresIn, string TokenType, IReadOnlyCollection Roles);
diff --git a/Auth/PasswordHasher.cs b/Auth/PasswordHasher.cs
new file mode 100644
index 0000000..708d372
--- /dev/null
+++ b/Auth/PasswordHasher.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Security.Cryptography;
+using Microsoft.AspNetCore.Cryptography.KeyDerivation;
+
+namespace AS400API.Auth;
+
+public static class PasswordHasher
+{
+ private const int SaltSize = 16;
+ private const int KeySize = 32;
+ private const int Iterations = 100_000;
+
+ public static (string Hash, string Salt) HashPassword(string password)
+ {
+ var salt = RandomNumberGenerator.GetBytes(SaltSize);
+ var hashBytes = KeyDerivation.Pbkdf2(password, salt, KeyDerivationPrf.HMACSHA256, Iterations, KeySize);
+ return (Convert.ToBase64String(hashBytes), Convert.ToBase64String(salt));
+ }
+
+ public static bool Verify(string password, string storedHash, string storedSalt)
+ {
+ var saltBytes = Convert.FromBase64String(storedSalt);
+ var computedBytes = KeyDerivation.Pbkdf2(password, saltBytes, KeyDerivationPrf.HMACSHA256, Iterations, KeySize);
+ var storedBytes = Convert.FromBase64String(storedHash);
+ return CryptographicOperations.FixedTimeEquals(storedBytes, computedBytes);
+ }
+}
diff --git a/Auth/Roles.cs b/Auth/Roles.cs
new file mode 100644
index 0000000..f70e9d2
--- /dev/null
+++ b/Auth/Roles.cs
@@ -0,0 +1,7 @@
+namespace AS400API.Auth;
+
+public static class Roles
+{
+ public const string Admin = "Admin";
+ public const string Operator = "Operator";
+}
diff --git a/Auth/TokenService.cs b/Auth/TokenService.cs
new file mode 100644
index 0000000..ad4074a
--- /dev/null
+++ b/Auth/TokenService.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Text;
+using AS400API.Configuration;
+using Microsoft.IdentityModel.Tokens;
+
+namespace AS400API.Auth;
+
+public sealed class TokenService
+{
+ private readonly JwtOptions _options;
+ private readonly JwtSecurityTokenHandler _tokenHandler = new();
+
+ public TokenService(JwtOptions options)
+ {
+ _options = options;
+ }
+
+ public string CreateToken(DemoUser user)
+ {
+ var signingCredentials = new SigningCredentials(
+ new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Key)),
+ SecurityAlgorithms.HmacSha256);
+
+ var claims = new List
+ {
+ new(JwtRegisteredClaimNames.Sub, user.Username),
+ new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
+ new(ClaimTypes.Name, user.Username)
+ };
+
+ foreach (var role in user.Roles)
+ {
+ claims.Add(new Claim(ClaimTypes.Role, role));
+ }
+
+ var token = new JwtSecurityToken(
+ issuer: _options.Issuer,
+ audience: _options.Audience,
+ claims: claims,
+ expires: DateTime.UtcNow.AddMinutes(_options.AccessTokenLifetimeMinutes),
+ signingCredentials: signingCredentials);
+
+ return _tokenHandler.WriteToken(token);
+ }
+}
diff --git a/Configuration/JwtOptions.cs b/Configuration/JwtOptions.cs
new file mode 100644
index 0000000..7cf9e9b
--- /dev/null
+++ b/Configuration/JwtOptions.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Text;
+
+namespace AS400API.Configuration;
+
+public sealed class JwtOptions
+{
+ public const string SectionName = "Jwt";
+
+ public string Issuer { get; set; } = "AS400API";
+ public string Audience { get; set; } = "AS400API.Clients";
+ public string Key { get; set; } = "change-me-to-a-long-random-secret";
+ public int AccessTokenLifetimeMinutes { get; set; } = 60;
+
+ public void EnsureIsValid()
+ {
+ if (string.IsNullOrWhiteSpace(Key) || Encoding.UTF8.GetByteCount(Key) < 32)
+ {
+ throw new InvalidOperationException("Jwt:Key must be at least 32 UTF-8 bytes. Set a strong secret in configuration");
+ }
+
+ if (AccessTokenLifetimeMinutes <= 0)
+ {
+ throw new InvalidOperationException("Jwt:AccessTokenLifetimeMinutes must be a positive integer");
+ }
+ }
+}
diff --git a/Configuration/OdbcOptions.cs b/Configuration/OdbcOptions.cs
new file mode 100644
index 0000000..4c7b9c7
--- /dev/null
+++ b/Configuration/OdbcOptions.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+
+namespace AS400API.Configuration;
+
+public sealed class OdbcOptions
+{
+ public string? System { get; set; }
+ public string? DefaultLibraries { get; set; }
+ public string? User { get; set; }
+ public string? Password { get; set; }
+ ///
+ /// 0 = SQL naming (*SYS = library/file); 1 = System naming (*SYS). Keep 1 by default.
+ ///
+ public string? Naming { get; set; }
+ public string? Translate { get; set; } = "1";
+ public string? ClientLocale { get; set; } = "en_US";
+ public bool Pooling { get; set; } = true;
+
+ public string ToConnectionString()
+ {
+ // Driver name must match installed name in odbcinst.ini
+ var driverName = Environment.GetEnvironmentVariable("AS400_DRIVER_NAME") ?? "IBM i Access ODBC Driver";
+ var parts = new List
+ {
+ $"Driver={{{driverName}}}",
+ $"System={System}"
+ };
+ if (!string.IsNullOrWhiteSpace(DefaultLibraries)) parts.Add($"DefaultLibraries={DefaultLibraries}");
+ if (!string.IsNullOrWhiteSpace(User)) parts.Add($"Uid={User}");
+ if (!string.IsNullOrWhiteSpace(Password)) parts.Add($"Pwd={Password}");
+ if (!string.IsNullOrWhiteSpace(Naming)) parts.Add($"Naming={Naming}");
+ if (!string.IsNullOrWhiteSpace(Translate)) parts.Add($"TRANSLATE={Translate}");
+ if (!string.IsNullOrWhiteSpace(ClientLocale)) parts.Add($"Client_Locale={ClientLocale}");
+ if (Pooling) parts.Add("Pooling=true");
+ return string.Join(";", parts);
+ }
+}
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..b8b18cc
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,70 @@
+# ---------- build stage ----------
+FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/sdk:9.0 AS build
+WORKDIR /src
+
+# Install tools needed to install IBM i Access ODBC (alien to convert RPM, unixODBC dev for headers)
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ alien curl ca-certificates unzip \
+ unixodbc unixodbc-dev \
+ && rm -rf /var/lib/apt/lists/*
+
+# Ensure directories exist even if the IBM driver is absent, so later COPY succeeds
+RUN mkdir -p /opt/ibm /usr/lib /usr/lib64 /tmp/odbc-libs/lib /tmp/odbc-libs/lib64
+
+# Copy IBM i Access ODBC RPM from build context (put your rpm under ./drivers)
+# Example filename: ibm-iaccess-1.1.0.29-1.0.x86_64.rpm.disabled (we drop the .disabled suffix below)
+COPY drivers/ /tmp/drivers/
+RUN set -ex; \
+ if ls /tmp/drivers/ibm-iaccess-*.rpm.disabled >/dev/null 2>&1; then \
+ for f in /tmp/drivers/ibm-iaccess-*.rpm.disabled; do mv "$f" "${f%.disabled}"; done; \
+ fi
+RUN set -ex; \
+ if ls /tmp/drivers/ibm-iaccess-*.rpm >/dev/null 2>&1; then \
+ for f in /tmp/drivers/ibm-iaccess-*.rpm; do alien -i --scripts "$f"; done; \
+ else echo ">> IBM i Access ODBC RPM not found in /tmp/drivers. Build will continue, but ODBC will not work until driver is present." ; fi
+
+# Collect IBM driver shared libraries so they can be copied without optional globs later
+RUN set -ex; \
+ if ls /opt/ibm/iaccess/lib64/libcwb*.so* >/dev/null 2>&1; then cp /opt/ibm/iaccess/lib64/libcwb*.so* /tmp/odbc-libs/lib64/; fi; \
+ if ls /opt/ibm/iaccess/lib/libcwb*.so* >/dev/null 2>&1; then cp /opt/ibm/iaccess/lib/libcwb*.so* /tmp/odbc-libs/lib/; fi
+
+# Register ODBC driver (odbcinst.ini)
+COPY docker/odbc/odbcinst.ini /etc/odbcinst.ini
+# Optional DSN (odbc.ini) if you prefer DSN connections
+COPY docker/odbc/odbc.ini /etc/odbc.ini
+
+# copy project and restore/build
+COPY AS400API.csproj ./
+RUN dotnet restore AS400API.csproj
+COPY . .
+RUN dotnet publish AS400API.csproj -c Release -o /app/publish /p:UseAppHost=false
+
+# ---------- runtime stage ----------
+FROM --platform=linux/amd64 mcr.microsoft.com/dotnet/aspnet:9.0 AS final
+WORKDIR /app
+
+# Install unixODBC and copy IBM i Access ODBC libs from build stage
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ unixodbc \
+ && rm -rf /var/lib/apt/lists/*
+
+# Copy IBM i driver files installed in build stage (if present)
+# Common path after installing ibm-iaccess via alien:
+COPY --from=build /opt/ibm /opt/ibm
+COPY --from=build /tmp/odbc-libs/lib64/ /usr/lib64/
+COPY --from=build /tmp/odbc-libs/lib/ /usr/lib/
+
+# ODBC config
+COPY docker/odbc/odbcinst.ini /etc/odbcinst.ini
+COPY docker/odbc/odbc.ini /etc/odbc.ini
+
+# App
+COPY --from=build /app/publish .
+
+# Default envs (override in compose)
+ENV ASPNETCORE_URLS=http://+:8080
+ENV AS400_DRIVER_NAME="IBM i Access ODBC Driver"
+ENV LD_LIBRARY_PATH=/opt/ibm/iaccess/lib64:/opt/ibm/iaccess/lib
+
+EXPOSE 8080
+ENTRYPOINT ["dotnet", "AS400API.dll"]
diff --git a/Endpoints/AS400_CP3FPRD/ORDUAG.cs b/Endpoints/AS400_CP3FPRD/ORDUAG.cs
new file mode 100644
index 0000000..caedd57
--- /dev/null
+++ b/Endpoints/AS400_CP3FPRD/ORDUAG.cs
@@ -0,0 +1,278 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Odbc;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using AS400API.Infrastructure;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Routing;
+
+namespace AS400API.Endpoints;
+
+public static class ORDUAGEndpoint
+{
+ private static readonly string[] ColumnNames =
+ [
+ "AG_CODE_COMPANY",
+ "AG_CODE_CURRENCY",
+ "AG_CODE_NUMBER",
+ "AGENT_AGENCY",
+ "AGENT_HOME_NAAD",
+ "AGENT_MAIL_NAAD",
+ "AGENT_PHONE",
+ "AGENT_STAT",
+ "AGENT_START_DATE",
+ "AGENT_DATE_TERM",
+ "AGENT_FILLER_01",
+ "AGENT_QUALITY_BONUS",
+ "AGENT_MARKET_BONUS",
+ "AGENT_NON_HOUSED_COMP",
+ "AGENT_OTHER_COMP",
+ "AGENT_FINANCE",
+ "AGENT_FILLER_02",
+ "AGENT_PAY_NOPAY",
+ "AGENT_HOLD_BACK",
+ "AGENT_DEVELOPMENT_AMT",
+ "AGENT_NON_MED_PRIV",
+ "AGENT_FRINGE_BENEFIT",
+ "AGENT_FILLER_03",
+ "AGENT_REGION",
+ "AGENT_SPONSOR_LIC",
+ "AGENT_PRIM_LIC_NO",
+ "AGENT_QUALIFICATION",
+ "AGENT_PREV_ID",
+ "AGENT_FILLER_05",
+ "AGENT_FYR_COMM_CURMTH",
+ "AGENT_FYR_COMM_YTD",
+ "AGENT_RENEWAL_COMM_CURMTH",
+ "AGENT_RENEWAL_COMM_YTD",
+ "AGENT_SERVFEE_CURMTH",
+ "AGENT_SERVFEE_YTD",
+ "AGENT_SINGPRM_CURMTH",
+ "AGENT_SINGPRM_YTD",
+ "AGENT_OVERRIDE_CURMTH",
+ "AGENT_OVERRIDE_YTD",
+ "AGENT_PAY_DED_CURMTH",
+ "AGENT_PAY_DED_YTD",
+ "AGENT_PAYABLE_BALANCE_YTD",
+ "AGENT_NET_FYR_COMM1",
+ "AGENT_NET_FYR_COMM2",
+ "AGENT_NET_FYR_COMM3",
+ "AGENT_NET_FYR_COMM4",
+ "AGENT_NET_FYR_COMM5",
+ "AGENT_NET_FYR_COMM6",
+ "AGENT_NET_FYR_COMM7",
+ "AGENT_NET_FYR_COMM8",
+ "AGENT_NET_FYR_COMM9",
+ "AGENT_NET_FYR_COMM10",
+ "AGENT_NET_FYR_COMM11",
+ "AGENT_NET_FYR_COMM12",
+ "AGENT_ASGNMT_MADE_AMT",
+ "AGENT_ASGNMT_REC_AMT",
+ "AGENT_NET_FYR_LIFE_COMM1",
+ "AGENT_NET_FYR_LIFE_COMM2",
+ "AGENT_NET_FYR_LIFE_COMM3",
+ "AGENT_NET_FYR_LIFE_COMM4",
+ "AGENT_NET_FYR_LIFE_COMM5",
+ "AGENT_NET_FYR_LIFE_COMM6",
+ "AGENT_NET_FYR_LIFE_COMM7",
+ "AGENT_NET_FYR_LIFE_COMM8",
+ "AGENT_NET_FYR_LIFE_COMM9",
+ "AGENT_NET_FYR_LIFE_COMM10",
+ "AGENT_NET_FYR_LIFE_COMM11",
+ "AGENT_NET_FYR_LIFE_COMM12",
+ "AGENT_FYC_SINGPRM_CURMTH",
+ "AGENT_FYC_SINGPRM_YTD",
+ "AGENT_FYR_LIFE_COMM_CURMTH",
+ "AGENT_FYR_LIFE_COMM_YTD",
+ "AGENT_REN_LIFE_COMM_CURMTH",
+ "AGENT_REN_LIFE_COMM_YTD",
+ "AGENT_QUALITY_BON_CURMTH",
+ "AGENT_QUALITY_BON_YTD",
+ "AGENT_MARKET_BON_CURMTH",
+ "AGENT_MARKET_BON_YTD",
+ "AGENT_NON_HOUSED_CURMTH",
+ "AGENT_NON_HOUSED_YTD",
+ "AGENT_APP_CNT_LSTMTH",
+ "AGENT_APP_CNT_YTM",
+ "AGENT_TOT_PREM_WRIT_LSTMTH",
+ "AGENT_TOT_PREM_WRIT_YTM",
+ "AGENT_LIFE_PREM_WRIT_LSTMTH",
+ "AGENT_LIFE_PREM_WRIT_YTM",
+ "AGENT_TOT_FYP_CURMTH",
+ "AGENT_TOT_FYP_YTD",
+ "AGENT_TOT_REN_CURMTH",
+ "AGENT_TOT_REN_YTD",
+ "AGENT_PERSISTENCY1",
+ "AGENT_PERSISTENCY2",
+ "AGENT_PERSISTENCY3",
+ "AGENT_PERSISTENCY4",
+ "AGENT_PRODUCTION1",
+ "AGENT_PRODUCTION2",
+ "AGENT_PRODUCTION3",
+ "AGENT_PRODUCTION4",
+ "AGENT_MDRT1",
+ "AGENT_MDRT2",
+ "AGENT_MDRT3",
+ "AGENT_MDRT4",
+ "AGENT_CONVENTIONS1",
+ "AGENT_CONVENTIONS2",
+ "AGENT_CONVENTIONS3",
+ "AGENT_CONVENTIONS4",
+ "AGENT_APPTIVITY_AWARD1",
+ "AGENT_APPTIVITY_AWARD2",
+ "AGENT_APPTIVITY_AWARD3",
+ "AGENT_APPTIVITY_AWARD4",
+ "AGENT_NQA1",
+ "AGENT_NQA2",
+ "AGENT_NQA3",
+ "AGENT_NQA4",
+ "AG_BLACKLIST_RSN",
+ "AG_TAX_NUMBER",
+ "AG_DIST_CHANEL_CODE",
+ "AGENT_LIAISON_OFF",
+ "AGENT_FILLER_07",
+ "AG_LIC_FPO",
+ "AG_LIC_START_DATE",
+ "AG_LIC_END_DATE",
+ "AG_GUARANTOR_NO",
+ "AG_GUARANTOR_SW",
+ "AG_BANK_ACCOUNT",
+ "AG_PREV_TAX_RATE",
+ "AG_CURR_TAX_RATE",
+ "AG_PRESERVE_AGENT",
+ "AG_LIC_REQ_DATE",
+ "AG_BLACKLIST_IND",
+ "AGENT_FILLER_06"
+ ];
+
+ private static readonly Regex LibraryNamePattern = new("^[A-Z0-9_]+$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ public static RouteGroupBuilder MapORDUAGEndpoints(this RouteGroupBuilder group)
+ {
+ group.MapGet("/v1/orduag/query", async (
+ [AsParameters] OrduagQuery query,
+ OdbcConnection conn,
+ ILoggerFactory loggerFactory) =>
+ {
+ var logger = loggerFactory.CreateLogger("ORDUAGEndpoint");
+ try
+ {
+ await conn.OpenAsync();
+
+ var page = query.Page.GetValueOrDefault(1);
+ if (page < 1)
+ {
+ logger.LogWarning("Invalid page value {Page}; defaulting to 1", query.Page);
+ page = 1;
+ }
+
+ var pageSize = query.PageSize.GetValueOrDefault(50);
+ pageSize = Math.Clamp(pageSize, 1, 500);
+
+ var offsetLong = (long)(page - 1) * pageSize;
+ if (offsetLong > int.MaxValue)
+ {
+ return Results.BadRequest(new { error = "Requested page is too large." });
+ }
+
+ var offset = (int)offsetLong;
+
+ var sqlBuilder = new StringBuilder();
+ sqlBuilder.AppendLine("SELECT");
+ sqlBuilder.AppendLine(string.Join(",\n", ColumnNames.Select(column => $" {column}")));
+ sqlBuilder.AppendLine("FROM CP3FPRD.ORDUAG");
+ sqlBuilder.AppendLine("WHERE 1 = 1");
+
+ var parameterValues = new List