diff --git a/.NCrunch_eSuite/eSuite.crunchsolution.cache b/.NCrunch_eSuite/eSuite.crunchsolution.cache
new file mode 100644
index 0000000..ad7d9f6
Binary files /dev/null and b/.NCrunch_eSuite/eSuite.crunchsolution.cache differ
diff --git a/.NCrunch_eSuite/eSuite.executiontimes.cache b/.NCrunch_eSuite/eSuite.executiontimes.cache
new file mode 100644
index 0000000..4631fe4
Binary files /dev/null and b/.NCrunch_eSuite/eSuite.executiontimes.cache differ
diff --git a/.vs/ProjectEvaluation/esuite.metadata.v10.bin b/.vs/ProjectEvaluation/esuite.metadata.v10.bin
new file mode 100644
index 0000000..600da39
Binary files /dev/null and b/.vs/ProjectEvaluation/esuite.metadata.v10.bin differ
diff --git a/.vs/ProjectEvaluation/esuite.projects.v10.bin b/.vs/ProjectEvaluation/esuite.projects.v10.bin
new file mode 100644
index 0000000..a58c798
Binary files /dev/null and b/.vs/ProjectEvaluation/esuite.projects.v10.bin differ
diff --git a/.vs/ProjectEvaluation/esuite.strings.v10.bin b/.vs/ProjectEvaluation/esuite.strings.v10.bin
new file mode 100644
index 0000000..0d28237
Binary files /dev/null and b/.vs/ProjectEvaluation/esuite.strings.v10.bin differ
diff --git a/.vs/eSuite/CopilotIndices/18.0.988.22099/CodeChunks.db b/.vs/eSuite/CopilotIndices/18.0.988.22099/CodeChunks.db
new file mode 100644
index 0000000..6708eb4
Binary files /dev/null and b/.vs/eSuite/CopilotIndices/18.0.988.22099/CodeChunks.db differ
diff --git a/.vs/eSuite/CopilotIndices/18.0.988.22099/SemanticSymbols.db b/.vs/eSuite/CopilotIndices/18.0.988.22099/SemanticSymbols.db
new file mode 100644
index 0000000..90a7159
Binary files /dev/null and b/.vs/eSuite/CopilotIndices/18.0.988.22099/SemanticSymbols.db differ
diff --git a/.vs/eSuite/DesignTimeBuild/.dtbcache.v2 b/.vs/eSuite/DesignTimeBuild/.dtbcache.v2
new file mode 100644
index 0000000..4683094
Binary files /dev/null and b/.vs/eSuite/DesignTimeBuild/.dtbcache.v2 differ
diff --git a/.vs/eSuite/FileContentIndex/084c3388-9459-4b64-b472-d52df81fefbb.vsidx b/.vs/eSuite/FileContentIndex/084c3388-9459-4b64-b472-d52df81fefbb.vsidx
new file mode 100644
index 0000000..6881975
Binary files /dev/null and b/.vs/eSuite/FileContentIndex/084c3388-9459-4b64-b472-d52df81fefbb.vsidx differ
diff --git a/.vs/eSuite/FileContentIndex/7b28d0da-76fd-4119-b37b-9863283bedf3.vsidx b/.vs/eSuite/FileContentIndex/7b28d0da-76fd-4119-b37b-9863283bedf3.vsidx
new file mode 100644
index 0000000..5847147
Binary files /dev/null and b/.vs/eSuite/FileContentIndex/7b28d0da-76fd-4119-b37b-9863283bedf3.vsidx differ
diff --git a/.vs/eSuite/FileContentIndex/ba823400-7c98-442e-8024-593cd4d5fef1.vsidx b/.vs/eSuite/FileContentIndex/ba823400-7c98-442e-8024-593cd4d5fef1.vsidx
new file mode 100644
index 0000000..a6a158a
Binary files /dev/null and b/.vs/eSuite/FileContentIndex/ba823400-7c98-442e-8024-593cd4d5fef1.vsidx differ
diff --git a/.vs/eSuite/FileContentIndex/da446847-cad0-4e8c-b3bf-115014925172.vsidx b/.vs/eSuite/FileContentIndex/da446847-cad0-4e8c-b3bf-115014925172.vsidx
new file mode 100644
index 0000000..8ad5d49
Binary files /dev/null and b/.vs/eSuite/FileContentIndex/da446847-cad0-4e8c-b3bf-115014925172.vsidx differ
diff --git a/.vs/eSuite/FileContentIndex/f30ef7d9-2362-40a9-862d-91e66ea33bec.vsidx b/.vs/eSuite/FileContentIndex/f30ef7d9-2362-40a9-862d-91e66ea33bec.vsidx
new file mode 100644
index 0000000..bb4395d
Binary files /dev/null and b/.vs/eSuite/FileContentIndex/f30ef7d9-2362-40a9-862d-91e66ea33bec.vsidx differ
diff --git a/.vs/eSuite/config/applicationhost.config b/.vs/eSuite/config/applicationhost.config
new file mode 100644
index 0000000..0d88f0d
--- /dev/null
+++ b/.vs/eSuite/config/applicationhost.config
@@ -0,0 +1,1016 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.vs/eSuite/v17/.futdcache.v2 b/.vs/eSuite/v17/.futdcache.v2
new file mode 100644
index 0000000..c986608
Binary files /dev/null and b/.vs/eSuite/v17/.futdcache.v2 differ
diff --git a/.vs/eSuite/v17/.suo b/.vs/eSuite/v17/.suo
new file mode 100644
index 0000000..0194f98
Binary files /dev/null and b/.vs/eSuite/v17/.suo differ
diff --git a/.vs/eSuite/v17/DocumentLayout.backup.json b/.vs/eSuite/v17/DocumentLayout.backup.json
new file mode 100644
index 0000000..af2d292
--- /dev/null
+++ b/.vs/eSuite/v17/DocumentLayout.backup.json
@@ -0,0 +1,37 @@
+{
+ "Version": 1,
+ "WorkspaceRootPath": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\",
+ "Documents": [
+ {
+ "AbsoluteMoniker": "D:0:0:{4A704FA7-4E3A-4CFA-B043-434A0C49AF89}|e-suite.API\\eSuite.API\\eSuite.API.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.api\\esuite.api\\dependencyinjection\\coreregistrationmodule.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{4A704FA7-4E3A-4CFA-B043-434A0C49AF89}|e-suite.API\\eSuite.API\\eSuite.API.csproj|solutionrelative:e-suite.api\\esuite.api\\dependencyinjection\\coreregistrationmodule.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ }
+ ],
+ "DocumentGroupContainers": [
+ {
+ "Orientation": 1,
+ "VerticalTabListWidth": 256,
+ "DocumentGroups": [
+ {
+ "DockedHeight": 200,
+ "SelectedChildIndex": 0,
+ "Children": [
+ {
+ "$type": "Document",
+ "DocumentIndex": 0,
+ "Title": "CoreRegistrationModule.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API\\eSuite.API\\DependencyInjection\\CoreRegistrationModule.cs",
+ "RelativeDocumentMoniker": "e-suite.API\\eSuite.API\\DependencyInjection\\CoreRegistrationModule.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API\\eSuite.API\\DependencyInjection\\CoreRegistrationModule.cs",
+ "RelativeToolTip": "e-suite.API\\eSuite.API\\DependencyInjection\\CoreRegistrationModule.cs",
+ "ViewState": "AgIAABIAAAAAAAAAAAAAwCMAAABIAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2024-12-26T13:06:45.739Z",
+ "EditorCaption": ""
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vs/eSuite/v17/DocumentLayout.json b/.vs/eSuite/v17/DocumentLayout.json
new file mode 100644
index 0000000..af2d292
--- /dev/null
+++ b/.vs/eSuite/v17/DocumentLayout.json
@@ -0,0 +1,37 @@
+{
+ "Version": 1,
+ "WorkspaceRootPath": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\",
+ "Documents": [
+ {
+ "AbsoluteMoniker": "D:0:0:{4A704FA7-4E3A-4CFA-B043-434A0C49AF89}|e-suite.API\\eSuite.API\\eSuite.API.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.api\\esuite.api\\dependencyinjection\\coreregistrationmodule.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{4A704FA7-4E3A-4CFA-B043-434A0C49AF89}|e-suite.API\\eSuite.API\\eSuite.API.csproj|solutionrelative:e-suite.api\\esuite.api\\dependencyinjection\\coreregistrationmodule.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ }
+ ],
+ "DocumentGroupContainers": [
+ {
+ "Orientation": 1,
+ "VerticalTabListWidth": 256,
+ "DocumentGroups": [
+ {
+ "DockedHeight": 200,
+ "SelectedChildIndex": 0,
+ "Children": [
+ {
+ "$type": "Document",
+ "DocumentIndex": 0,
+ "Title": "CoreRegistrationModule.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API\\eSuite.API\\DependencyInjection\\CoreRegistrationModule.cs",
+ "RelativeDocumentMoniker": "e-suite.API\\eSuite.API\\DependencyInjection\\CoreRegistrationModule.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API\\eSuite.API\\DependencyInjection\\CoreRegistrationModule.cs",
+ "RelativeToolTip": "e-suite.API\\eSuite.API\\DependencyInjection\\CoreRegistrationModule.cs",
+ "ViewState": "AgIAABIAAAAAAAAAAAAAwCMAAABIAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2024-12-26T13:06:45.739Z",
+ "EditorCaption": ""
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vs/eSuite/v18/.futdcache.v2 b/.vs/eSuite/v18/.futdcache.v2
new file mode 100644
index 0000000..b88aa70
Binary files /dev/null and b/.vs/eSuite/v18/.futdcache.v2 differ
diff --git a/.vs/eSuite/v18/.suo b/.vs/eSuite/v18/.suo
new file mode 100644
index 0000000..6dc85ad
Binary files /dev/null and b/.vs/eSuite/v18/.suo differ
diff --git a/.vs/eSuite/v18/DocumentLayout.backup.json b/.vs/eSuite/v18/DocumentLayout.backup.json
new file mode 100644
index 0000000..e73205e
--- /dev/null
+++ b/.vs/eSuite/v18/DocumentLayout.backup.json
@@ -0,0 +1,215 @@
+{
+ "Version": 1,
+ "WorkspaceRootPath": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\",
+ "Documents": [
+ {
+ "AbsoluteMoniker": "D:0:0:{974467C0-14E3-D020-066A-74EDE1567927}|e-suite.API.Common\\e-suite.API.Common\\e-suite.API.Common.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.api.common\\e-suite.api.common\\models\\readperformancereportsummary.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{974467C0-14E3-D020-066A-74EDE1567927}|e-suite.API.Common\\e-suite.API.Common\\e-suite.API.Common.csproj|solutionrelative:e-suite.api.common\\e-suite.api.common\\models\\readperformancereportsummary.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{974467C0-14E3-D020-066A-74EDE1567927}|e-suite.API.Common\\e-suite.API.Common\\e-suite.API.Common.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.api.common\\e-suite.api.common\\models\\readperformancereport.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{974467C0-14E3-D020-066A-74EDE1567927}|e-suite.API.Common\\e-suite.API.Common\\e-suite.API.Common.csproj|solutionrelative:e-suite.api.common\\e-suite.api.common\\models\\readperformancereport.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{7DC1F493-76A5-3740-E774-C8DAA51ED83A}|e-suite.API.Common\\e-suite.API.Common.UnitTests\\e-suite.API.Common.UnitTests.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.api.common\\e-suite.api.common.unittests\\dtoautotester.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{7DC1F493-76A5-3740-E774-C8DAA51ED83A}|e-suite.API.Common\\e-suite.API.Common.UnitTests\\e-suite.API.Common.UnitTests.csproj|solutionrelative:e-suite.api.common\\e-suite.api.common.unittests\\dtoautotester.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{4A704FA7-4E3A-4CFA-B043-434A0C49AF89}|e-suite.API\\eSuite.API\\eSuite.API.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.api\\esuite.api\\swagger\\swaggerextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{4A704FA7-4E3A-4CFA-B043-434A0C49AF89}|e-suite.API\\eSuite.API\\eSuite.API.csproj|solutionrelative:e-suite.api\\esuite.api\\swagger\\swaggerextension.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{4A704FA7-4E3A-4CFA-B043-434A0C49AF89}|e-suite.API\\eSuite.API\\eSuite.API.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.api\\esuite.api\\swagger\\hasallowanonymousoperationsfilter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{4A704FA7-4E3A-4CFA-B043-434A0C49AF89}|e-suite.API\\eSuite.API\\eSuite.API.csproj|solutionrelative:e-suite.api\\esuite.api\\swagger\\hasallowanonymousoperationsfilter.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{CE69D868-8B81-B043-A340-E8F23683C0A2}|e-suite.Modules.SpecificationManager\\e-suite.Modules.SpecificationManager.UnitTests\\e-suite.Modules.SpecificationManager.UnitTests.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.modules.specificationmanager\\e-suite.modules.specificationmanager.unittests\\helpers\\fakeformrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{CE69D868-8B81-B043-A340-E8F23683C0A2}|e-suite.Modules.SpecificationManager\\e-suite.Modules.SpecificationManager.UnitTests\\e-suite.Modules.SpecificationManager.UnitTests.csproj|solutionrelative:e-suite.modules.specificationmanager\\e-suite.modules.specificationmanager.unittests\\helpers\\fakeformrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{B612A69B-3F3A-C33C-77F5-DE6A915E4E7D}|e-suite.Modules.OrganisationManager\\e-Suite.Modules.OrganisationManger.UnitTests\\e-suite.Modules.OrganisationsManager.UnitTests.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.modules.organisationmanager\\e-suite.modules.organisationmanger.unittests\\repository\\fakeorganisationmanagerrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{B612A69B-3F3A-C33C-77F5-DE6A915E4E7D}|e-suite.Modules.OrganisationManager\\e-Suite.Modules.OrganisationManger.UnitTests\\e-suite.Modules.OrganisationsManager.UnitTests.csproj|solutionrelative:e-suite.modules.organisationmanager\\e-suite.modules.organisationmanger.unittests\\repository\\fakeorganisationmanagerrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{5419EC24-755C-4D26-A6F1-90F478734EC3}|e-suite.MessageProcessor\\e-suite.MessageProcessor\\e-suite.MessageProcessor.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.messageprocessor\\e-suite.messageprocessor\\dependencyinjection\\coreregistrationmodule.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{5419EC24-755C-4D26-A6F1-90F478734EC3}|e-suite.MessageProcessor\\e-suite.MessageProcessor\\e-suite.MessageProcessor.csproj|solutionrelative:e-suite.messageprocessor\\e-suite.messageprocessor\\dependencyinjection\\coreregistrationmodule.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{F3A13EA7-AB5F-977E-3F71-82C5341119CD}|e-suite.Modules.FormsManager\\e_suite.Modules.Form.ManagerUnitTest\\e_suite.Modules.FormsManagerUnitTests.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.modules.formsmanager\\e_suite.modules.form.managerunittest\\fakeformsrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{F3A13EA7-AB5F-977E-3F71-82C5341119CD}|e-suite.Modules.FormsManager\\e_suite.Modules.Form.ManagerUnitTest\\e_suite.Modules.FormsManagerUnitTests.csproj|solutionrelative:e-suite.modules.formsmanager\\e_suite.modules.form.managerunittest\\fakeformsrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{73E50199-E462-B108-664A-1921E56D60A7}|e-suite.Modules.CustomFieldsManager\\e_suite.Modules.CusomFieldManagerUnitTest\\e_suite.Modules.CustomFieldManagerUnitTests.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.modules.customfieldsmanager\\e_suite.modules.cusomfieldmanagerunittest\\fakecustomfieldrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{73E50199-E462-B108-664A-1921E56D60A7}|e-suite.Modules.CustomFieldsManager\\e_suite.Modules.CusomFieldManagerUnitTest\\e_suite.Modules.CustomFieldManagerUnitTests.csproj|solutionrelative:e-suite.modules.customfieldsmanager\\e_suite.modules.cusomfieldmanagerunittest\\fakecustomfieldrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ },
+ {
+ "AbsoluteMoniker": "D:0:0:{A3A65D33-B1BA-4F89-4567-B45103987C1E}|e-suite.Service.EFlowSync\\e-suite.Service.EFlowSync.UnitTests\\e-suite.Service.EFlowSync.UnitTests.csproj|c:\\users\\me\\onedrive\\code\\sun\\e-suite\\e-suite.service.eflowsync\\e-suite.service.eflowsync.unittests\\fakerepositories\\fakeformsrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
+ "RelativeMoniker": "D:0:0:{A3A65D33-B1BA-4F89-4567-B45103987C1E}|e-suite.Service.EFlowSync\\e-suite.Service.EFlowSync.UnitTests\\e-suite.Service.EFlowSync.UnitTests.csproj|solutionrelative:e-suite.service.eflowsync\\e-suite.service.eflowsync.unittests\\fakerepositories\\fakeformsrepository.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
+ }
+ ],
+ "DocumentGroupContainers": [
+ {
+ "Orientation": 1,
+ "VerticalTabListWidth": 256,
+ "DocumentGroups": [
+ {
+ "DockedHeight": 200,
+ "SelectedChildIndex": 0,
+ "Children": [
+ {
+ "$type": "Document",
+ "DocumentIndex": 0,
+ "Title": "ReadPerformanceReportSummary.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API.Common\\e-suite.API.Common\\models\\ReadPerformanceReportSummary.cs",
+ "RelativeDocumentMoniker": "e-suite.API.Common\\e-suite.API.Common\\models\\ReadPerformanceReportSummary.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API.Common\\e-suite.API.Common\\models\\ReadPerformanceReportSummary.cs",
+ "RelativeToolTip": "e-suite.API.Common\\e-suite.API.Common\\models\\ReadPerformanceReportSummary.cs",
+ "ViewState": "AgIAAAQAAAAAAAAAAAAzwAgAAAAiAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:46:44.514Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 1,
+ "Title": "ReadPerformanceReport.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API.Common\\e-suite.API.Common\\models\\ReadPerformanceReport.cs",
+ "RelativeDocumentMoniker": "e-suite.API.Common\\e-suite.API.Common\\models\\ReadPerformanceReport.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API.Common\\e-suite.API.Common\\models\\ReadPerformanceReport.cs",
+ "RelativeToolTip": "e-suite.API.Common\\e-suite.API.Common\\models\\ReadPerformanceReport.cs",
+ "ViewState": "AgIAAAUAAAAAAAAAAAAAAAoAAAAhAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:44:45.87Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 2,
+ "Title": "DtoAutoTester.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API.Common\\e-suite.API.Common.UnitTests\\DtoAutoTester.cs",
+ "RelativeDocumentMoniker": "e-suite.API.Common\\e-suite.API.Common.UnitTests\\DtoAutoTester.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API.Common\\e-suite.API.Common.UnitTests\\DtoAutoTester.cs",
+ "RelativeToolTip": "e-suite.API.Common\\e-suite.API.Common.UnitTests\\DtoAutoTester.cs",
+ "ViewState": "AgIAAAcAAAAAAAAAAAA8wBMAAAAJAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:44:17.876Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Bookmark",
+ "Name": "ST:0:0:{6da21f7a-58db-45ce-8a78-22e8ff00cb95}"
+ },
+ {
+ "$type": "Bookmark",
+ "Name": "ST:0:0:{4e0252e6-905c-4466-b94e-c0b946fda83c}"
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 3,
+ "Title": "SwaggerExtension.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API\\eSuite.API\\Swagger\\SwaggerExtension.cs",
+ "RelativeDocumentMoniker": "e-suite.API\\eSuite.API\\Swagger\\SwaggerExtension.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API\\eSuite.API\\Swagger\\SwaggerExtension.cs",
+ "RelativeToolTip": "e-suite.API\\eSuite.API\\Swagger\\SwaggerExtension.cs",
+ "ViewState": "AgIAACUAAAAAAAAAAAAQwAEAAAAXAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:39:00.498Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 4,
+ "Title": "HasAllowAnonymousOperationsFilter.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API\\eSuite.API\\Swagger\\HasAllowAnonymousOperationsFilter.cs",
+ "RelativeDocumentMoniker": "e-suite.API\\eSuite.API\\Swagger\\HasAllowAnonymousOperationsFilter.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.API\\eSuite.API\\Swagger\\HasAllowAnonymousOperationsFilter.cs",
+ "RelativeToolTip": "e-suite.API\\eSuite.API\\Swagger\\HasAllowAnonymousOperationsFilter.cs",
+ "ViewState": "AgIAAAkAAAAAAAAAAAA7wBwAAAAJAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:38:02.181Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 5,
+ "Title": "FakeFormRepository.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Modules.SpecificationManager\\e-suite.Modules.SpecificationManager.UnitTests\\Helpers\\FakeFormRepository.cs",
+ "RelativeDocumentMoniker": "e-suite.Modules.SpecificationManager\\e-suite.Modules.SpecificationManager.UnitTests\\Helpers\\FakeFormRepository.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Modules.SpecificationManager\\e-suite.Modules.SpecificationManager.UnitTests\\Helpers\\FakeFormRepository.cs",
+ "RelativeToolTip": "e-suite.Modules.SpecificationManager\\e-suite.Modules.SpecificationManager.UnitTests\\Helpers\\FakeFormRepository.cs",
+ "ViewState": "AgIAAGEAAAAAAAAAAAAqwGwAAAAsAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:37:50.818Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 6,
+ "Title": "FakeOrganisationManagerRepository.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Modules.OrganisationManager\\e-Suite.Modules.OrganisationManger.UnitTests\\Repository\\FakeOrganisationManagerRepository.cs",
+ "RelativeDocumentMoniker": "e-suite.Modules.OrganisationManager\\e-Suite.Modules.OrganisationManger.UnitTests\\Repository\\FakeOrganisationManagerRepository.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Modules.OrganisationManager\\e-Suite.Modules.OrganisationManger.UnitTests\\Repository\\FakeOrganisationManagerRepository.cs",
+ "RelativeToolTip": "e-suite.Modules.OrganisationManager\\e-Suite.Modules.OrganisationManger.UnitTests\\Repository\\FakeOrganisationManagerRepository.cs",
+ "ViewState": "AgIAAA8AAAAAAAAAAAASwBgAAABFAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:37:42.769Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 7,
+ "Title": "CoreRegistrationModule.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.MessageProcessor\\e-suite.MessageProcessor\\DependencyInjection\\CoreRegistrationModule.cs",
+ "RelativeDocumentMoniker": "e-suite.MessageProcessor\\e-suite.MessageProcessor\\DependencyInjection\\CoreRegistrationModule.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.MessageProcessor\\e-suite.MessageProcessor\\DependencyInjection\\CoreRegistrationModule.cs",
+ "RelativeToolTip": "e-suite.MessageProcessor\\e-suite.MessageProcessor\\DependencyInjection\\CoreRegistrationModule.cs",
+ "ViewState": "AgIAACkAAAAAAAAAAAASwDIAAAATAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:36:59.391Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 8,
+ "Title": "FakeFormsRepository.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Modules.FormsManager\\e_suite.Modules.Form.ManagerUnitTest\\FakeFormsRepository.cs",
+ "RelativeDocumentMoniker": "e-suite.Modules.FormsManager\\e_suite.Modules.Form.ManagerUnitTest\\FakeFormsRepository.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Modules.FormsManager\\e_suite.Modules.Form.ManagerUnitTest\\FakeFormsRepository.cs",
+ "RelativeToolTip": "e-suite.Modules.FormsManager\\e_suite.Modules.Form.ManagerUnitTest\\FakeFormsRepository.cs",
+ "ViewState": "AgIAABMAAAAAAAAAAAASwBwAAABVAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:36:51.024Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 9,
+ "Title": "FakeCustomFieldRepository.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Modules.CustomFieldsManager\\e_suite.Modules.CusomFieldManagerUnitTest\\FakeCustomFieldRepository.cs",
+ "RelativeDocumentMoniker": "e-suite.Modules.CustomFieldsManager\\e_suite.Modules.CusomFieldManagerUnitTest\\FakeCustomFieldRepository.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Modules.CustomFieldsManager\\e_suite.Modules.CusomFieldManagerUnitTest\\FakeCustomFieldRepository.cs",
+ "RelativeToolTip": "e-suite.Modules.CustomFieldsManager\\e_suite.Modules.CusomFieldManagerUnitTest\\FakeCustomFieldRepository.cs",
+ "ViewState": "AgIAAA0AAAAAAAAAAIAzwBYAAAA8AAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:36:43.076Z",
+ "EditorCaption": ""
+ },
+ {
+ "$type": "Document",
+ "DocumentIndex": 10,
+ "Title": "FakeFormsRepository.cs",
+ "DocumentMoniker": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Service.EFlowSync\\e-suite.Service.EFlowSync.UnitTests\\FakeRepositories\\FakeFormsRepository.cs",
+ "RelativeDocumentMoniker": "e-suite.Service.EFlowSync\\e-suite.Service.EFlowSync.UnitTests\\FakeRepositories\\FakeFormsRepository.cs",
+ "ToolTip": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\e-suite.Service.EFlowSync\\e-suite.Service.EFlowSync.UnitTests\\FakeRepositories\\FakeFormsRepository.cs",
+ "RelativeToolTip": "e-suite.Service.EFlowSync\\e-suite.Service.EFlowSync.UnitTests\\FakeRepositories\\FakeFormsRepository.cs",
+ "ViewState": "AgIAABMAAAAAAAAAAIAxwBsAAABUAAAAAAAAAA==",
+ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
+ "WhenOpened": "2026-01-20T14:36:09.899Z",
+ "EditorCaption": ""
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vs/eSuite/v18/DocumentLayout.json b/.vs/eSuite/v18/DocumentLayout.json
new file mode 100644
index 0000000..ff6cba3
--- /dev/null
+++ b/.vs/eSuite/v18/DocumentLayout.json
@@ -0,0 +1,23 @@
+{
+ "Version": 1,
+ "WorkspaceRootPath": "C:\\Users\\me\\OneDrive\\Code\\Sun\\e-suite\\",
+ "Documents": [],
+ "DocumentGroupContainers": [
+ {
+ "Orientation": 1,
+ "VerticalTabListWidth": 256,
+ "DocumentGroups": [
+ {
+ "DockedHeight": 200,
+ "SelectedChildIndex": -1,
+ "Children": [
+ {
+ "$type": "Bookmark",
+ "Name": "ST:0:0:{4e0252e6-905c-4466-b94e-c0b946fda83c}"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vs/eSuite/v18/fileList.bin b/.vs/eSuite/v18/fileList.bin
new file mode 100644
index 0000000..d897598
Binary files /dev/null and b/.vs/eSuite/v18/fileList.bin differ
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..7a9dfa0
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "pwa-chrome",
+ "request": "launch",
+ "name": "Launch Chrome against localhost",
+ "url": "http://localhost:8080",
+ "webRoot": "${workspaceFolder}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Sigma server connects for Colin.txt b/Sigma server connects for Colin.txt
new file mode 100644
index 0000000..1aee988
--- /dev/null
+++ b/Sigma server connects for Colin.txt
@@ -0,0 +1,89 @@
+
+Sigma Airlock
+
+Database:
+
+airlock
+
+Internal IP:
+172.27.18.45
+
+
+External IP:
+87.224.57.13
+
+
+Username:
+e-suite.18.45
+
+Password:
+
+s2f$yuSbVAd;&rXNUeP'@Z
+
+******************************************************
+
+
+Sigma Printer checklist
+
+Database:
+
+external crom
+
+Internal IP:
+172.27.17.228
+
+External IP:
+
+87.224.57.32
+
+Username:
+e-suite.service.228
+
+Password:
+
+Y2:y`4Q#Su+cm/LK!Vv~8<
+
+******************************************************
+
+
+Sigma web live
+
+Database:
+si6ma
+
+Internal IP:
+172.27.18.22
+
+External IP:
+87.224.57.12
+
+
+Username:
+e-suite-18-22
+
+Password:
+J]v6mjL#Cf3^E;?4as5cQ
+
+
+******************************************************
+
+
+Webstore
+
+Database:
+ftpu
+
+Internal IP:
+172.27.18.63
+
+
+External IP:
+87.224.57.9
+
+
+Username:
+e-suite.18.63
+
+Password:
+
+f9X{,W%3E8$`VR"Y>=vLqM
\ No newline at end of file
diff --git a/e-suite.API.Common/.gitattributes b/e-suite.API.Common/.gitattributes
new file mode 100644
index 0000000..d7c444c
--- /dev/null
+++ b/e-suite.API.Common/.gitattributes
@@ -0,0 +1 @@
+* -crlf
\ No newline at end of file
diff --git a/e-suite.API.Common/.gitignore b/e-suite.API.Common/.gitignore
new file mode 100644
index 0000000..fa03bff
--- /dev/null
+++ b/e-suite.API.Common/.gitignore
@@ -0,0 +1,201 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+
+[Dd]ebug/
+[Rr]elease/
+x64/
+[Bb]in/
+[Oo]bj/
+
+# New VS2015 folders
+.vs/
+
+# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
+!packages/*/build/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.log
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+.*crunch*.local.xml
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# NuGet Packages Directory
+packages/
+
+# Compare files
+*.orig
+
+# Windows Azure Build Output
+csx
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.pfx
+*.publishsettings
+*.swp
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+# =========================
+# Windows detritus
+# =========================
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Mac crap
+.DS_Store
+
+# TeX files
+Documentation/*.aux
+Documentation/*.out
+Documentation/*.pdf
+Documentation/*.idx
+Documentation/*.toc
+Documentation/*.gz
+
+# image resizer cache
+imagecache/
+*.DotSettings
+
+# TypeScript mapping files
+*.js.map
+
+# Exclude all Typescript-generated JS files
+src/Sunrise.Web.Customer/App/*.js
+src/Sunrise.Web.Customer/App/**/*.js
+src/Sunrise.Web.Customer/App/**/*.js
+src/Sunrise.Web.Customer/Views/**/*.js
+src/Sunrise.Web.Customer/Areas/**/*.js
+src/Sunrise.Web.Support/Views/**/*.js
+
+*.GhostDoc.user.dic
+*.GhostDoc.xml
+
+/TestResult.xml
+*.VisualState.xml
+
+artifacts/
+/src/Sunrise.Web.Customer/Scripts/typings
+
+# Db project
+src/Sunrise.Database/*.jfm
+src/Sunrise.Database/Sunrise.Database.jfm
+/src/Sunrise.Database/*.jfm
+/src/Sunrise.Database/Sunrise.Database.jfm
+/src/Sunrise.Web.Customer/node_modules
+
+# node modules
+node_modules
+.eslintrc
+
+#NCrunch folders
+_NCrunch*/
+src/Sunrise.Web.FrontEnd/.eslintrc.js
+src/Sunrise.Web.FrontEnd/.prettierrc
+src/Sunrise.Web.FrontEnd/.vscode/settings.json
+src/Sunrise.Web.Customer/Content/bundles/vendor.js
diff --git a/e-suite.API.Common/.runsettings b/e-suite.API.Common/.runsettings
new file mode 100644
index 0000000..07b5799
--- /dev/null
+++ b/e-suite.API.Common/.runsettings
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .*\.dll$
+ .*\.exe$
+
+
+ .*moq.dll
+ .*nunit3.testadapter.dll
+
+
+
+ C:\b59fb11c-1611-4562-9a2b-c35719da65d3
+
+
+
+
+
+
+
+ True
+
+ True
+
+ True
+
+ False
+
+ True
+
+ True
+
+ True
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e-suite.API.Common/README.md b/e-suite.API.Common/README.md
new file mode 100644
index 0000000..0ca446a
--- /dev/null
+++ b/e-suite.API.Common/README.md
@@ -0,0 +1,20 @@
+# Introduction
+TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
+
+# Getting Started
+TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
+1. Installation process
+2. Software dependencies
+3. Latest releases
+4. API references
+
+# Build and Test
+TODO: Describe and show how to build your code and run the tests.
+
+# Contribute
+TODO: Explain how other users and developers can contribute to make your code better.
+
+If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
+- [ASP.NET Core](https://github.com/aspnet/Home)
+- [Visual Studio Code](https://github.com/Microsoft/vscode)
+- [Chakra Core](https://github.com/Microsoft/ChakraCore)
\ No newline at end of file
diff --git a/e-suite.API.Common/azure-pipelines.yml b/e-suite.API.Common/azure-pipelines.yml
new file mode 100644
index 0000000..bd7d20e
--- /dev/null
+++ b/e-suite.API.Common/azure-pipelines.yml
@@ -0,0 +1,136 @@
+# ASP.NET Core (.NET Framework)
+# Build and test ASP.NET Core projects targeting the full .NET Framework.
+# Add steps that publish symbols, save build artifacts, and more:
+# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core
+
+#name: $(BuildDefinitionName)_$(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)$(prerelease) # NOTE: rev resets when the default retention period expires
+name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)$(prerelease) # NOTE: rev resets when the default retention period expires
+
+trigger:
+ branches:
+ include:
+ - '*'
+
+pool:
+ vmImage: 'windows-latest'
+
+variables:
+ solution: '**/*.sln'
+ nugetProject: 'e-suite.API.Common/e-suite.API.Common.csproj'
+ buildPlatform: 'Any CPU'
+ buildConfiguration: 'Release'
+ ${{ if eq(variables['Build.SourceBranchName'], 'master') }}:
+ prerelease: ''
+ ${{ elseif eq(variables['Build.SourceBranchName'], 'develop') }}:
+ prerelease: '-beta'
+ ${{ else }}:
+ prerelease: '-alpha'
+
+steps:
+- task: NuGetToolInstaller@1
+ displayName: 'Install Nuget'
+
+- task: NuGetCommand@2
+ displayName: 'Nuget Restore'
+ inputs:
+ restoreSolution: '$(solution)'
+ feedsToUse: config
+ includeNuGetOrg: true
+ packagesdirectory: '..\packages'
+
+- task: VSBuild@1
+ displayName: 'Build Solution'
+ inputs:
+ solution: '$(solution)'
+ msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:DesktopBuildPackageLocation="$(build.artifactStagingDirectory)\WebApp.zip" /p:DeployIisAppPath="Default Web Site"'
+ platform: '$(buildPlatform)'
+ configuration: '$(buildConfiguration)'
+
+- task: VSTest@2
+ displayName: 'Run Tests'
+ inputs:
+ testAssemblyVer2: |
+ **\*Tests.dll
+ !**\obj\**
+ runOnlyImpactedTests: false
+ runInParallel: true
+ codeCoverageEnabled: true
+ runSettingsFile: .runsettings
+ platform: '$(BuildPlatform)'
+ configuration: '$(BuildConfiguration)'
+
+- task: BuildQualityChecks@9
+ displayName: 'Check build quality'
+ inputs:
+ # ===== Warnings Policy Inputs =====
+ checkWarnings: true # Optional
+ warningFailOption: fixed # Optional; Valid values: build, fixed
+ warningThreshold: '0' # Optional
+ #forceFewerWarnings: false # Optional
+ #allowWarningVariance: false # Optional
+ #warningVariance: # Required if allowWarningVariance = true
+ #showStatistics: false # Optional
+ #evaluateTaskWarnings: true # Optional
+ #warningTaskSelectors: '/^((vs|ms)build|ant(\s+.+)?|gradle(w)?(\s+.+)?|grunt|gulp|maven(\s+.+)?|xamarin(android|ios)|xcode(\s+.+)?|cmake|build\s+.+)$/i' # Optional (alias: warningTaskFilters)
+ #warningSelectors: # Optional (alias: warningFilters)
+ #inclusiveSelection: false # Optional (alias: inclusiveFiltering)
+ #evaluateFileWarnings: false # Optional
+ #warningFilesFolder: # Optional
+ #warningFiles: # Required if evaluateFileWarnings = true
+ #fileWarningSelectors: # Required if evaluateFileWarnings = true (alias: warningFileFilters)
+ #warningFilesArtifact: # Required if evaluateFileWarnings = true and (warningFailOption = build or showStatistics = true)
+ # ===== Code Coverage Policy Inputs =====
+ checkCoverage: true # Optional
+ coverageFailOption: fixed # Optional; Valid values: build, fixed
+ coverageType: lines # Optional; Valid values: blocks, lines, branches, custom
+ #customCoverageType: # Required if coverageType = custom
+ #treat0of0as100: false # Optional
+ coverageThreshold: '80' # Optional
+ forceCoverageImprovement: true # Optional
+ #coverageUpperThreshold: '80' # Optional
+ #ignoreDecreaseAboveUpperThreshold: true # Optional
+ #useUncoveredElements: false # Optional
+ #allowCoverageVariance: false # Optional
+ #coverageVariance: # Required if allowCoverageVariance = true
+ #coverageDeltaType: percentage # Optional; Valid values: absolute, percentage
+ #coveragePrecision: '4' # Optional
+ #buildConfiguration: # Optional
+ #buildPlatform: # Optional
+ #explicitSelector: false # Optional (alias: explicitFilter)
+ # ===== Baseline Inputs =====
+ #includePartiallySucceeded: true # Optional
+ #fallbackOnPRTargetBranch: true # Optional
+ #baseDefinitionSelector: # Ignored - only used by UI editor
+ #baseDefinitionId: # Optional
+ #baseRepoId: # Ignored - only used by UI editor
+ #baseBranchRef: # Optional
+ # ===== Reporting Inputs =====
+ #runTitle: # Optional
+ #fileAnalysisTitle: # Optional
+ # ===== Advanced Inputs =====
+ #disableCertCheck: false # Optional
+ #createBuildIssues: true # Optional
+
+- task: DotNetCoreCLI@2
+ displayName: "Package Nuget Artifact"
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ command: 'pack'
+ arguments: '--configuration $(buildConfiguration)'
+ packagesToPack: '$(nugetProject)'
+ nobuild: true
+ versionEnvVar: 'build.BuildNumber'
+ versioningScheme: 'byEnvVar'
+ #versioningScheme: byBuildNumber # https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/dotnet-core-cli?view=azure-devops#yaml-snippet
+
+- task: NuGetCommand@2
+ displayName: 'Publish Nuget Artifact'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ command: 'push'
+ feedsToUse: 'select'
+ packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;!$(Build.ArtifactStagingDirectory)/**/*.symbols.nupkg'
+ nuGetFeedType: 'internal'
+ publishVstsFeed: 'e-suite/e-suite'
+ versioningScheme: 'off'
+ allowPackageConflicts: true
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common.UnitTests/CustomFieldValueExtensionsUnitTests.cs b/e-suite.API.Common/e-suite.API.Common.UnitTests/CustomFieldValueExtensionsUnitTests.cs
new file mode 100644
index 0000000..6ac299f
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common.UnitTests/CustomFieldValueExtensionsUnitTests.cs
@@ -0,0 +1,43 @@
+using e_suite.API.Common.models;
+using eSuite.Core.Miscellaneous;
+using NUnit.Framework;
+
+namespace e_suite.API.Common.UnitTests;
+
+[TestFixture]
+public class CustomFieldValueExtensionsUnitTests
+{
+ [Test]
+ public void test()
+ {
+ //Arrange
+ var values = new List();
+ values.Add(new CustomFieldValues
+ {
+ Id = new GeneralIdRef
+ {
+ Id = 666,
+ Guid = new Guid("c8564cb3-9d42-4a56-8b0d-df1792cd6c4a")
+ },
+ Values = new List
+ {
+ new()
+ {
+ Value = "Hello",
+ DisplayValue = "Hello Display"
+ }
+ }
+ });
+
+ //Act
+ var results = values.ToCustomFieldsValues();
+
+ //Assert
+ Assert.That(results.Count, Is.EqualTo(1));
+ Assert.That(results[0].Id.Id, Is.EqualTo(666));
+ Assert.That(results[0].Id.Guid, Is.EqualTo(new Guid("c8564cb3-9d42-4a56-8b0d-df1792cd6c4a")));
+ Assert.That(results[0].Values.Count, Is.EqualTo(1));
+ Assert.That(results[0].Values[0].Value, Is.EqualTo("Hello"));
+ Assert.That(results[0].Values[0].DisplayValue, Is.EqualTo("Hello Display"));
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common.UnitTests/DtoAutoTester.cs b/e-suite.API.Common/e-suite.API.Common.UnitTests/DtoAutoTester.cs
new file mode 100644
index 0000000..d0ff40d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common.UnitTests/DtoAutoTester.cs
@@ -0,0 +1,102 @@
+using System.Reflection;
+using e_suite.API.Common.models;
+using NUnit.Framework;
+
+namespace e_suite.API.Common.UnitTests;
+
+[TestFixture]
+public class DtoAutoTester
+{
+ [Test]
+ public void RunTests()
+ {
+ var modelsList = new List();
+
+ var assembly = Assembly.GetAssembly(typeof(AddRoleSecurityAccess));
+
+ var allTypes = assembly!.GetTypes().Where( x => x.Namespace == "e_suite.API.Common.models");
+
+ foreach (var type in allTypes)
+ {
+ RunTestsOnClass(type);
+ }
+ }
+
+ private static void RunTestsOnClass(Type type)
+ {
+ if (type.IsAbstract)
+ return; //Nothing to test here, as abstract classes do not get created.
+
+ if (type.DeclaringType != null)
+ return; //types that have a declaring type are doing something funky, and I don't want to test them directly.
+
+ //Create an instance of the object to play with.
+ var instance = Activator.CreateInstance(type);
+
+ var allProperties = type.GetProperties();
+ foreach (var property in allProperties)
+ {
+ if (property.CanWrite)
+ TestCanWrite(property, instance);
+
+ if (property.CanRead)
+ TestCanRead(property, instance);
+ }
+ }
+
+ private static void TestCanRead(PropertyInfo property, object? instance)
+ {
+ try
+ {
+ var result = property.GetValue(instance);
+ }
+ catch(Exception ex)
+ {
+ Assert.Fail(ex.Message);
+ }
+ }
+
+ private static void TestCanWrite(PropertyInfo property, object? instance)
+ {
+ var propertyType = property.PropertyType;
+
+ if (propertyType.IsInterface)
+ {
+ try
+ {
+ var actualPropertyType = GetTypeForInterface(propertyType);
+
+ var writeValue = Activator.CreateInstance(actualPropertyType!);
+ property.SetValue(instance, writeValue, null);
+ }
+ catch (Exception ex)
+ {
+ Assert.Fail(ex.ToString());
+ }
+ }
+ else if (propertyType != typeof(string))
+ {
+ try
+ {
+ var writeValue = Activator.CreateInstance(property.PropertyType);
+ property.SetValue(instance, writeValue, null);
+ }
+ catch (Exception ex)
+ {
+ Assert.Fail(ex.ToString());
+ }
+ }
+ }
+
+ private static Type? GetTypeForInterface(Type propertyType)
+ {
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+
+ var alltypes = assemblies
+ .SelectMany(s => s.GetTypes()).ToList();
+
+ var actualPropertyType = alltypes
+ .FirstOrDefault(p => p.GetInterfaces().Any(x => x == propertyType));
+ return actualPropertyType;
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common.UnitTests/ExceptionUnitTests.cs b/e-suite.API.Common/e-suite.API.Common.UnitTests/ExceptionUnitTests.cs
new file mode 100644
index 0000000..5b48462
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common.UnitTests/ExceptionUnitTests.cs
@@ -0,0 +1,22 @@
+using e_suite.API.Common.exceptions;
+using e_suite.API.Common.UnitTests.helper;
+using NUnit.Framework;
+
+namespace e_suite.API.Common.UnitTests;
+
+[TestFixture]
+public class ExceptionUnitTests
+{
+ [TestCase(typeof(ExistsException))]
+ [TestCase(typeof(InvalidEmailException))]
+ [TestCase(typeof(InvalidReferenceObjectId))]
+ [TestCase(typeof(MaximumRangeException))]
+ [TestCase(typeof(MinimumRangeException))]
+ [TestCase(typeof(NotFoundException))]
+ [TestCase(typeof(TokenInvalidException))]
+ [TestCase(typeof(WeakPasswordException))]
+ public void ExistsException_PerformExcpetionTests(Type testcase)
+ {
+ ExceptionTester.PerformTestSuite(testcase);
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common.UnitTests/ValidateUnitTests.cs b/e-suite.API.Common/e-suite.API.Common.UnitTests/ValidateUnitTests.cs
new file mode 100644
index 0000000..c5b7f8e
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common.UnitTests/ValidateUnitTests.cs
@@ -0,0 +1,164 @@
+using System.ComponentModel.DataAnnotations;
+using e_suite.API.Common.models;
+using eSuite.Core.Miscellaneous;
+using eSuite.Core.Sequences;
+using NUnit.Framework;
+
+namespace SequenceManager.UnitTests;
+
+[TestFixture]
+public class ValidateUnitTests
+{
+ [Test]
+ public void Validate_MissingNumericPlaceholder_FailValidation()
+ {
+ // Arrange
+ var sequence = new Sequence
+ {
+ GeneralIdRef = new GeneralIdRef { Guid = Guid.NewGuid() },
+ Increment = 1,
+ Name = "Sequence name",
+ Pattern = "{DD}",
+ RolloverType = Rollover.Day,
+ Seed = 1
+ };
+
+ var context = new ValidationContext(sequence);
+
+ // Act
+ var results = sequence.Validate(context).ToList();
+
+ // Assert
+ Assert.That(results.Count, Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.Count(), Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.ToList()[0], Is.EqualTo(nameof(Sequence.Pattern)));
+ Assert.That(results[0].ErrorMessage, Is.EqualTo("At minimum all patterns must contain one numeric value (i.e. [0])"));
+ }
+
+ [Test]
+ public void Validate_MissingYearPlaceholder_FailValidation()
+ {
+ // Arrange
+ var sequence = new Sequence
+ {
+ GeneralIdRef = new GeneralIdRef { Guid = Guid.NewGuid() },
+ Increment = 1,
+ Name = "Sequence name",
+ Pattern = "Order-[000]",
+ RolloverType = Rollover.Year,
+ Seed = 1
+ };
+
+ var context = new ValidationContext(sequence);
+
+ // Act
+ var results = sequence.Validate(context).ToList();
+
+ // Asserts
+ Assert.That(results.Count, Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.Count(), Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.ToList()[0], Is.EqualTo(nameof(Sequence.Pattern)));
+ Assert.That(results[0].ErrorMessage, Is.EqualTo("When the rollover type is set to 'Annual', then the pattern must include either a {YY} or {YYYY} placeholder"));
+ }
+
+ [Test]
+ public void Validate_MissingMonthPlaceholder_FailValidation()
+ {
+ // Arrange
+ var sequence = new Sequence
+ {
+ GeneralIdRef = new GeneralIdRef { Guid = Guid.NewGuid() },
+ Increment = 1,
+ Name = "Sequence name",
+ Pattern = "Order-[000]",
+ RolloverType = Rollover.Month,
+ Seed = 1
+ };
+
+ var context = new ValidationContext(sequence);
+
+ // Act
+ var results = sequence.Validate(context).ToList();
+
+ // Asserts
+ Assert.That(results.Count, Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.Count(), Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.ToList()[0], Is.EqualTo(nameof(Sequence.Pattern)));
+ Assert.That(results[0].ErrorMessage, Is.EqualTo("When the rollover type is set to 'Monthly', then the pattern must include either a {MM} placeholder"));
+ }
+
+ [Test]
+ public void Validate_MissingDayPlaceholder_FailValidation()
+ {
+ // Arrange
+ var sequence = new Sequence
+ {
+ GeneralIdRef = new GeneralIdRef { Guid = Guid.NewGuid() },
+ Increment = 1,
+ Name = "Sequence name",
+ Pattern = "Order-[000]",
+ RolloverType = Rollover.Day,
+ Seed = 1
+ };
+
+ var context = new ValidationContext(sequence);
+
+ // Act
+ var results = sequence.Validate(context).ToList();
+
+ // Asserts
+ Assert.That(results.Count, Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.Count(), Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.ToList()[0], Is.EqualTo(nameof(Sequence.Pattern)));
+ Assert.That(results[0].ErrorMessage, Is.EqualTo("When the rollover type is set to 'Daily', then the pattern must include either a {DD} placeholder"));
+ }
+
+ [Test]
+ public void Validate_Validation_Pass()
+ {
+ // Arrange
+ var sequence = new Sequence
+ {
+ GeneralIdRef = new GeneralIdRef { Guid = Guid.NewGuid() },
+ Increment = 1,
+ Name = "Sequence name",
+ Pattern = "Order [0000] from {DD}-{MM}-{YYYY}",
+ RolloverType = Rollover.Day,
+ Seed = 1
+ };
+
+ var context = new ValidationContext(sequence);
+
+ // Act
+ var results = sequence.Validate(context).ToList();
+
+ // Assert
+ Assert.That(results.Count, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void Validate_Increment0_Fails()
+ {
+ // Arrange
+ var sequence = new Sequence
+ {
+ GeneralIdRef = new GeneralIdRef { Guid = Guid.NewGuid() },
+ Increment = 0,
+ Name = "Sequence name",
+ Pattern = "Order [0000] from {DD}-{MM}-{YYYY}",
+ RolloverType = Rollover.Day,
+ Seed = 1
+ };
+
+ var context = new ValidationContext(sequence);
+
+ // Act
+ var results = sequence.Validate(context).ToList();
+
+ // Assert
+ Assert.That(results.Count, Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.Count(), Is.EqualTo(1));
+ Assert.That(results[0].MemberNames.ToList()[0], Is.EqualTo(nameof(Sequence.Increment)));
+ Assert.That(results[0].ErrorMessage, Is.EqualTo("Increment can not be 0"));
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common.UnitTests/e-suite.API.Common.UnitTests.csproj b/e-suite.API.Common/e-suite.API.Common.UnitTests/e-suite.API.Common.UnitTests.csproj
new file mode 100644
index 0000000..376715e
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common.UnitTests/e-suite.API.Common.UnitTests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net10.0
+ e_suite.API.Common.UnitTests
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e-suite.API.Common/e-suite.API.Common.UnitTests/helper/ExceptionTester.cs b/e-suite.API.Common/e-suite.API.Common.UnitTests/helper/ExceptionTester.cs
new file mode 100644
index 0000000..ebfc69a
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common.UnitTests/helper/ExceptionTester.cs
@@ -0,0 +1,57 @@
+using NUnit.Framework;
+
+namespace e_suite.API.Common.UnitTests.helper;
+
+public static class ExceptionTester
+{
+ public static void PerformTestSuite(Type exceptionUnderTest)
+ {
+ Assert.That(exceptionUnderTest, Is.Not.Null);
+ Assert.That(InheritsFrom(exceptionUnderTest), Is.True);
+ Assert.That(CreateInstanceWithNoMessage(exceptionUnderTest), Is.True);
+ Assert.That(CreateInstanceWithMessage(exceptionUnderTest), Is.True);
+ Assert.That(CreateInstanceWithMessageAndInnerException(exceptionUnderTest), Is.True);
+ }
+
+ private static bool CreateInstanceWithMessageAndInnerException(Type exceptionUnderTest)
+ {
+ var innerException = new Exception("InnerException");
+
+ var newInstance = (Exception)Activator.CreateInstance(exceptionUnderTest, "Test", innerException)!;
+
+ Assert.That(newInstance, Is.Not.Null);
+ Assert.That(newInstance?.InnerException, Is.EqualTo(innerException));
+
+ return newInstance?.GetType() == exceptionUnderTest;
+ }
+
+ private static bool CreateInstanceWithMessage(Type exceptionUnderTest)
+ {
+ var newInstance = Activator.CreateInstance(exceptionUnderTest, "Test");
+ Assert.That(newInstance, Is.Not.Null);
+ return newInstance?.GetType() == exceptionUnderTest;
+ }
+
+ private static bool CreateInstanceWithNoMessage(Type exceptionUnderTest)
+ {
+ var newInstance = Activator.CreateInstance(exceptionUnderTest);
+ Assert.That(newInstance, Is.Not.Null);
+ return newInstance?.GetType() == exceptionUnderTest;
+ }
+
+ private static bool InheritsFrom(Type exceptionUnderTest)
+ {
+ var typeUnderTest = exceptionUnderTest;
+
+ while (typeUnderTest != null)
+ {
+ if (typeUnderTest == typeof(T))
+ {
+ return true;
+ }
+ typeUnderTest = typeUnderTest.BaseType;
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common.sln b/e-suite.API.Common/e-suite.API.Common.sln
new file mode 100644
index 0000000..2082796
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common.sln
@@ -0,0 +1,39 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.4.33213.308
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "e-suite.API.Common", "e-suite.API.Common\e-suite.API.Common.csproj", "{B83BC836-F72F-4109-ABEF-6DBFB0D32CF3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{E3CF6121-8D87-4C4B-A3EE-73AE78F92DF4}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{D4A0A029-718D-436F-AECE-F04CCD4FDD65}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "e-suite.API.Common.UnitTests", "e-suite.API.Common.UnitTests\e-suite.API.Common.UnitTests.csproj", "{4D647593-4BEA-4ACB-A886-82491D703E52}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B83BC836-F72F-4109-ABEF-6DBFB0D32CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B83BC836-F72F-4109-ABEF-6DBFB0D32CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B83BC836-F72F-4109-ABEF-6DBFB0D32CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B83BC836-F72F-4109-ABEF-6DBFB0D32CF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4D647593-4BEA-4ACB-A886-82491D703E52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4D647593-4BEA-4ACB-A886-82491D703E52}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4D647593-4BEA-4ACB-A886-82491D703E52}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4D647593-4BEA-4ACB-A886-82491D703E52}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {D4A0A029-718D-436F-AECE-F04CCD4FDD65} = {E3CF6121-8D87-4C4B-A3EE-73AE78F92DF4}
+ {4D647593-4BEA-4ACB-A886-82491D703E52} = {D4A0A029-718D-436F-AECE-F04CCD4FDD65}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {63B8E197-C2CC-44D5-8FE3-4DB963DB9D2B}
+ EndGlobalSection
+EndGlobal
diff --git a/e-suite.API.Common/e-suite.API.Common/IAuditLog.cs b/e-suite.API.Common/e-suite.API.Common/IAuditLog.cs
new file mode 100644
index 0000000..2283e4d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IAuditLog.cs
@@ -0,0 +1,10 @@
+using e_suite.API.Common.models;
+using e_suite.Utilities.Pagination;
+
+namespace e_suite.API.Common;
+
+public interface IAuditLog
+{
+ Task> GetAuditLogEntries(Paging paging, string? logEntry, bool primaryOnly,
+ CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IBlockedIPsManager.cs b/e-suite.API.Common/e-suite.API.Common/IBlockedIPsManager.cs
new file mode 100644
index 0000000..219e6be
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IBlockedIPsManager.cs
@@ -0,0 +1,12 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+
+namespace e_suite.API.Common;
+
+public interface IBlockedIPsManager
+{
+ Task> GetBlockedIPs(Paging paging, CancellationToken cancellationToken);
+
+ Task UnblockIPAddress(AuditUserDetails auditUserDetails, string ipaddress, CancellationToken cancellationToken);
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/IContactsManager.cs b/e-suite.API.Common/e-suite.API.Common/IContactsManager.cs
new file mode 100644
index 0000000..3de4368
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IContactsManager.cs
@@ -0,0 +1,12 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface IContactsManager
+{
+ Task CreateContact(AuditUserDetails auditUserDetails, CreateContact createContactDto, CancellationToken cancellationToken);
+ Task EditContact(AuditUserDetails auditUserDetails, EditContact editContactDto, CancellationToken cancellationToken);
+ Task GetContact(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/ICustomFieldHelper.cs b/e-suite.API.Common/e-suite.API.Common/ICustomFieldHelper.cs
new file mode 100644
index 0000000..9338e49
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/ICustomFieldHelper.cs
@@ -0,0 +1,18 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Core.Models;
+using e_suite.Database.Core.Tables.CustomFields;
+using e_suite.Database.Core.Tables.Domain;
+using e_suite.Database.Core.Tables.Forms;
+using e_suite.Database.Core.Tables.Glossaries;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface ICustomFieldHelper
+{
+ Task TranslateToCustomFieldDefinitionAsync(CustomField customField, CancellationToken cancellationToken);
+ Task GetFormTemplateByGeneralRefIdAsync(IGeneralIdRef idRef, CancellationToken cancellationToken);
+ Task GetGlossaryByGeneralRefIdAsync(IGeneralIdRef idRef, CancellationToken cancellationToken);
+ Task GetDomainByGeneralRefIdAsync(IGeneralIdRef idRef, CancellationToken cancellationToken);
+ Task> CustomFieldValuesList(IEnumerable enumerableCustomFieldValues, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/ICustomFieldManager.cs b/e-suite.API.Common/e-suite.API.Common/ICustomFieldManager.cs
new file mode 100644
index 0000000..df024d9
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/ICustomFieldManager.cs
@@ -0,0 +1,19 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface ICustomFieldManager
+{
+ Task> GetFieldsAsync(Paging paging, CancellationToken cancellationToken);
+ Task CreateFieldAsync(AuditUserDetails auditUserDetails, CreateCustomField custimField, CancellationToken cancellationToken);
+ Task EditFieldAsync(AuditUserDetails auditUserDetails, EditCustomFields custimField, CancellationToken cancellationToken);
+
+ Task GetFieldAsync(IGeneralIdRef id, CancellationToken cancellationToken);
+
+ Task GetFieldAsync(string name, CancellationToken cancellationToken);
+
+ Task DeleteFieldAsync(AuditUserDetails auditUserDetails, IGeneralIdRef id, CancellationToken cancellationToken);
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/ICustomFieldValidator.cs b/e-suite.API.Common/e-suite.API.Common/ICustomFieldValidator.cs
new file mode 100644
index 0000000..0e8ab81
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/ICustomFieldValidator.cs
@@ -0,0 +1,11 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Database.Core.Tables.CustomFields;
+
+namespace e_suite.API.Common;
+
+public interface ICustomFieldValidator
+{
+ Task> ValidateFields(AuditUserDetails auditUserDetails, IEnumerable customFieldValues,
+ IReadOnlyCollection customFields, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IDomainManager.cs b/e-suite.API.Common/e-suite.API.Common/IDomainManager.cs
new file mode 100644
index 0000000..ae9bdf4
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IDomainManager.cs
@@ -0,0 +1,18 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface IDomainManager
+{
+ public Task> GetDomainsAsync(Paging paging, CancellationToken cancellationToken);
+ public Task GetDomainAsync(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+
+ public Task CreateDomainAsync(AuditUserDetails auditUserDetails, CreateDomain createDomain, CancellationToken cancellationToken);
+
+ public Task EditDomainAsync(AuditUserDetails auditUserDetails, EditDomain editDomain, CancellationToken cancellationToken);
+
+ public Task DeleteDomainAsync(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IEFlowSync.cs b/e-suite.API.Common/e-suite.API.Common/IEFlowSync.cs
new file mode 100644
index 0000000..1b9aea1
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IEFlowSync.cs
@@ -0,0 +1,8 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface IEFlowSync
+{
+ Task SyncEFlowPrinterCategories(List fullMessageDomains);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IExceptionLogManager.cs b/e-suite.API.Common/e-suite.API.Common/IExceptionLogManager.cs
new file mode 100644
index 0000000..517e1c5
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IExceptionLogManager.cs
@@ -0,0 +1,11 @@
+using e_suite.API.Common.models;
+using e_suite.Utilities.Pagination;
+
+namespace e_suite.API.Common;
+
+public interface IExceptionLogManager
+{
+ Task LogException(Exception ex, string application, string supportingArgs, CancellationToken cancellationToken);
+
+ Task> GetExceptionLogs(Paging paging, CancellationToken cancellationToken);
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/IFormsManager.cs b/e-suite.API.Common/e-suite.API.Common/IFormsManager.cs
new file mode 100644
index 0000000..23e2a60
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IFormsManager.cs
@@ -0,0 +1,27 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface IFormsManager
+{
+ Task CreateFormTemplateAsync(AuditUserDetails auditUserDetails, CreateFormTemplate createFormTemplate, CancellationToken cancellationToken);
+
+ Task EditFormTemplateAsync(AuditUserDetails auditUserDetails, EditFormTemplate editFormTemplate, CancellationToken cancellationToken);
+
+ Task GetFormTemplateAsync(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+
+ Task> GetFormTemplatesAsync(Paging paging, CancellationToken cancellationToken);
+
+ Task DeleteFormTemplateAsync(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+
+ Task CreateFormInstanceAsync(AuditUserDetails auditUserDetails, CreateFormInstance createFormInstance, CancellationToken cancellationToken);
+ Task CreateFormInstancesAsync(AuditUserDetails auditUserDetails, IEnumerable createFormInstance, CancellationToken cancellationToken);
+ Task GetFormInstanceAsync(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task> GetFormInstanceAsync(IEnumerable generalIdRefs, CancellationToken cancellationToken);
+
+ Task EditFormInstanceAsync(AuditUserDetails auditUserDetails, EditFormInstance editFormInstance, CancellationToken cancellationToken);
+ Task EditFormInstanceAsync(AuditUserDetails auditUserDetails, IEnumerable editFormInstances, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IGlossariesManager.cs b/e-suite.API.Common/e-suite.API.Common/IGlossariesManager.cs
new file mode 100644
index 0000000..b10f404
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IGlossariesManager.cs
@@ -0,0 +1,14 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface IGlossariesManager
+{
+ Task AddGlossaryItem(AuditUserDetails auditUserDetails, NewGlossaryItem glossaryItem, CancellationToken cancellationToken);
+ Task AddGlossaryItems(AuditUserDetails auditUserDetails, IEnumerable glossaryItems, CancellationToken cancellationToken);
+ Task DeleteGlossaryItem(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetGlossaryItem(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task UpdateGlossaryItem(AuditUserDetails auditUserDetails, EditGlossaryItem editGlossaryItem, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IMailTemplateManager.cs b/e-suite.API.Common/e-suite.API.Common/IMailTemplateManager.cs
new file mode 100644
index 0000000..c238e90
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IMailTemplateManager.cs
@@ -0,0 +1,14 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.MailService;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface IMailTemplateManager
+{
+ Task> GetMailTemplateTypes(Paging paging, CancellationToken cancellationToken);
+ Task GetMailTemplate( IGeneralIdRef domain, MailType mailType, CancellationToken cancellationToken);
+ Task PostMailTemplate(PostMailTemplate mailTemplate, AuditUserDetails auditUserDetails, CancellationToken cancellationToken );
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IOrganisationsManager.cs b/e-suite.API.Common/e-suite.API.Common/IOrganisationsManager.cs
new file mode 100644
index 0000000..0a8d28f
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IOrganisationsManager.cs
@@ -0,0 +1,17 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Database.Core.Tables.Printer;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface IOrganisationsManager
+{
+ Task> GetOrganisationList(Paging paging, CancellationToken cancellationToken);
+ Task AddOrganisation(AuditUserDetails auditUserDetails, CreateOrganisation addOrganisationDto, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task DeleteOrganisation(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task EditOrganisation(AuditUserDetails auditUserDetails, EditOrganisation editOrganisationDto, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task GetOrganisation(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IPerformanceMaintenance.cs b/e-suite.API.Common/e-suite.API.Common/IPerformanceMaintenance.cs
new file mode 100644
index 0000000..779a2c5
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IPerformanceMaintenance.cs
@@ -0,0 +1,6 @@
+namespace e_suite.API.Common;
+
+public interface IPerformanceMaintenance
+{
+ Task ClearOldPerformanceData();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IPerformanceManager.cs b/e-suite.API.Common/e-suite.API.Common/IPerformanceManager.cs
new file mode 100644
index 0000000..28833e3
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IPerformanceManager.cs
@@ -0,0 +1,5 @@
+namespace e_suite.API.Common;
+
+public interface IPerformanceManager
+{
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IRoleManager.cs b/e-suite.API.Common/e-suite.API.Common/IRoleManager.cs
new file mode 100644
index 0000000..191f349
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IRoleManager.cs
@@ -0,0 +1,29 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Database.Core.Tables.UserManager;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+using eSuite.Core.Security;
+
+namespace e_suite.API.Common;
+
+public interface IRoleManager
+{
+ Task> GetRoles(Paging paging, IGeneralIdRef domain, CancellationToken cancellationToken);
+
+ Task GetRole(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+
+ Task CreateRole(AuditUserDetails auditUserDetails, CreateRole sequence, CancellationToken cancellationToken);
+ Task DeleteRole(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task EditRole(AuditUserDetails auditUserDetails, EditRole editSequence, CancellationToken cancellationToken);
+ Task> GetRoleUsers(Paging paging, GeneralIdRef roleId, CancellationToken cancellationToken);
+ Task AddRoleUser(AuditUserDetails auditUserDetails, UserRoleIds userRole, CancellationToken cancellationToken);
+ Task DeleteRoleUser(AuditUserDetails auditUserDetails, UserRoleIds userRole, CancellationToken cancellationToken);
+ Task>GetAccessList(Paging paging, CancellationToken cancellationToken);
+ Task> GetRoleAccess(Paging paging, CancellationToken cancellationToken);
+ Task AddRoleSecurityAccess(AuditUserDetails auditUserDetails, AddRoleSecurityAccess accessToAdd, CancellationToken cancellationToken);
+ Task DeleteRoleSecurityAccess(AuditUserDetails auditUserDetails, DeleteRoleSecurityAccess accessToRemove, CancellationToken cancellationToken);
+ Task HasAnyAccess(long? userId, SecurityAccess accessKey);
+ Task CheckHasDomainAccess(long userId, IGeneralIdRef? specificDomain, SecurityAccess accessKey, CancellationToken cancellationToken);
+ IEnumerable GetMyUserAccess(User user);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/ISequenceManager.cs b/e-suite.API.Common/e-suite.API.Common/ISequenceManager.cs
new file mode 100644
index 0000000..cf236c0
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/ISequenceManager.cs
@@ -0,0 +1,17 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface ISequenceManager
+{
+ Task GetSequence(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task CreateSequence(AuditUserDetails auditUserDetails, NewSequence sequence, CancellationToken cancellationToken);
+ Task DeleteSequence(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task EditSequence(AuditUserDetails auditUserDetails, Sequence editSequence, CancellationToken cancellationToken);
+ Task> GetSequences(Paging paging, CancellationToken cancellationToken);
+ Task> NextValue(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task> NextValue(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, int number, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/ISigmaImporter.cs b/e-suite.API.Common/e-suite.API.Common/ISigmaImporter.cs
new file mode 100644
index 0000000..745d0e7
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/ISigmaImporter.cs
@@ -0,0 +1,8 @@
+namespace e_suite.API.Common;
+
+public interface ISigmaImporter
+{
+ Task ImportGMGProfiles();
+
+ Task ImportPrintSpecifications();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/ISiteManager.cs b/e-suite.API.Common/e-suite.API.Common/ISiteManager.cs
new file mode 100644
index 0000000..1e95ced
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/ISiteManager.cs
@@ -0,0 +1,20 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface ISiteManager
+{
+ Task> GetSites(Paging paging, CancellationToken cancellationToken);
+
+ Task GetSite(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetSiteByName(IGeneralIdRef organisationGeneralIdRef, string siteName, CancellationToken cancellationToken);
+ Task GetSiteBySigmaId(long sigmaId, CancellationToken cancellationToken);
+
+ Task CreateSite(AuditUserDetails auditUserDetails, CreateSite site, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task CreateSites(AuditUserDetails auditUserDetails, IEnumerable sites, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task EditSite(AuditUserDetails auditUserDetails, EditSite site, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task DeleteSite(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, bool triggerEFlowSync, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/ISpecificationManager.cs b/e-suite.API.Common/e-suite.API.Common/ISpecificationManager.cs
new file mode 100644
index 0000000..1a5913c
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/ISpecificationManager.cs
@@ -0,0 +1,18 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface ISpecificationManager
+{
+ Task GetTemplateForPrintSpec(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task> GetSpecifications(Paging paging, CancellationToken cancellationToken);
+ Task GetSpecification(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task CreateSpecification(AuditUserDetails auditUserDetails, CreateSpecification create, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task CreateSpecification(AuditUserDetails auditUserDetails, IEnumerable create, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task DeleteSpecification(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task EditSpecification(AuditUserDetails auditUserDetails, EditSpecification edit, bool triggerEFlowSync, CancellationToken cancellationToken);
+ Task EditSpecification(AuditUserDetails auditUserDetails, IEnumerable items, bool triggerEFlowSync, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/ISsoManager.cs b/e-suite.API.Common/e-suite.API.Common/ISsoManager.cs
new file mode 100644
index 0000000..18b52fb
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/ISsoManager.cs
@@ -0,0 +1,15 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface ISsoManager
+{
+ Task> GetSsoProvidersAsync(Paging paging, CancellationToken cancellationToken);
+ Task GetSsoProviderAsync(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task AddSsoProviderAsync(AuditUserDetails auditUserDetails, CreateSsoProvider ssoProvider, CancellationToken cancellationToken);
+ Task EditSsoProviderAsync(AuditUserDetails auditUserDetails, EditSsoProvider ssoProvider, CancellationToken cancellationToken);
+ Task RemoveSsoProviderAsync( AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IUserManager.cs b/e-suite.API.Common/e-suite.API.Common/IUserManager.cs
new file mode 100644
index 0000000..e5a2b2d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IUserManager.cs
@@ -0,0 +1,37 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Database.Core.Models;
+using e_suite.Database.Core.Tables.UserManager;
+using e_suite.Utilities.Pagination;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common;
+
+public interface IUserManager
+{
+ Task Login(Login login, CancellationToken cancellationToken = default!);
+ Task LoginSso(long ssoId, string ssoUserId, CancellationToken cancellationToken = default!);
+ Task ForgotPassword(string email, CancellationToken cancellationToken = default!);
+ Task CreateUser(AuditUserDetails auditUserDetails, UserRegistration userRegistration, CancellationToken cancellationToken = default!);
+ Task RefreshToken(string email, CancellationToken cancellationToken = default!);
+ Task RefreshToken(IGeneralIdRef id, CancellationToken cancellationToken = default!);
+ Task CompleteEmailAction(EmailActionToken token, CancellationToken cancellationToken = default!);
+ Task DeactivateUser(AuditUserDetails auditUserDetails, string email, CancellationToken cancellationToken = default!);
+ Task DeactivateUser(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken = default!);
+ Task GetProfile(string email, CancellationToken cancellationToken = default!);
+ Task UpdateProfile(AuditUserDetails auditUserDetails, string email, UpdatedUserProfile userProfile, CancellationToken cancellationToken = default!);
+ public Task> GetUsersAsync(Paging paging, CancellationToken cancellationToken);
+ Task GetUserAsync(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetUserByEmailAsync(string email, CancellationToken cancellationToken);
+
+ Task EditUser(AuditUserDetails auditUserDetails, EditUser user, CancellationToken cancellationToken);
+ Task ResendConfirmEmail(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetCurrentEmailActionUrl(string emailAddress, EmailUserActionType emailUserActionType, CancellationToken cancellationToken );
+ Task GetSsoProviderForEmail(string loginEmail, CancellationToken cancellationToken);
+ Task GetSsoProviderById(long ssoProviderId, CancellationToken cancellationToken);
+ Task LinkSsoProfileToUser(AuditUserDetails auditUserDetails, User user, long ssoId, string ssoUserId, bool setEmailConfirmed, CancellationToken cancellationToken);
+ Task TurnOfSsoForUser(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task CreateSingleUseGuid(AuditUserDetails auditUserDetails, GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetUserWithSingleUseGuid(Guid guid, CancellationToken cancellationToken);
+ Task SetAuthentication( AuditUserDetails auditUserDetails, UserAuthenticationDetails userAuthenticationDetails, bool setEmailConfirmed, CancellationToken cancellationToken );
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/IUserManagerMaintenance.cs b/e-suite.API.Common/e-suite.API.Common/IUserManagerMaintenance.cs
new file mode 100644
index 0000000..3949646
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/IUserManagerMaintenance.cs
@@ -0,0 +1,7 @@
+namespace e_suite.API.Common;
+
+public interface IUserManagerMaintenance
+{
+ Task ClearOldEmailActions();
+ Task ClearOldSingleUserGuids();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/e-suite.API.Common.csproj b/e-suite.API.Common/e-suite.API.Common/e-suite.API.Common.csproj
new file mode 100644
index 0000000..6a99cae
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/e-suite.API.Common.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net10.0
+ e_suite.API.Common
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/EmailNotConfirmedException.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/EmailNotConfirmedException.cs
new file mode 100644
index 0000000..0a908b0
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/EmailNotConfirmedException.cs
@@ -0,0 +1,23 @@
+namespace e_suite.API.Common.exceptions;
+
+public class EmailNotConfirmedException : Exception
+{
+ public EmailNotConfirmedException()
+ {
+ }
+
+ public EmailNotConfirmedException(string? message)
+ : base(message)
+ {
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public EmailNotConfirmedException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/ExistsException.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/ExistsException.cs
new file mode 100644
index 0000000..e329acf
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/ExistsException.cs
@@ -0,0 +1,24 @@
+namespace e_suite.API.Common.exceptions;
+
+public class ExistsException : Exception
+{
+ public ExistsException()
+ {
+
+ }
+
+ public ExistsException(string? message)
+ : base(message)
+ {
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public ExistsException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/InvalidEmailException.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/InvalidEmailException.cs
new file mode 100644
index 0000000..922eafa
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/InvalidEmailException.cs
@@ -0,0 +1,25 @@
+namespace e_suite.API.Common.exceptions;
+
+public class InvalidEmailException : Exception
+{
+ public InvalidEmailException()
+ {
+
+ }
+
+ public InvalidEmailException(string? message)
+ : base(message)
+ {
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public InvalidEmailException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/InvalidReferanceObjectId.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/InvalidReferanceObjectId.cs
new file mode 100644
index 0000000..dfe6de6
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/InvalidReferanceObjectId.cs
@@ -0,0 +1,16 @@
+namespace e_suite.API.Common.exceptions;
+
+public class InvalidReferenceObjectId : Exception
+{
+ public InvalidReferenceObjectId()
+ {
+ }
+
+ public InvalidReferenceObjectId(string? message) : base(message)
+ {
+ }
+
+ public InvalidReferenceObjectId(string? message, Exception? innerException) : base(message, innerException)
+ {
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/MaximumRangeException.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/MaximumRangeException.cs
new file mode 100644
index 0000000..f2152d3
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/MaximumRangeException.cs
@@ -0,0 +1,24 @@
+namespace e_suite.API.Common.exceptions;
+
+public class MaximumRangeException : Exception
+{
+ public MaximumRangeException() : base()
+ {
+
+ }
+
+ public MaximumRangeException(string? message)
+ : base(message)
+ {
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public MaximumRangeException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/MinimumRangeException.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/MinimumRangeException.cs
new file mode 100644
index 0000000..7c5c681
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/MinimumRangeException.cs
@@ -0,0 +1,24 @@
+namespace e_suite.API.Common.exceptions;
+
+public class MinimumRangeException : Exception
+{
+ public MinimumRangeException() : base()
+ {
+
+ }
+
+ public MinimumRangeException(string? message)
+ : base(message)
+ {
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public MinimumRangeException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/NotFoundException.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/NotFoundException.cs
new file mode 100644
index 0000000..26309e8
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/NotFoundException.cs
@@ -0,0 +1,24 @@
+namespace e_suite.API.Common.exceptions;
+
+public class NotFoundException : Exception
+{
+ public NotFoundException()
+ {
+
+ }
+
+ public NotFoundException(string? message)
+ : base(message)
+ {
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public NotFoundException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/TokenInvalidException.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/TokenInvalidException.cs
new file mode 100644
index 0000000..7df7a2e
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/TokenInvalidException.cs
@@ -0,0 +1,25 @@
+namespace e_suite.API.Common.exceptions;
+
+public class TokenInvalidException : Exception
+{
+ public TokenInvalidException()
+ {
+
+ }
+
+ public TokenInvalidException(string? message)
+ : base(message)
+ {
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public TokenInvalidException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/exceptions/WeakPasswordException.cs b/e-suite.API.Common/e-suite.API.Common/exceptions/WeakPasswordException.cs
new file mode 100644
index 0000000..feaa747
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/exceptions/WeakPasswordException.cs
@@ -0,0 +1,23 @@
+namespace e_suite.API.Common.exceptions;
+
+public class WeakPasswordException : Exception
+{
+ public WeakPasswordException()
+ {
+ }
+
+ public WeakPasswordException(string? message)
+ : base(message)
+ {
+ }
+
+ // Creates a new Exception. All derived classes should
+ // provide this constructor.
+ // Note: the stack trace is not started until the exception
+ // is thrown
+ //
+ public WeakPasswordException(string? message, Exception? innerException)
+ : base(message, innerException)
+ {
+ }
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/extensions/ConfigurationHelper.cs b/e-suite.API.Common/e-suite.API.Common/extensions/ConfigurationHelper.cs
new file mode 100644
index 0000000..8b88293
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/extensions/ConfigurationHelper.cs
@@ -0,0 +1,16 @@
+using Microsoft.Extensions.Configuration;
+
+namespace e_suite.API.Common.extensions;
+
+public static class ConfigurationHelper
+{
+ public static T? GetConfigValue(this IConfiguration configuration, string environmentVariable, string key, T defaultValue)
+ {
+ var envVariable = Environment.GetEnvironmentVariable(environmentVariable);
+
+ if (!string.IsNullOrWhiteSpace(envVariable))
+ return (T?)Convert.ChangeType(envVariable, typeof(T));
+
+ return configuration.GetValue(key, defaultValue);
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/extensions/ObjectExtensions.cs b/e-suite.API.Common/e-suite.API.Common/extensions/ObjectExtensions.cs
new file mode 100644
index 0000000..9476c7b
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/extensions/ObjectExtensions.cs
@@ -0,0 +1,16 @@
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System;
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+public static class ObjectExtensions
+{
+ public static T DeepClone(this T obj)
+ {
+ using var stream = new MemoryStream();
+ var serializer = new Xml.Serialization.XmlSerializer(typeof(T));
+
+ serializer.Serialize(stream, obj);
+ stream.Position = 0;
+ return (T)serializer.Deserialize(stream)!;
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/AddRoleSecurityAccess.cs b/e-suite.API.Common/e-suite.API.Common/models/AddRoleSecurityAccess.cs
new file mode 100644
index 0000000..32b296a
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/AddRoleSecurityAccess.cs
@@ -0,0 +1,10 @@
+using eSuite.Core.Miscellaneous;
+using eSuite.Core.Security;
+
+namespace e_suite.API.Common.models;
+
+public class AddRoleSecurityAccess
+{
+ public GeneralIdRef RoleId { get; set; } = null!;
+ public List SecurityAccess { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/AuditLogEntry.cs b/e-suite.API.Common/e-suite.API.Common/models/AuditLogEntry.cs
new file mode 100644
index 0000000..f2f1d81
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/AuditLogEntry.cs
@@ -0,0 +1,27 @@
+using e_suite.Database.Audit.Models;
+
+namespace e_suite.API.Common.models;
+
+public class AuditLogEntry : IId
+{
+ public long Id { get; set; }
+
+ public long UserId { get; set; }
+ public string UserDisplayName { get; set; } = string.Empty;
+
+ public string Type { get; set; } = string.Empty;
+
+ public DateTimeOffset DateTime { get; set; }
+
+ public string Fields { get; set; } = string.Empty;
+
+ public string Comment { get; set; } = string.Empty;
+
+ public string EntityName { get; set; } = string.Empty;
+
+ public string PrimaryKey { get; set; } = string.Empty;
+
+ public string EntityDisplayName { get; set; } = string.Empty;
+
+ public string DisplayName { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/AuditParams.cs b/e-suite.API.Common/e-suite.API.Common/models/AuditParams.cs
new file mode 100644
index 0000000..71d63d9
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/AuditParams.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class AuditParams
+{
+ [JsonPropertyName("entityName")]
+ public string EntityName { get; set; } = string.Empty;
+
+ [JsonPropertyName("primaryKey")]
+ public string PrimaryKey { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/BlockedIPs.cs b/e-suite.API.Common/e-suite.API.Common/models/BlockedIPs.cs
new file mode 100644
index 0000000..8a868b1
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/BlockedIPs.cs
@@ -0,0 +1,13 @@
+namespace e_suite.API.Common.models;
+
+public class BlockedIPs
+{
+ public string IPAddress { get; set; } = string.Empty;
+
+ public int NumberOfAttempts { get; set; }
+
+ public DateTimeOffset BlockedAt { get; set; }
+
+ public int UnblockedIn { get; set; }
+
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateContact.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateContact.cs
new file mode 100644
index 0000000..45bcd2c
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateContact.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class CreateContact
+{
+ [JsonPropertyName("guid")]
+ public Guid? Guid { get; set; }
+
+ [JsonPropertyName("jCard")]
+ public string JCard { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateCustomField.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateCustomField.cs
new file mode 100644
index 0000000..fe4a664
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateCustomField.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+
+namespace e_suite.API.Common.models;
+
+public class CreateCustomField : CustomFieldBase
+{
+
+ [JsonPropertyName("guid")]
+ public Guid? Guid { get; set; } = System.Guid.NewGuid();
+
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateDomain.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateDomain.cs
new file mode 100644
index 0000000..9b36876
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateDomain.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+
+namespace e_suite.API.Common.models;
+
+public class CreateDomain : DomainBase
+{
+ [JsonPropertyName("guid")]
+ public Guid? Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateFormInstance.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateFormInstance.cs
new file mode 100644
index 0000000..fe33f1f
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateFormInstance.cs
@@ -0,0 +1,19 @@
+using System.Text.Json.Serialization;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class CreateFormInstance
+{
+ [JsonPropertyName("templateId")]
+ public GeneralIdRef TemplateId { get; set; } = null!;
+
+ [JsonPropertyName("version")]
+ public long Version { get; set; }
+
+ [JsonPropertyName("customFieldValues")]
+ public List CustomFieldValues { get; set; } = new();
+
+ [JsonPropertyName("guid")]
+ public Guid? Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateFormTemplate.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateFormTemplate.cs
new file mode 100644
index 0000000..3275179
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateFormTemplate.cs
@@ -0,0 +1,17 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class CreateFormTemplate
+{
+ [JsonPropertyName("name")]
+ [Required]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("definition")]
+ public string Definition { get; set; } = string.Empty;
+
+ [JsonPropertyName("guid")]
+ public Guid? Guid { get; set; }
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateOrganisation.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateOrganisation.cs
new file mode 100644
index 0000000..71fc3c4
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateOrganisation.cs
@@ -0,0 +1,20 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class CreateOrganisation
+{
+ [JsonPropertyName("guid")]
+ public Guid? Guid { get; set; }
+ [JsonPropertyName("name")]
+ [Required]
+ public string Name { get; set; } = string.Empty;
+ [JsonPropertyName("address")]
+ [Required]
+ public string Address { get; set; } = string.Empty;
+ [JsonPropertyName("status")]
+ [Required]
+ public OrganisationStatus Status { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateRole.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateRole.cs
new file mode 100644
index 0000000..4e06a07
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateRole.cs
@@ -0,0 +1,13 @@
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class CreateRole : RoleBase
+{
+ public Guid? Guid { get; set; }
+ public bool IsSuperUser { get; set; }
+ public bool IsAdministrator { get; set; }
+ public bool CanDelete { get; set; } = true;
+ public GeneralIdRef DomainId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateSite.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateSite.cs
new file mode 100644
index 0000000..2ea07b5
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateSite.cs
@@ -0,0 +1,8 @@
+using e_suite.API.Common.models.@base;
+
+namespace e_suite.API.Common.models;
+
+public class CreateSite : SiteBase
+{
+ public Guid? Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateSpecification.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateSpecification.cs
new file mode 100644
index 0000000..95ccfae
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateSpecification.cs
@@ -0,0 +1,8 @@
+using e_suite.API.Common.models.@base;
+
+namespace e_suite.API.Common.models;
+
+public class CreateSpecification : SpecificationBase
+{
+ public Guid? Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CreateSsoProvider.cs b/e-suite.API.Common/e-suite.API.Common/models/CreateSsoProvider.cs
new file mode 100644
index 0000000..39c9cd8
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CreateSsoProvider.cs
@@ -0,0 +1,8 @@
+using e_suite.API.Common.models.@base;
+
+namespace e_suite.API.Common.models;
+
+public class CreateSsoProvider : SsoProviderBase
+{
+ public Guid? Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValue.cs b/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValue.cs
new file mode 100644
index 0000000..375a3a9
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValue.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class CustomFieldValue
+{
+ ///
+ /// Value can be either a string or a GeneralRefId
+ ///
+ [JsonPropertyName("value")]
+ public object Value { get; set; } = null!;
+
+ [JsonPropertyName("displayValue")]
+ public string? DisplayValue { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValueExtensions.cs b/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValueExtensions.cs
new file mode 100644
index 0000000..63a6c53
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValueExtensions.cs
@@ -0,0 +1,28 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public static class CustomFieldValueExtensions
+{
+ public static List ToCustomFieldsValues(this IEnumerable values)
+ {
+ var items = new Dictionary();
+ foreach (var value in values)
+ {
+ if (!items.ContainsKey(value.Id))
+ {
+ var newCustomFieldValues = new CustomFieldValues
+ {
+ Id = value.Id
+ };
+ newCustomFieldValues.Values.AddRange(value.Values);
+
+ items.Add(value.Id, newCustomFieldValues);
+ }
+ }
+
+ var result = new List();
+ result.AddRange(items.Values);
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValues.cs b/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValues.cs
new file mode 100644
index 0000000..e730d9b
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/CustomFieldValues.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class CustomFieldValues
+{
+ [JsonPropertyName("id")]
+ public GeneralIdRef Id { get; set; } = new();
+
+ [JsonPropertyName("values")]
+
+ public List Values { get; set; } = new();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/DeleteRoleSecurityAccess.cs b/e-suite.API.Common/e-suite.API.Common/models/DeleteRoleSecurityAccess.cs
new file mode 100644
index 0000000..e5b76e1
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/DeleteRoleSecurityAccess.cs
@@ -0,0 +1,10 @@
+using eSuite.Core.Miscellaneous;
+using eSuite.Core.Security;
+
+namespace e_suite.API.Common.models;
+
+public class DeleteRoleSecurityAccess
+{
+ public GeneralIdRef RoleId { get; set; } = null!;
+ public List SecurityAccess { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditContact.cs b/e-suite.API.Common/e-suite.API.Common/models/EditContact.cs
new file mode 100644
index 0000000..e706b02
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditContact.cs
@@ -0,0 +1,9 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditContact
+{
+ public GeneralIdRef GeneralIdRef { get; set; } = null!;
+ public string JCard { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditCustomFields.cs b/e-suite.API.Common/e-suite.API.Common/models/EditCustomFields.cs
new file mode 100644
index 0000000..f2c1052
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditCustomFields.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditCustomFields : CustomFieldBase
+{
+ [JsonPropertyName("id")]
+ public GeneralIdRef Id { get; set; } = new GeneralIdRef();
+
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditDomain.cs b/e-suite.API.Common/e-suite.API.Common/models/EditDomain.cs
new file mode 100644
index 0000000..f4d1d2b
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditDomain.cs
@@ -0,0 +1,13 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditDomain : DomainBase
+{
+ [Required]
+ [JsonPropertyName("id")]
+ public GeneralIdRef Id { get; set; } = new GeneralIdRef();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditFormInstance.cs b/e-suite.API.Common/e-suite.API.Common/models/EditFormInstance.cs
new file mode 100644
index 0000000..ff00f47
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditFormInstance.cs
@@ -0,0 +1,36 @@
+using System.Text.Json.Serialization;
+using e_suite.Database.Core.Extensions;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditFormInstance
+{
+ public EditFormInstance()
+ {
+
+ }
+
+ public EditFormInstance(ReadFormInstance readFormInstance)
+ {
+ FormInstanceId = readFormInstance.ToGeneralIdRef()!;
+ TemplateId = readFormInstance.TemplateId;
+ Version = readFormInstance.Version;
+ foreach (var values in readFormInstance.CustomFieldValues)
+ {
+ CustomFieldValues.Add(values.DeepClone());
+ }
+ }
+
+ [JsonPropertyName("formInstanceId")]
+ public GeneralIdRef FormInstanceId { get; set; } = null!;
+
+ [JsonPropertyName("templateId")]
+ public GeneralIdRef TemplateId { get; set; } = null!;
+
+ [JsonPropertyName("version")]
+ public long Version { get; set; }
+
+ [JsonPropertyName("customFieldValues")]
+ public List CustomFieldValues { get; set; } = [];
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditFormTemplate.cs b/e-suite.API.Common/e-suite.API.Common/models/EditFormTemplate.cs
new file mode 100644
index 0000000..b261133
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditFormTemplate.cs
@@ -0,0 +1,18 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditFormTemplate
+{
+ [Required]
+ [JsonPropertyName("id")]
+ public GeneralIdRef Id { get; set; } = new GeneralIdRef();
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("definition")]
+ public string Definition { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditGlossaryItem.cs b/e-suite.API.Common/e-suite.API.Common/models/EditGlossaryItem.cs
new file mode 100644
index 0000000..959dcfe
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditGlossaryItem.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditGlossaryItem : EditableGlossaryItem
+{
+ [JsonPropertyName("id")]
+ public GeneralIdRef Id { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditOrganisation.cs b/e-suite.API.Common/e-suite.API.Common/models/EditOrganisation.cs
new file mode 100644
index 0000000..17eecc4
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditOrganisation.cs
@@ -0,0 +1,12 @@
+using e_suite.Database.Core.Models;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditOrganisation
+{
+ public GeneralIdRef GeneralIdRef { get; set; } = null!;
+ public string Name { get; set; } = string.Empty;
+ public string Address { get; set; } = string.Empty;
+ public OrganisationStatus Status { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditRole.cs b/e-suite.API.Common/e-suite.API.Common/models/EditRole.cs
new file mode 100644
index 0000000..f497c3a
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditRole.cs
@@ -0,0 +1,9 @@
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditRole : RoleBase
+{
+ public GeneralIdRef GeneralIdRef { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditSite.cs b/e-suite.API.Common/e-suite.API.Common/models/EditSite.cs
new file mode 100644
index 0000000..5b5f2dd
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditSite.cs
@@ -0,0 +1,9 @@
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditSite : SiteBase
+{
+ public GeneralIdRef Id { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditSpecification.cs b/e-suite.API.Common/e-suite.API.Common/models/EditSpecification.cs
new file mode 100644
index 0000000..4c6ad7e
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditSpecification.cs
@@ -0,0 +1,9 @@
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditSpecification : SpecificationBase
+{
+ public GeneralIdRef Id { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditSsoProvider.cs b/e-suite.API.Common/e-suite.API.Common/models/EditSsoProvider.cs
new file mode 100644
index 0000000..548a595
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditSsoProvider.cs
@@ -0,0 +1,11 @@
+using System.ComponentModel.DataAnnotations;
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditSsoProvider : SsoProviderBase
+{
+ [Required]
+ public GeneralIdRef Id { get; set; } = new GeneralIdRef();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditUser.cs b/e-suite.API.Common/e-suite.API.Common/models/EditUser.cs
new file mode 100644
index 0000000..bb17d39
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditUser.cs
@@ -0,0 +1,15 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class EditUser : UserBase
+{
+ [JsonPropertyName("id")]
+ [Required]
+ [DefaultValue(null)]
+ public GeneralIdRef? Id { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EditableGlossaryItem.cs b/e-suite.API.Common/e-suite.API.Common/models/EditableGlossaryItem.cs
new file mode 100644
index 0000000..495aefa
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EditableGlossaryItem.cs
@@ -0,0 +1,18 @@
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public abstract class EditableGlossaryItem : GlossaryItemBase
+{
+ [JsonPropertyName("parent")]
+ public GeneralIdRef Parent { get; set; } = null!;
+
+ [JsonPropertyName("childCustomFieldDefinition")]
+ public List ChildCustomFieldDefinition { get; set; } = new();
+
+ [JsonPropertyName("customFieldValues")]
+ public List CustomFieldValues { get; set; } = new();
+
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/EmailActionToken.cs b/e-suite.API.Common/e-suite.API.Common/models/EmailActionToken.cs
new file mode 100644
index 0000000..21fea39
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/EmailActionToken.cs
@@ -0,0 +1,22 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class EmailActionToken
+{
+ [JsonPropertyName("email")]
+ [Required]
+ public string Email { get; set; } = string.Empty;
+
+ [JsonPropertyName("token")]
+ [Required]
+ public Guid Token { get; set; }
+
+ [JsonPropertyName("password")]
+ public string Password { get; set; } = string.Empty;
+
+ [JsonPropertyName("emailActionType")]
+ public EmailUserActionType EmailActionType { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ExceptionLog.cs b/e-suite.API.Common/e-suite.API.Common/models/ExceptionLog.cs
new file mode 100644
index 0000000..ac56376
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ExceptionLog.cs
@@ -0,0 +1,18 @@
+namespace e_suite.API.Common.models;
+
+public class ExceptionLog
+{
+ public long Id { get; set; }
+
+ public string Application { get; set; } = string.Empty;
+
+ public string ExceptionJson { get; set; } = string.Empty;
+
+ public string Message { get; set; } = string.Empty;
+
+ public string StackTrace { get; set; } = string.Empty;
+
+ public string SupportingData { get; set; } = string.Empty;
+
+ public DateTimeOffset OccuredAt { get; set; }
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GetCustomField.cs b/e-suite.API.Common/e-suite.API.Common/models/GetCustomField.cs
new file mode 100644
index 0000000..08a45be
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GetCustomField.cs
@@ -0,0 +1,22 @@
+using eSuite.Core.CustomFields;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class CustomFieldDefinition
+{
+ public long Id { get; set; }
+
+ public string Name { get; set; } = string.Empty;
+
+ public FieldType FieldType { get; set; }
+
+ public string DefaultValue { get; set; } = string.Empty;
+
+ public long MinEntries { get; set; } = 1;
+ public long? MaxEntries { get; set; }
+
+ public IGeneralIdRef? RefElementId { get; set; }
+ public string? Parameters { get; set; }
+ public Guid Guid { get; set; }
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GetDomain.cs b/e-suite.API.Common/e-suite.API.Common/models/GetDomain.cs
new file mode 100644
index 0000000..f977ad4
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GetDomain.cs
@@ -0,0 +1,14 @@
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class GetDomain : DomainBase, IGeneralId
+{
+ [JsonPropertyName("id")]
+ public long Id { get; set; }
+
+ [JsonPropertyName("guid")]
+ public Guid Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GetFormInstance.cs b/e-suite.API.Common/e-suite.API.Common/models/GetFormInstance.cs
new file mode 100644
index 0000000..82380fc
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GetFormInstance.cs
@@ -0,0 +1,17 @@
+using System.Text.Json.Serialization;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class GetFormInstance
+{
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = String.Empty;
+
+ [JsonPropertyName("id")]
+ public GeneralIdRef Id { get; set; } = new GeneralIdRef();
+
+ [JsonPropertyName("versionNumber")]
+ public int VersionNumber { get; set; }
+
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GetFormTemplate.cs b/e-suite.API.Common/e-suite.API.Common/models/GetFormTemplate.cs
new file mode 100644
index 0000000..28ee410
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GetFormTemplate.cs
@@ -0,0 +1,25 @@
+using System.Text.Json.Serialization;
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class GetFormTemplate : IGeneralId
+{
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("definition")]
+ public string Definition { get; set; } = string.Empty;
+
+ [JsonPropertyName("id")]
+ public long Id { get; set; }
+
+ [JsonPropertyName("guid")]
+ public Guid Guid { get; set; }
+
+ [JsonPropertyName("version")]
+ public long Version { get; set; }
+
+ [JsonPropertyName("customFieldDefinitions")]
+ public List CustomFieldDefinitions { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GetMailTemplate.cs b/e-suite.API.Common/e-suite.API.Common/models/GetMailTemplate.cs
new file mode 100644
index 0000000..2b7c420
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GetMailTemplate.cs
@@ -0,0 +1,8 @@
+namespace e_suite.API.Common.models;
+
+public class GetMailTemplate
+{
+ public bool IsOverridden { get; set; }
+ public string Subject { get; set; } = string.Empty;
+ public string TemplateDefinition { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GetRoleSecurityAccess.cs b/e-suite.API.Common/e-suite.API.Common/models/GetRoleSecurityAccess.cs
new file mode 100644
index 0000000..d345d4f
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GetRoleSecurityAccess.cs
@@ -0,0 +1,8 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class GetRoleSecurityAccess : GetSecurityAccess
+{
+ public GeneralIdRef RoleId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GetSecurityAccess.cs b/e-suite.API.Common/e-suite.API.Common/models/GetSecurityAccess.cs
new file mode 100644
index 0000000..8c49d1d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GetSecurityAccess.cs
@@ -0,0 +1,11 @@
+using eSuite.Core.Security;
+
+namespace e_suite.API.Common.models;
+
+public class GetSecurityAccess
+{
+ public SecurityAccess SecurityAccess { get; set; }
+ public string Name { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+ public string GroupName { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GetUser.cs b/e-suite.API.Common/e-suite.API.Common/models/GetUser.cs
new file mode 100644
index 0000000..e3f00ba
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GetUser.cs
@@ -0,0 +1,20 @@
+using e_suite.API.Common.models.@base;
+
+namespace e_suite.API.Common.models;
+
+public class GetUser : UserBase
+{
+ public long Id { get; set; }
+
+ public Guid Guid { get; set; }
+
+ public string DisplayName { get; set; } = string.Empty;
+
+ public string DomainName { get; set; } = string.Empty;
+
+ public DateTimeOffset Created { get; set; }
+
+ public DateTimeOffset LastUpdated { get; set; }
+
+ public bool EmailConfirmed { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/GlossaryItem.cs b/e-suite.API.Common/e-suite.API.Common/models/GlossaryItem.cs
new file mode 100644
index 0000000..585f8ed
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/GlossaryItem.cs
@@ -0,0 +1,26 @@
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class GlossaryItem : GlossaryItemBase, IGeneralId
+{
+ [JsonPropertyName("id")]
+ public long Id { get; set; }
+
+ [JsonPropertyName("guid")]
+ public Guid Guid { get; set; }
+
+ [JsonPropertyName("childCustomFieldDefinition")]
+ public List? ChildCustomFieldDefinition { get; set; }
+
+ [JsonPropertyName("children")]
+ public List? Children { get; set; }
+
+ [JsonPropertyName("customFieldValues")]
+ public List? CustomFieldValues { get; set; }
+
+ [JsonPropertyName("parent")]
+ public GlossaryItem? Parent { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/Login.cs b/e-suite.API.Common/e-suite.API.Common/models/Login.cs
new file mode 100644
index 0000000..07c306d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/Login.cs
@@ -0,0 +1,52 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using e_suite.Nuget.PasswordHasher;
+
+namespace e_suite.API.Common.models;
+
+///
+/// Used to pass details when trying to establish a user connection to the API.
+///
+public class Login : IPassword
+{
+ ///
+ /// users registered e-mail address
+ ///
+ [JsonPropertyName("email")]
+ [DefaultValue("testuser@sun-strategy.com")]
+ [Required]
+ [EmailAddress]
+ [Display(Name = "E-Mail")]
+ public string Email { get; set; } = string.Empty;
+
+ ///
+ /// Users password. Note, this is unencrypted and used only for comparison against the stored hashed password.
+ ///
+ [JsonPropertyName("password")]
+ [DefaultValue("12345 Same as my luggage")]
+ [Display(Name = "Password")]
+ [DataType(DataType.Password)]
+ public string Password { get; set; } = string.Empty;
+
+ ///
+ /// Authenticator security code. Compatible with Google Authenticator.
+ ///
+ [JsonPropertyName("securityCode")]
+ [DefaultValue("")]
+ [Display(Name = "Security Code")]
+ public string SecurityCode { get; set; } = string.Empty;
+
+ ///
+ /// User has lost their TFA device and needs to start the process of removing the TFA from their account. An email will be sent to the user with a link to complete the request.
+ ///
+ [JsonPropertyName("requestTfaRemoval")]
+ [DefaultValue(false)]
+ [Display(Name = "Request 2FA Removal")]
+ public bool RequestTfaRemoval { get; set; } = false;
+
+ [JsonPropertyName("forgotPassword")]
+ [Display(Name = "Forgot Password")]
+ [DefaultValue(false)]
+ public bool ForgotPassword { get; set; } = false;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/LoginResponse.cs b/e-suite.API.Common/e-suite.API.Common/models/LoginResponse.cs
new file mode 100644
index 0000000..d6512f1
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/LoginResponse.cs
@@ -0,0 +1,7 @@
+namespace e_suite.API.Common.models;
+
+public class LoginResponse
+{
+ public LoginResult Result { get; set; }
+ public string Token { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/LoginResult.cs b/e-suite.API.Common/e-suite.API.Common/models/LoginResult.cs
new file mode 100644
index 0000000..273a12d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/LoginResult.cs
@@ -0,0 +1,11 @@
+namespace e_suite.API.Common.models;
+
+public enum LoginResult
+{
+ Failed,
+ EmailNotConfirmed,
+ TwoFactorAuthenticationRemovalRequested,
+ TwoFactorAuthenticationCodeRequired,
+ TwoFactorAuthenticationCodeIncorrect,
+ Success
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/MailTemplateType.cs b/e-suite.API.Common/e-suite.API.Common/models/MailTemplateType.cs
new file mode 100644
index 0000000..ec8ac59
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/MailTemplateType.cs
@@ -0,0 +1,9 @@
+using eSuite.Core.MailService;
+
+namespace e_suite.API.Common.models;
+
+public class MailTemplateType
+{
+ public MailType MailType { get; set; }
+ public string? Description { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/NewGlossaryItem.cs b/e-suite.API.Common/e-suite.API.Common/models/NewGlossaryItem.cs
new file mode 100644
index 0000000..0792d78
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/NewGlossaryItem.cs
@@ -0,0 +1,9 @@
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class NewGlossaryItem : EditableGlossaryItem
+{
+ [JsonPropertyName("guid")]
+ public Guid? Guid { get; set; } = System.Guid.NewGuid();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/NewSequence.cs b/e-suite.API.Common/e-suite.API.Common/models/NewSequence.cs
new file mode 100644
index 0000000..f933383
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/NewSequence.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+
+namespace e_suite.API.Common.models;
+
+public class NewSequence : SequenceBase
+{
+ [JsonPropertyName("Guid")]
+ public System.Guid? Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/NumberFieldParameters.cs b/e-suite.API.Common/e-suite.API.Common/models/NumberFieldParameters.cs
new file mode 100644
index 0000000..9151ec4
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/NumberFieldParameters.cs
@@ -0,0 +1,13 @@
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class NumberFieldParameters
+{
+ [JsonPropertyName("minValue")]
+ public string? MinimumValue { get; set; }
+ [JsonPropertyName("maxValue")]
+ public string? MaximumValue { get; set; }
+ [JsonPropertyName("step")]
+ public string? Step { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/PerformanceReportSummary.cs b/e-suite.API.Common/e-suite.API.Common/models/PerformanceReportSummary.cs
new file mode 100644
index 0000000..da01744
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/PerformanceReportSummary.cs
@@ -0,0 +1,14 @@
+namespace e_suite.API.Common.models;
+
+public class PerformanceReportSummary
+{
+ public long MaxTotalTimeMS;
+ public string Host { get; set; }
+ public string ControllerName { get; set; }
+ public string ActionName { get; set; }
+ public string RequestType { get; set; }
+ public object MinTotalTimeMs { get; set; }
+ public object AvgTotalTimeMs { get; set; }
+ public object SumTotalTimeMs { get; set; }
+ public object Count { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/PostMailTemplate.cs b/e-suite.API.Common/e-suite.API.Common/models/PostMailTemplate.cs
new file mode 100644
index 0000000..8bc4aad
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/PostMailTemplate.cs
@@ -0,0 +1,12 @@
+using eSuite.Core.MailService;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class PostMailTemplate
+{
+ public GeneralIdRef Domain { get; set; } = null!;
+ public MailType MailType { get; set; }
+ public string Subject { get; set; } = string.Empty;
+ public string TemplateDefinition { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadContactDto.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadContactDto.cs
new file mode 100644
index 0000000..88627f4
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadContactDto.cs
@@ -0,0 +1,9 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class ReadContact
+{
+ public GeneralIdRef GeneralIdRef { get; set; } = null!;
+ public string JCard { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadFormInstance.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadFormInstance.cs
new file mode 100644
index 0000000..f23309b
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadFormInstance.cs
@@ -0,0 +1,35 @@
+using System.Text.Json.Serialization;
+using e_suite.Database.Core.Models;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class ReadFormInstance : IGeneralId
+{
+ [JsonPropertyName("id")]
+ public long Id { get; set; }
+
+ [JsonPropertyName("guid")]
+ public Guid Guid { get; set; }
+
+ [JsonPropertyName("templateId")]
+ public GeneralIdRef TemplateId { get; set; } = null!;
+
+ [JsonPropertyName("version")]
+ public long Version { get; set; }
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("definition")]
+ public string Definition { get; set; } = string.Empty;
+
+ [JsonPropertyName("customFieldValues")]
+ public List CustomFieldValues { get; set; } = [];
+
+ [JsonPropertyName("customFieldDefinitions")]
+ public List CustomFieldDefinitions { get; set; } = null!;
+
+ [JsonPropertyName("updatedVersion")]
+ public long UpdatedVersion { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadOrganisation.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadOrganisation.cs
new file mode 100644
index 0000000..9f754f4
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadOrganisation.cs
@@ -0,0 +1,15 @@
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class ReadOrganisation : IGeneralId
+{
+ public long Id { get; set; }
+ public Guid Guid { get; set; }
+
+ public string Name { get; set; } = string.Empty;
+
+ public string Address { get; set; } = string.Empty;
+
+ public OrganisationStatus Status { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadPerformanceReport.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadPerformanceReport.cs
new file mode 100644
index 0000000..e7f2810
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadPerformanceReport.cs
@@ -0,0 +1,24 @@
+using e_suite.Database.Core.Tables.Diagnostics;
+
+namespace e_suite.API.Common.models;
+
+public class ReadPerformanceReport
+{
+ public ReadPerformanceReport()
+ {
+ Host = string.Empty;
+ ControllerName = string.Empty;
+ ActionName = string.Empty;
+ }
+
+ public ReadPerformanceReport(PerformanceReport report)
+ {
+ Host = report.Host;
+ ControllerName = report.ControllerName;
+ ActionName = report.ActionName;
+ }
+
+ public string Host { get; set; }
+ public string ControllerName { get; set; }
+ public string ActionName { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadPerformanceReportSummary.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadPerformanceReportSummary.cs
new file mode 100644
index 0000000..3c9683b
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadPerformanceReportSummary.cs
@@ -0,0 +1,21 @@
+namespace e_suite.API.Common.models;
+
+public class ReadPerformanceReportSummary
+{
+ public ReadPerformanceReportSummary()
+ {
+ Host = string.Empty;
+ ControllerName = string.Empty;
+ ActionName = string.Empty;
+ }
+ public ReadPerformanceReportSummary(PerformanceReportSummary performanceReportSummary)
+ {
+ Host = performanceReportSummary.Host;
+ ControllerName = performanceReportSummary.ControllerName;
+ ActionName = performanceReportSummary.ActionName;
+ }
+
+ public string Host { get; set; }
+ public string ControllerName { get; set; }
+ public string ActionName { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadRole.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadRole.cs
new file mode 100644
index 0000000..0ef4673
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadRole.cs
@@ -0,0 +1,15 @@
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class ReadRole : RoleBase
+{
+ public long Id { get; set; }
+ public Guid Guid { get; set; }
+ public bool IsSuperUser { get; set; }
+ public bool IsAdministrator { get; set; }
+ public bool CanDelete { get; set; } = true;
+ public GeneralIdRef DomainId { get; set; } = null!;
+ public string DomainName { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadSequence.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadSequence.cs
new file mode 100644
index 0000000..addb67e
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadSequence.cs
@@ -0,0 +1,8 @@
+namespace e_suite.API.Common.models;
+
+public class ReadSequence
+{
+ public string Name { get; set; } = String.Empty;
+ public long Id { get; set; }
+ public Guid Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadSite.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadSite.cs
new file mode 100644
index 0000000..9a75f37
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadSite.cs
@@ -0,0 +1,10 @@
+using e_suite.API.Common.models.@base;
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class ReadSite : SiteBase, IGeneralId
+{
+ public long Id { get; set; }
+ public Guid Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadSpecification.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadSpecification.cs
new file mode 100644
index 0000000..a10bc89
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadSpecification.cs
@@ -0,0 +1,11 @@
+using e_suite.API.Common.models.@base;
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class ReadSpecification : SpecificationBase, IGeneralId
+{
+ public long Id { get; set; }
+ public Guid Guid { get; set; }
+
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ReadSsoProvider.cs b/e-suite.API.Common/e-suite.API.Common/models/ReadSsoProvider.cs
new file mode 100644
index 0000000..3fcd42c
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ReadSsoProvider.cs
@@ -0,0 +1,10 @@
+using e_suite.API.Common.models.@base;
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class ReadSsoProvider : SsoProviderBase, IGeneralId
+{
+ public long Id { get; set; }
+ public Guid Guid { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/RoleUser.cs b/e-suite.API.Common/e-suite.API.Common/models/RoleUser.cs
new file mode 100644
index 0000000..b529a76
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/RoleUser.cs
@@ -0,0 +1,11 @@
+using e_suite.Database.Core.Models;
+
+namespace e_suite.API.Common.models;
+
+public class RoleUser : IGeneralId
+{
+ public long Id { get; set; }
+ public Guid Guid { get; set; }
+ public string DisplayName { get; set; } = string.Empty;
+
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/Sequence.cs b/e-suite.API.Common/e-suite.API.Common/models/Sequence.cs
new file mode 100644
index 0000000..dcc6215
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/Sequence.cs
@@ -0,0 +1,15 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using e_suite.API.Common.models.@base;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class Sequence : SequenceBase
+{
+ [JsonPropertyName("id")]
+ [Required]
+ [DefaultValue(null)]
+ public GeneralIdRef? GeneralIdRef { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/TextFieldParameters.cs b/e-suite.API.Common/e-suite.API.Common/models/TextFieldParameters.cs
new file mode 100644
index 0000000..982be44
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/TextFieldParameters.cs
@@ -0,0 +1,9 @@
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class TextFieldParameters
+{
+ [JsonPropertyName("multiLine")]
+ public bool MultiLine { get; set; }
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/models/TwoFactorAuthenticationSettings.cs b/e-suite.API.Common/e-suite.API.Common/models/TwoFactorAuthenticationSettings.cs
new file mode 100644
index 0000000..50bed5a
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/TwoFactorAuthenticationSettings.cs
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class TwoFactorAuthenticationSettings
+{
+ [JsonPropertyName("qrCodeImageUrl")]
+ public string QrCodeImageUrl { get; set; } = null!;
+
+ [JsonPropertyName("manualEntrySetupCode")]
+ public string ManualEntrySetupCode { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/UserAuthenticationDetails.cs b/e-suite.API.Common/e-suite.API.Common/models/UserAuthenticationDetails.cs
new file mode 100644
index 0000000..52a53c5
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/UserAuthenticationDetails.cs
@@ -0,0 +1,11 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class UserAuthenticationDetails
+{
+ public IGeneralIdRef Id { get; set; } = null!;
+ public string Password { get; set; } = string.Empty;
+ public string SecurityCode { get; set; } = string.Empty;
+ public bool UsingTwoFactorAuthentication { get; set; } = false;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/UserProfile.cs b/e-suite.API.Common/e-suite.API.Common/models/UserProfile.cs
new file mode 100644
index 0000000..991421f
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/UserProfile.cs
@@ -0,0 +1,82 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models;
+
+public class UserProfile
+{
+ [JsonPropertyName("firstName")]
+ [Display(Name = "First Name")]
+ public string FirstName { get; set; } = string.Empty;
+
+ [JsonPropertyName("middleNames")]
+ [Display(Name = "Middle Name(s)")]
+ public string MiddleNames { get; set; } = string.Empty;
+
+ [JsonPropertyName("lastName")]
+ [Display(Name = "Last Name")]
+ public string LastName { get; set; } = string.Empty;
+
+ [JsonPropertyName("email")]
+ [Required]
+ [Display(Name = "E-Mail")]
+ public string Email { get; set; } = string.Empty;
+
+ [JsonPropertyName("password")]
+ [Display(Name = "Password")]
+ public string Password { get; set; } = string.Empty;
+
+ [JsonPropertyName("usingTwoFactorAuthentication")]
+ [Required]
+ public bool UsingTwoFactorAuthentication { get; set; }
+
+ [JsonPropertyName("created")]
+ public DateTimeOffset Created { get; set; }
+
+ [JsonPropertyName("twoFactorAuthenticationSettings")]
+ public TwoFactorAuthenticationSettings TwoFactorAuthenticationSettings { get; set; } = null!;
+
+ [JsonPropertyName("securityCode")]
+ public string SecurityCode { get; set; } = null!;
+
+ [JsonPropertyName("domainSsoProviderId")]
+
+ public long? DomainSsoProviderId { get; set; } = null;
+
+ [JsonPropertyName("ssoProviderId")]
+
+ public long? SsoProviderId { get; set; } = null;
+
+ [JsonPropertyName("ssoSubject")]
+
+ public string SsoSubject { get; set; } = string.Empty;
+
+ public Dictionary SsoProviders { get; set; } = null!;
+}
+
+
+public class UpdatedUserProfile
+{
+ [JsonPropertyName("firstName")]
+ public string FirstName { get; set; } = string.Empty;
+
+ [JsonPropertyName("middleNames")]
+ public string MiddleNames { get; set; } = string.Empty;
+
+ [JsonPropertyName("lastName")]
+ public string LastName { get; set; } = string.Empty;
+
+ [JsonPropertyName("email")]
+ [Required]
+ public string Email { get; set; } = string.Empty;
+
+ [JsonPropertyName("password")]
+ public string Password { get; set; } = string.Empty;
+
+ [JsonPropertyName("usingTwoFactorAuthentication")]
+ [Required]
+ public bool UsingTwoFactorAuthentication { get; set; }
+
+ [JsonPropertyName("securityCode")]
+ public string SecurityCode { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/UserRegistration.cs b/e-suite.API.Common/e-suite.API.Common/models/UserRegistration.cs
new file mode 100644
index 0000000..5ac9465
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/UserRegistration.cs
@@ -0,0 +1,29 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class UserRegistration
+{
+ [JsonPropertyName("firstName")]
+ [DefaultValue("Fred")]
+ public string FirstName { get; set; } = string.Empty;
+
+ [JsonPropertyName("middleNames")]
+ [DefaultValue("")]
+ public string MiddleNames { get; set; } = string.Empty;
+
+ [JsonPropertyName("lastName")]
+ [DefaultValue("bloggs")]
+ public string LastName { get; set; } = string.Empty;
+
+ [JsonPropertyName("email")]
+ [DefaultValue("fred.bloggs@sun-strategy.com")]
+ [Required]
+ public string Email { get; set; } = string.Empty;
+
+ [JsonPropertyName("domainId")]
+ public GeneralIdRef? DomainId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/UserRoleIds.cs b/e-suite.API.Common/e-suite.API.Common/models/UserRoleIds.cs
new file mode 100644
index 0000000..40882a2
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/UserRoleIds.cs
@@ -0,0 +1,9 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models;
+
+public class UserRoleIds
+{
+ public GeneralIdRef RoleId { get; set; } = null!;
+ public GeneralIdRef UserId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/ValidatedCustomFieldValue.cs b/e-suite.API.Common/e-suite.API.Common/models/ValidatedCustomFieldValue.cs
new file mode 100644
index 0000000..e6f491e
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/ValidatedCustomFieldValue.cs
@@ -0,0 +1,14 @@
+using e_suite.Database.Core.Tables.CustomFields;
+
+namespace e_suite.API.Common.models;
+
+public class ValidatedCustomFieldValue
+{
+ public CustomField CustomField { get; set; } = null!;
+ public long CustomFieldId { get; set; }
+ public int Index { get; set; }
+ public string Value { get; set; } = string.Empty;
+ public string? DisplayValue { get; set; } = string.Empty;
+ public bool Valid => ValidationErrors.Count == 0;
+ public List ValidationErrors { get; set; } = new List();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/CustomFieldBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/CustomFieldBase.cs
new file mode 100644
index 0000000..721938d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/CustomFieldBase.cs
@@ -0,0 +1,29 @@
+using System.Text.Json.Serialization;
+using eSuite.Core.CustomFields;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models.@base;
+
+public abstract class CustomFieldBase
+{
+ [JsonPropertyName("fieldType")]
+ public FieldType FieldType { get; set; }
+
+ [JsonPropertyName("refElementId")]
+ public GeneralIdRef? RefElementId { get; set; }
+
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("defaultValue")]
+ public string DefaultValue { get; set; } = string.Empty;
+
+ [JsonPropertyName("minEntries")]
+ public long MinEntries { get; set; }
+
+ [JsonPropertyName("maxEntries")]
+ public long? MaxEntries { get; set; }
+
+ [JsonPropertyName("params")]
+ public string Parameters { get; set; } = string.Empty;
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/DomainBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/DomainBase.cs
new file mode 100644
index 0000000..64bf284
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/DomainBase.cs
@@ -0,0 +1,26 @@
+using System.Text.Json.Serialization;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models.@base;
+
+public abstract class DomainBase
+{
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("sunriseHostName")]
+ public string SunriseHostName { get; set; } = string.Empty;
+
+ [JsonPropertyName("sunriseAppId")]
+ public string SunriseAppId { get; set; } = string.Empty;
+
+ [JsonPropertyName("sunriseCategoryId")]
+ public string SunriseCategoryId { get; set; } = string.Empty;
+
+ [JsonPropertyName("ssoProviderId")]
+ public GeneralIdRef? SsoProviderId { get; set; } = null;
+
+ [JsonPropertyName("sigmaId")]
+ public long? SigmaId { get; set; } = null;
+
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/GlossaryItemBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/GlossaryItemBase.cs
new file mode 100644
index 0000000..69b18ca
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/GlossaryItemBase.cs
@@ -0,0 +1,9 @@
+using System.Text.Json.Serialization;
+
+namespace e_suite.API.Common.models.@base;
+
+public abstract class GlossaryItemBase
+{
+ [JsonPropertyName("name")]
+ public string Name { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/RoleBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/RoleBase.cs
new file mode 100644
index 0000000..7a2980a
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/RoleBase.cs
@@ -0,0 +1,6 @@
+namespace e_suite.API.Common.models.@base;
+
+public abstract class RoleBase
+{
+ public string Name { get; set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/SequenceBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/SequenceBase.cs
new file mode 100644
index 0000000..5452dc0
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/SequenceBase.cs
@@ -0,0 +1,75 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using System.Text.RegularExpressions;
+using eSuite.Core.Sequences;
+
+namespace e_suite.API.Common.models.@base;
+
+public abstract class SequenceBase : IValidatableObject
+{
+ private static readonly Regex YearPlaceholderExpression =
+ new(@"\{(YY|YYYY)\}", RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
+ private static readonly Regex MonthPlaceholderExpression =
+ new(@"\{MM\}", RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
+ private static readonly Regex DayPlaceholderExpression =
+ new(@"\{DD\}", RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
+ private static readonly Regex NumericPlaceholderExpression =
+ new(@"\[0+\]", RegexOptions.Compiled | RegexOptions.CultureInvariant);
+
+ [JsonPropertyName("name")]
+ [Required]
+ [DefaultValue("Sequence name")]
+ public string Name { get; set; } = string.Empty;
+
+ [JsonPropertyName("seed")]
+ [Required]
+ [DefaultValue(1)]
+ public long Seed { get; set; }
+
+ [JsonPropertyName("increment")]
+ [Required]
+ [DefaultValue(1)]
+ public long Increment { get; set; }
+
+ [JsonPropertyName("pattern")]
+ [Required]
+ [DefaultValue("[0]")]
+ public string Pattern { get; set; } = "[0]";
+
+ [JsonPropertyName("rolloverType")]
+ [Required]
+ [DefaultValue(Rollover.Continuous)]
+ public Rollover RolloverType { get; set; }
+
+ public IEnumerable Validate(ValidationContext validationContext)
+ {
+ if (RolloverType == Rollover.Day && !DayPlaceholderExpression.IsMatch(Pattern))
+ {
+ yield return new ValidationResult("When the rollover type is set to 'Daily', then the pattern must include either a {DD} placeholder", new string[] { nameof(Pattern) });
+ }
+
+ if (RolloverType == Rollover.Month && !MonthPlaceholderExpression.IsMatch(Pattern))
+ {
+ yield return new ValidationResult("When the rollover type is set to 'Monthly', then the pattern must include either a {MM} placeholder", new string[] { nameof(Pattern) });
+ }
+
+ if (RolloverType == Rollover.Year && !YearPlaceholderExpression.IsMatch(Pattern))
+ {
+ yield return new ValidationResult("When the rollover type is set to 'Annual', then the pattern must include either a {YY} or {YYYY} placeholder", new string[] { nameof(Pattern) });
+ }
+
+ if (!NumericPlaceholderExpression.IsMatch(Pattern))
+ {
+ yield return new ValidationResult("At minimum all patterns must contain one numeric value (i.e. [0])", new string[] { nameof(Pattern) });
+ }
+
+ if (Increment == 0)
+ {
+ yield return new ValidationResult("Increment can not be 0", new string[] { nameof(Increment) });
+ }
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/SiteBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/SiteBase.cs
new file mode 100644
index 0000000..ad0432f
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/SiteBase.cs
@@ -0,0 +1,13 @@
+using e_suite.Database.Core.Models;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models.@base;
+
+public abstract class SiteBase
+{
+ public string Name { get; set; } = string.Empty;
+ public string Address { get; set; } = string.Empty;
+ public OrganisationStatus Status { get; set; }
+ public GeneralIdRef OrganisationId { get; set; } = null!;
+ public long? SigmaId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/SpecificationBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/SpecificationBase.cs
new file mode 100644
index 0000000..08c3d2d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/SpecificationBase.cs
@@ -0,0 +1,11 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models.@base;
+
+public class SpecificationBase
+{
+ public string Name { get; set; } = string.Empty;
+ public GeneralIdRef FormInstanceId { get; set; } = null!;
+ public GeneralIdRef Site { get; set; } = null!;
+ public long? SigmaId { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/SsoProviderBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/SsoProviderBase.cs
new file mode 100644
index 0000000..1be6846
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/SsoProviderBase.cs
@@ -0,0 +1,26 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace e_suite.API.Common.models.@base;
+
+public class SsoProviderBase
+{
+ [Required]
+ public string Name { get; set; } = string.Empty;
+
+ [Required]
+ public string ClientId { get; set; } = string.Empty;
+
+ [Required]
+ public string ClientSecret { get; set; } = string.Empty;
+
+ [Required]
+ public string ValidIssuer { get; set; } = string.Empty;
+
+ [Required]
+ public string AuthorizationEndpoint { get; set; } = string.Empty;
+
+ [Required]
+ public string TokenEndpoint { get; set; } = string.Empty;
+
+ public bool IsPublic { get; set; } = true;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/models/base/UserBase.cs b/e-suite.API.Common/e-suite.API.Common/models/base/UserBase.cs
new file mode 100644
index 0000000..f26b1e5
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/models/base/UserBase.cs
@@ -0,0 +1,16 @@
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.models.@base;
+
+public abstract class UserBase
+{
+ public string FirstName { get; set; } = string.Empty;
+
+ public string LastName { get; set; } = string.Empty;
+
+ public string MiddleNames { get; set; } = string.Empty;
+
+ public string Email { get; set; } = string.Empty;
+
+ public GeneralIdRef Domain { get; set; } = null!;
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IAuditLogRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IAuditLogRepository.cs
new file mode 100644
index 0000000..4bbc694
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IAuditLogRepository.cs
@@ -0,0 +1,9 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit.Tables.Audit;
+
+namespace e_suite.API.Common.repository;
+
+public interface IAuditLogRepository
+{
+ IQueryable GetAuditEntries(AuditParams? auditParams, bool primaryOnly);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IBlockedIPsManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IBlockedIPsManagerRepository.cs
new file mode 100644
index 0000000..0428d0e
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IBlockedIPsManagerRepository.cs
@@ -0,0 +1,11 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using FailedAccessAttempt = e_suite.Database.Core.Tables.Sentinel.FailedAccessAttempt;
+
+namespace e_suite.API.Common.repository;
+public interface IBlockedIPsManagerRepository: IRepository
+{
+ IQueryable GetBlockedIPs(DateTimeOffset earliestAttemptTime);
+
+ Task UnBlockIP(AuditUserDetails auditUserDetails, string ipAddress, CancellationToken cancellationToken);
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IContactsManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IContactsManagerRepository.cs
new file mode 100644
index 0000000..a8a7111
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IContactsManagerRepository.cs
@@ -0,0 +1,16 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Contacts;
+using e_suite.Database.Core.Tables.Printer;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface IContactsManagerRepository : IRepository
+{
+ Task AddContact(AuditUserDetails auditUserDetails, Contact contact, CancellationToken cancellationToken);
+ Task EditContact(AuditUserDetails auditUserDetails, Contact contact, CancellationToken cancellationToken);
+ Task GetContact(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task LinkContactToOrganisation(AuditUserDetails auditUserDetails, OrganisationContact organisationContact, CancellationToken cancellationToken);
+ Task LinkContactToSite(AuditUserDetails auditUserDetails, SiteContact siteContact, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldReferenceObjectRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldReferenceObjectRepository.cs
new file mode 100644
index 0000000..f8be910
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldReferenceObjectRepository.cs
@@ -0,0 +1,51 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Database.Core.Tables.CustomFields;
+using e_suite.Database.Core.Tables.Domain;
+using e_suite.Database.Core.Tables.Forms;
+using e_suite.Database.Core.Tables.Glossaries;
+using eSuite.Core.Miscellaneous;
+using Sequence = e_suite.Database.Core.Tables.Sequences.Sequence;
+
+namespace e_suite.API.Common.repository;
+
+public interface ICustomFieldReferenceObjectRepository
+{
+ Task GetSequenceByGeneralRefIdAsync(IGeneralIdRef idRef, CancellationToken cancellationToken);
+
+ Task GetFormTemplateByGeneralRefIdAsync(IGeneralIdRef idRef, CancellationToken cancellationToken);
+
+ Task GetGlossaryByGeneralRefIdAsync(IGeneralIdRef idRef, CancellationToken cancellationToken);
+
+ Task GetDomainByGeneralRefIdAsync(IGeneralIdRef idRef, CancellationToken cancellationToken);
+
+ Task CreateFormReferenceAsync(AuditUserDetails auditUserDetails, long customFieldId, long formId, CancellationToken cancellationToken);
+ Task CreateGlossaryReferenceAsync(AuditUserDetails auditUserDetails, long customField, long formId, CancellationToken cancellationToken);
+ Task CreateDomainReferenceAsync(AuditUserDetails auditUserDetails, long customField, long formId, CancellationToken cancellationToken);
+ Task CreateSequenceReferenceAsync(AuditUserDetails auditUserDetails, long customField, long formId, CancellationToken cancellationToken);
+
+ Task DeleteFormReferenceAsync(AuditUserDetails auditUserDetails, long customFieldId, CancellationToken cancellationToken);
+ Task DeleteGlossaryReferenceAsync(AuditUserDetails auditUserDetails, long customField, CancellationToken cancellationToken);
+ Task DeleteDomainReferenceAsync(AuditUserDetails auditUserDetails, long customField, CancellationToken cancellationToken);
+ Task DeleteSequenceReferenceAsync(AuditUserDetails auditUserDetails, long customFiled, CancellationToken cancellationToken);
+
+ Task EditFormReferenceAsync(AuditUserDetails auditUserDetails,CustomFieldFormTemplate customFieldFormTemplate, CancellationToken cancellationToken);
+ Task EditGlossaryReferenceAsync(AuditUserDetails auditUserDetails, CustomFieldGlossary customFieldGlossary, CancellationToken cancellationToken);
+ Task EditDomainReferenceAsync(AuditUserDetails auditUserDetails, CustomFieldGlossary customFieldGlossary, CancellationToken cancellationToken);
+ Task EditSequenceReferenceAsync(AuditUserDetails auditUserDetails, CustomFieldSequence customFieldSequence, CancellationToken cancellationToken);
+
+ Task GetFormReferenceAsync(long customFieldId, CancellationToken cancellationToken);
+
+ Task GetSequenceReferenceAsync(long customFieldId, CancellationToken cancellationToken);
+
+ Task GetGlossaryReferenceAsync(long cusotmFieldId, CancellationToken cancellationToken);
+ Task GetNumbersParametersAsync(long customFieldId, CancellationToken cancellationToken);
+ Task CreateCustomFieldNumbersAsync(AuditUserDetails auditUserDetails, CustomFieldNumber customFieldNumber, CancellationToken cancellationToken);
+ Task EditCustomFieldNumbersAsync(AuditUserDetails auditUserDetails, CustomFieldNumber customFieldNumber, NumberFieldParameters numberFieldParameters, CancellationToken cancellationToken);
+ Task DeleteCustomFieldNumbersAsync(AuditUserDetails auditUserDetails, CustomFieldNumber customFieldNumber, CancellationToken cancellationToken);
+
+ Task GetTextsParametersAsync(long customFieldId, CancellationToken cancellationToken);
+ Task CreateCustomFieldTextsAsync(AuditUserDetails auditUserDetails, CustomFieldText customFieldText, CancellationToken cancellationToken);
+ Task EditCustomFieldTextsAsync(AuditUserDetails auditUserDetails, CustomFieldText customFieldText, TextFieldParameters textFieldParameters, CancellationToken cancellationToken);
+ Task DeleteCustomFieldTextsAsync(AuditUserDetails auditUserDetails, CustomFieldText customFieldText, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldRepository.cs
new file mode 100644
index 0000000..7eaac52
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldRepository.cs
@@ -0,0 +1,18 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.CustomFields;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface ICustomFieldRepository : IRepository
+{
+ Task GetByIdAsync(IGeneralIdRef id, CancellationToken cancellationToken);
+
+ Task GetByNameAsync(string name, CancellationToken cancellationToken);
+ Task EditAsync(AuditUserDetails auditUserDetails, CustomField customField, CancellationToken cancellationToken);
+
+ Task CreateAsync(AuditUserDetails auditUserDetails, CustomField customField, CancellationToken cancellationToken);
+
+ IQueryable GetCustomFieldList();
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldValidatorRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldValidatorRepository.cs
new file mode 100644
index 0000000..f5d5eed
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/ICustomFieldValidatorRepository.cs
@@ -0,0 +1,18 @@
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.CustomFields;
+using e_suite.Database.Core.Tables.Domain;
+using e_suite.Database.Core.Tables.Forms;
+using e_suite.Database.Core.Tables.Glossaries;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface ICustomFieldValidatorRepository : IRepository
+{
+ Task FindSequenceIdForCustomField(long customFieldId, CancellationToken cancellationToken);
+ Task GetTemplateByGeneralRefId(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetGlossaryByGeneralRefId(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetDomainByGeneralRefId(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetCustomFieldNumbersAsync(long fieldId, CancellationToken cancellationToken);
+ Task GetCustomFieldTextsAsync(long fieldId, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IDomainRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IDomainRepository.cs
new file mode 100644
index 0000000..7758b91
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IDomainRepository.cs
@@ -0,0 +1,16 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Domain;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface IDomainRepository : IRepository
+{
+ IQueryable GetDomains();
+ Task GetDomainById(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetDomainByName(string domainName, CancellationToken cancellationToken);
+ Task EditDomainAsync(AuditUserDetails auditUserDetails, Domain domain, CancellationToken cancellationToken);
+ Task CreateDomainAsync(AuditUserDetails auditUserDetails, Domain domain, CancellationToken cancellationToken);
+ Task AddAdministratorRoleAsync(AuditUserDetails auditUserDetails, Domain domain, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IExceptionLogRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IExceptionLogRepository.cs
new file mode 100644
index 0000000..72db500
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IExceptionLogRepository.cs
@@ -0,0 +1,10 @@
+using e_suite.Database.Core.Tables.Diagnostics;
+
+namespace e_suite.API.Common.repository;
+
+public interface IExceptionLogRepository
+{
+ Task LogException(ExceptionLog exception, CancellationToken cancellationToken);
+
+ IQueryable GetExceptionLogs();
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IFormRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IFormRepository.cs
new file mode 100644
index 0000000..337c56e
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IFormRepository.cs
@@ -0,0 +1,31 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Helpers;
+using e_suite.Database.Core.Tables.Forms;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface IFormRepository : IRepository
+{
+ //FormTemplate
+ Task GetTemplateAsync(IGeneralIdRef id, CancellationToken cancellationToken);
+ Task GetTemplateWithOutLoadedRefereranceAsync(IGeneralIdRef id, CancellationToken cancellationToken);
+ Task CreateTemplateAsync(AuditUserDetails auditUserDetails, FormTemplate template, CancellationToken cancellationToken);
+ IQueryable GetTemplates();
+ Task DeleteAllVersionsAsync(AuditUserDetails auditUserDetails, IEnumerable formTemplateVersions, CancellationToken cancellationToken);
+ Task EditFormTemplateAsync(AuditUserDetails auditUserDetails,FormTemplate formTemplate, CancellationToken cancellationToken);
+ Task CreateNewFormVersionAsync(AuditUserDetails auditUserDetails, FormTemplateVersion formTemplateVersion, CancellationToken cancellationToken);
+ IQueryable GetAllFormVersions(long templateId);
+ Task GetFormTemplateVersionAsync(IGeneralIdRef id, CancellationToken cancellationToken);
+ Task GetFormTemplateWithSpecificVersionAsync(IGeneralIdRef id, long formTemplateVersionId, CancellationToken cancellationToken);
+
+ //FormInstances
+ Task AddFormInstance(AuditUserDetails auditUserDetails, FormInstance newFormInstance, CancellationToken cancellationToken);
+ Task AddFormInstances(AuditUserDetails auditUserDetails, IEnumerable newFormInstance, CancellationToken cancellationToken);
+ Task AddFormInstanceValues(AuditUserDetails auditUserDetails, List customFieldValues, CancellationToken cancellationToken);
+ Task GetFormInstance(GeneralIdRef createFormInstanceId, bool tracking, CancellationToken cancellationToken);
+ Task> GetFormInstances(IEnumerable generalIdRefs, bool tracking, CancellationToken cancellationToken);
+ Task SaveCustomFieldValues(AuditUserDetails auditUserDetails, Delta delta, CancellationToken cancellationToken);
+ Task> GetFormsContainingFieldValue(IGeneralIdRef customFieldGeneralIdRef, string value, CancellationToken cancellationToken);
+}
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IGlossariesManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IGlossariesManagerRepository.cs
new file mode 100644
index 0000000..6c9c34d
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IGlossariesManagerRepository.cs
@@ -0,0 +1,25 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Audit.Models;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Helpers;
+using e_suite.Database.Core.Tables.CustomFields;
+using e_suite.Database.Core.Tables.Glossaries;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface IGlossariesManagerRepository : IRepository
+{
+ Task AddGlossaryItem(AuditUserDetails auditUserDetails, Glossary glossary, CancellationToken cancellationToken);
+ Task AddGlossaryItems(AuditUserDetails auditUserDetails, IEnumerable items, CancellationToken cancellationToken);
+ Task EditGlossaryItem(AuditUserDetails auditUserDetails, Glossary glossaryItem, CancellationToken cancellationToken);
+ Task FindGlossary(IGeneralIdRef glossaryItem, CancellationToken cancellationToken);
+ Task> FindGlossaryChildren(long parentId, CancellationToken cancellationToken);
+ Task> GetCustomFieldDefinitions(IEnumerable customFieldIdRefs, CancellationToken cancellationToken);
+ Task GetParentGlossary(IGeneralIdRef? glossaryItemParent, CancellationToken cancellationToken);
+ IQueryable GetGlossaryCustomFields(long glossaryId);
+ Task SaveChildCustomFieldDefinitions(AuditUserDetails auditUserDetails, IReadOnlyList removalList, IReadOnlyList additionList, CancellationToken cancellationToken);
+ Task> GetGlossaryCustomValues(IId glossary, CancellationToken cancellationToken);
+ Task SaveCustomFieldValues(AuditUserDetails auditUserDetails, Delta delta, CancellationToken cancellationToken);
+ Task SaveCustomFieldValues(AuditUserDetails auditUserDetails, IEnumerable> deltas, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IMailServiceRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IMailServiceRepository.cs
new file mode 100644
index 0000000..d08e0a7
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IMailServiceRepository.cs
@@ -0,0 +1,10 @@
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Mail;
+using eSuite.Core.MailService;
+
+namespace e_suite.API.Common.repository;
+
+public interface IMailServiceRepository : IRepository
+{
+ Task GetMailTemplate(long domainId, MailType emailRequestEmailType, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IMailTemplatesManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IMailTemplatesManagerRepository.cs
new file mode 100644
index 0000000..01ea25b
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IMailTemplatesManagerRepository.cs
@@ -0,0 +1,15 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Mail;
+using eSuite.Core.MailService;
+
+namespace e_suite.API.Common.repository;
+
+public interface IMailTemplatesManagerRepository : IRepository
+{
+ Task GetMailTemplate(long domainId, MailType emailRequestEmailType, CancellationToken cancellationToken);
+ Task InsertMailTemplateAsync(MailTemplate template, AuditUserDetails auditUserDetails,
+ CancellationToken cancellationToken);
+ Task UpdateMailTemplateAsync(MailTemplate template, AuditUserDetails auditUserDetails,
+ CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IOrganisationsManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IOrganisationsManagerRepository.cs
new file mode 100644
index 0000000..90948e6
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IOrganisationsManagerRepository.cs
@@ -0,0 +1,15 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Printer;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface IOrganisationsManagerRepository : IRepository
+{
+ IQueryable GetOrganisationsList();
+ Task AddOrganisation(AuditUserDetails auditUserDetails, Organisation organisation, CancellationToken cancellationToken);
+ Task DeleteOrganisation(AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task EditOrganisation(AuditUserDetails auditUserDetails, Organisation organisation, CancellationToken cancellationToken);
+ Task FindOrganisation(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IPerformanceManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IPerformanceManagerRepository.cs
new file mode 100644
index 0000000..6c31866
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IPerformanceManagerRepository.cs
@@ -0,0 +1,10 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Core.Tables.Diagnostics;
+
+namespace e_suite.API.Common.repository;
+
+public interface IPerformanceManagerRepository
+{
+ IQueryable GetPerformanceReportSummary();
+ IQueryable GetPerformanceReports(string hostName, string controllerName, string actionName, string requestType);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IRoleManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IRoleManagerRepository.cs
new file mode 100644
index 0000000..5feb5d6
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IRoleManagerRepository.cs
@@ -0,0 +1,23 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Domain;
+using e_suite.Database.Core.Tables.UserManager;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface IRoleManagerRepository : IRepository
+{
+ IQueryable GetRolesList();
+ Task GetRoleById(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetRoleByName(Domain domain, string sequenceName, CancellationToken cancellationToken);
+ Task EditRole(AuditUserDetails auditUserDetails, Role existingSequence, CancellationToken cancellationToken);
+ Task AddRole(AuditUserDetails auditUserDetails, Role existingSequence, CancellationToken cancellationToken);
+ IQueryable GetUserRoles();
+ Task AddUserRole(AuditUserDetails auditUserDetails, Role existingRole, User existingUser, CancellationToken cancellationToken);
+ Task DeleteUserRole(AuditUserDetails auditUserDetails, Role existingRole, User existingUser, CancellationToken cancellationToken);
+ IQueryable GetAccessForRole();
+ Task AddSecurityAccess(AuditUserDetails auditUserDetails, List rollAccessToAdd, CancellationToken cancellationToken);
+ Task DeleteSecurityAccess(AuditUserDetails auditUserDetails, List rollAccessToAdd, CancellationToken cancellationToken);
+ IQueryable GetUserAccess();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/ISentinelRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/ISentinelRepository.cs
new file mode 100644
index 0000000..cdc5484
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/ISentinelRepository.cs
@@ -0,0 +1,10 @@
+using e_suite.Database.Core.Tables.Sentinel;
+
+namespace e_suite.API.Common.repository;
+
+public interface ISentinelRepository
+{
+ Task AddFailedAccessAttempt(FailedAccessAttempt failedLoginAttempt, CancellationToken cancellationToken);
+ Task GetAccessAttemptsSince(string ipAddress, DateTimeOffset earliestAttemptTime, CancellationToken cancellationToken);
+ Task DeleteAccessAttemptsBefore(DateTimeOffset earliestAttemptTime);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/ISequenceManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/ISequenceManagerRepository.cs
new file mode 100644
index 0000000..87a7e17
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/ISequenceManagerRepository.cs
@@ -0,0 +1,15 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Sequences;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface ISequenceManagerRepository : IRepository
+{
+ Task AddSequence(AuditUserDetails auditUserDetails, Sequence sequence, CancellationToken cancellationToken);
+ Task EditSequence(AuditUserDetails auditUserDetails, Sequence sequence, CancellationToken cancellationToken);
+ Task GetSequenceById(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetSequenceByName(string Name, CancellationToken cancellationToken);
+ IQueryable GetSequenceList();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/ISiteManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/ISiteManagerRepository.cs
new file mode 100644
index 0000000..72f156c
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/ISiteManagerRepository.cs
@@ -0,0 +1,16 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Printer;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface ISiteManagerRepository : IRepository
+{
+ IQueryable GetSites();
+ Task GetSite(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetSite(long sigmaId, CancellationToken cancellationToken);
+ Task EditSite(AuditUserDetails auditUserDetails, Site editedSite, CancellationToken cancellationToken);
+ Task CreateSite(AuditUserDetails auditUserDetails, Site newSite, CancellationToken cancellationToken);
+ Task CreateSite(AuditUserDetails auditUserDetails, IEnumerable newSites, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/ISpecificationManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/ISpecificationManagerRepository.cs
new file mode 100644
index 0000000..c5279dd
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/ISpecificationManagerRepository.cs
@@ -0,0 +1,18 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.Printer;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface ISpecificationManagerRepository : IRepository
+{
+ IQueryable GetSpecifications();
+ IQueryable GetSpecificationsFromForms(IEnumerable formIds);
+ Task GetSpecification(GeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task CreateSpecification(AuditUserDetails auditUserDetails, Specification newSpecification, CancellationToken cancellationToken);
+ Task CreateSpecification(AuditUserDetails auditUserDetails, IEnumerable newSpecification, CancellationToken cancellationToken);
+
+ Task EditSpecification(AuditUserDetails auditUserDetails, Specification specification, CancellationToken cancellationToken);
+ Task EditSpecification(AuditUserDetails auditUserDetails, IEnumerable specifications, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/ISsoManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/ISsoManagerRepository.cs
new file mode 100644
index 0000000..4943df8
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/ISsoManagerRepository.cs
@@ -0,0 +1,14 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Tables.UserManager;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface ISsoManagerRepository : IRepository
+{
+ IQueryable GetSsoProviders();
+ Task GetSsoProviderAsync(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task CreateNewSsoProviderAsync(AuditUserDetails auditUserDetails, SsoProvider newSsoProvider, CancellationToken cancellationToken);
+ Task EditNewSsoProviderAsync(AuditUserDetails auditUserDetails, SsoProvider ssoProvider, CancellationToken cancellationToken);
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/e-suite.API.Common/repository/IUserManagerRepository.cs b/e-suite.API.Common/e-suite.API.Common/repository/IUserManagerRepository.cs
new file mode 100644
index 0000000..ca1b7c4
--- /dev/null
+++ b/e-suite.API.Common/e-suite.API.Common/repository/IUserManagerRepository.cs
@@ -0,0 +1,33 @@
+using e_suite.Database.Audit;
+using e_suite.Database.Core;
+using e_suite.Database.Core.Models;
+using e_suite.Database.Core.Tables.Domain;
+using e_suite.Database.Core.Tables.UserManager;
+using eSuite.Core.Miscellaneous;
+
+namespace e_suite.API.Common.repository;
+
+public interface IUserManagerRepository : IRepository
+{
+ Task GetUserByEmail(string loginEmail, CancellationToken cancellationToken);
+ Task GetUserSsoId(long ssoId, string ssoUserId, CancellationToken cancellationToken);
+ Task GetUserByDomainSsoId(long domainSsoId, string ssoUserId, CancellationToken cancellationToken);
+
+ Task AddEmailUserAction(AuditUserDetails auditUserDetails, EmailUserAction emailConfirmation,
+ CancellationToken cancellationToken);
+ Task AddUser(AuditUserDetails auditUserDetails, User user, CancellationToken cancellationToken);
+ Task EditUser(AuditUserDetails auditUserDetails, User user, CancellationToken cancellationToken);
+ Task GetEmailUserAction(Guid token, CancellationToken cancellationToken);
+ Task GetCurrentEmailUserAction(long userId, EmailUserActionType emailUserActionType, CancellationToken cancellationToken);
+ Task DeleteEmailUserAction(AuditUserDetails auditUserDetails, EmailUserAction emailUserAction, CancellationToken cancellationToken);
+ IQueryable GetUsers();
+ Task GetUserById(IGeneralIdRef generalIdRef, CancellationToken cancellationToken);
+ Task GetSsoProviderById(long id, CancellationToken cancellationToken);
+ IQueryable GetSsoProviders();
+ Task SaveSingleUseGuidForUser(SingleUseGuid singleUseGuid, CancellationToken cancellationToken);
+ Task GetUserBySingleUseGuid(Guid guid, CancellationToken cancellationToken);
+ Task DeleteSingleUseGuid(Guid guid, CancellationToken cancellationToken);
+ Task GetDomainById(GeneralIdRef userRegistrationDomainId, CancellationToken cancellationToken);
+ Task DeleteExpiredEmailUserActions();
+ Task DeleteExpiredSingleUseGuids();
+}
\ No newline at end of file
diff --git a/e-suite.API.Common/nuget.config b/e-suite.API.Common/nuget.config
new file mode 100644
index 0000000..e86847e
--- /dev/null
+++ b/e-suite.API.Common/nuget.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e-suite.API/.dockerignore b/e-suite.API/.dockerignore
new file mode 100644
index 0000000..bdca33b
--- /dev/null
+++ b/e-suite.API/.dockerignore
@@ -0,0 +1,25 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/e-suite.API/.gitattributes b/e-suite.API/.gitattributes
new file mode 100644
index 0000000..d7c444c
--- /dev/null
+++ b/e-suite.API/.gitattributes
@@ -0,0 +1 @@
+* -crlf
\ No newline at end of file
diff --git a/e-suite.API/.gitignore b/e-suite.API/.gitignore
new file mode 100644
index 0000000..fa03bff
--- /dev/null
+++ b/e-suite.API/.gitignore
@@ -0,0 +1,201 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+
+[Dd]ebug/
+[Rr]elease/
+x64/
+[Bb]in/
+[Oo]bj/
+
+# New VS2015 folders
+.vs/
+
+# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
+!packages/*/build/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.log
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+.*crunch*.local.xml
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# NuGet Packages Directory
+packages/
+
+# Compare files
+*.orig
+
+# Windows Azure Build Output
+csx
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.pfx
+*.publishsettings
+*.swp
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+# =========================
+# Windows detritus
+# =========================
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Mac crap
+.DS_Store
+
+# TeX files
+Documentation/*.aux
+Documentation/*.out
+Documentation/*.pdf
+Documentation/*.idx
+Documentation/*.toc
+Documentation/*.gz
+
+# image resizer cache
+imagecache/
+*.DotSettings
+
+# TypeScript mapping files
+*.js.map
+
+# Exclude all Typescript-generated JS files
+src/Sunrise.Web.Customer/App/*.js
+src/Sunrise.Web.Customer/App/**/*.js
+src/Sunrise.Web.Customer/App/**/*.js
+src/Sunrise.Web.Customer/Views/**/*.js
+src/Sunrise.Web.Customer/Areas/**/*.js
+src/Sunrise.Web.Support/Views/**/*.js
+
+*.GhostDoc.user.dic
+*.GhostDoc.xml
+
+/TestResult.xml
+*.VisualState.xml
+
+artifacts/
+/src/Sunrise.Web.Customer/Scripts/typings
+
+# Db project
+src/Sunrise.Database/*.jfm
+src/Sunrise.Database/Sunrise.Database.jfm
+/src/Sunrise.Database/*.jfm
+/src/Sunrise.Database/Sunrise.Database.jfm
+/src/Sunrise.Web.Customer/node_modules
+
+# node modules
+node_modules
+.eslintrc
+
+#NCrunch folders
+_NCrunch*/
+src/Sunrise.Web.FrontEnd/.eslintrc.js
+src/Sunrise.Web.FrontEnd/.prettierrc
+src/Sunrise.Web.FrontEnd/.vscode/settings.json
+src/Sunrise.Web.Customer/Content/bundles/vendor.js
diff --git a/e-suite.API/.runsettings b/e-suite.API/.runsettings
new file mode 100644
index 0000000..07b5799
--- /dev/null
+++ b/e-suite.API/.runsettings
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .*\.dll$
+ .*\.exe$
+
+
+ .*moq.dll
+ .*nunit3.testadapter.dll
+
+
+
+ C:\b59fb11c-1611-4562-9a2b-c35719da65d3
+
+
+
+
+
+
+
+ True
+
+ True
+
+ True
+
+ False
+
+ True
+
+ True
+
+ True
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e-suite.API/README.md b/e-suite.API/README.md
new file mode 100644
index 0000000..0ca446a
--- /dev/null
+++ b/e-suite.API/README.md
@@ -0,0 +1,20 @@
+# Introduction
+TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project.
+
+# Getting Started
+TODO: Guide users through getting your code up and running on their own system. In this section you can talk about:
+1. Installation process
+2. Software dependencies
+3. Latest releases
+4. API references
+
+# Build and Test
+TODO: Describe and show how to build your code and run the tests.
+
+# Contribute
+TODO: Explain how other users and developers can contribute to make your code better.
+
+If you want to learn more about creating good readme files then refer the following [guidelines](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops). You can also seek inspiration from the below readme files:
+- [ASP.NET Core](https://github.com/aspnet/Home)
+- [Visual Studio Code](https://github.com/Microsoft/vscode)
+- [Chakra Core](https://github.com/Microsoft/ChakraCore)
\ No newline at end of file
diff --git a/e-suite.API/StartE-Suite.cmd b/e-suite.API/StartE-Suite.cmd
new file mode 100644
index 0000000..6eb3238
--- /dev/null
+++ b/e-suite.API/StartE-Suite.cmd
@@ -0,0 +1,27 @@
+@echo off
+echo running in %~dp0
+
+set DOTNET_ENVIRONMENT=Development
+
+
+
+cd %~dp0\e-suite.Database.Migrator\bin\Debug\net6.0
+e-suite.Database.Migrator.exe
+
+if %ERRORLEVEL% == 0 goto startapi
+
+echo ERROR MIGRATING DATABASE, PLEASE FIX THIS NOW
+pause
+goto end
+
+:startapi
+echo starting api
+
+cd %~dp0\eSuite.API\bin\Debug\net6.0
+start eSuite.API.exe
+
+start "" https://localhost:7066/swagger/index.html
+
+goto end
+
+:end
\ No newline at end of file
diff --git a/e-suite.API/azure-pipelines.yml b/e-suite.API/azure-pipelines.yml
new file mode 100644
index 0000000..65f544b
--- /dev/null
+++ b/e-suite.API/azure-pipelines.yml
@@ -0,0 +1,142 @@
+# Starter pipeline
+# Start with a minimal pipeline that you can customize to build and deploy your code.
+# Add steps that build, run tests, deploy, and more:
+# https://aka.ms/yaml
+
+name: $(Year:yyyy).$(Month).$(DayOfMonth)$(Rev:.r)$(postfix)$(branchName) # NOTE: rev resets when the default retention period expires
+
+trigger:
+ branches:
+ include:
+ - '*'
+
+pool:
+ vmImage: ubuntu-latest # ubuntu-latest - set to windows-latest or another Windows vmImage for Windows builds
+
+variables:
+ solution: '**/*.sln'
+ buildPlatform: 'Any CPU'
+ buildConfiguration: 'Release'
+ containerRegistry: esuite.azurecr.io
+ DOCKER_BUILDKIT: 1
+
+ apiImageName: 'e-suite.api'
+ migratorImageName: 'e-suite.database.migrator'
+
+ apiPublishPath: $(Build.Repository.LocalPath)/build/eSuite.API
+ migratorPublishPath: $(Build.ArtifactStagingDirectory)/build/e-suite.Database.Migrator
+
+ ${{ if eq(variables['Build.SourceBranchName'], 'master') }}:
+ branchName: ''
+ ${{ elseif startsWith(variables['Build.SourceBranch'], 'refs/heads/') }}:
+ branchName: $[ replace(replace(variables['Build.SourceBranch'], 'refs/heads/', ''), '/', '-' ) ]
+ ${{ elseif startsWith(variables['Build.SourceBranch'], 'refs/pull/') }}:
+ branchName: $[ replace(replace(variables['System.PullRequest.TargetBranch'], 'refs/heads/', ''), '/', '-' ) ]
+
+ ${{ if eq(variables['branchName'], '') }}:
+ postfix: ''
+ ${{ else }}:
+ postfix: '-'
+
+steps:
+- task: UseDotNet@2
+ displayName: 'Set .net core version'
+ inputs:
+ version: '9.0.x'
+
+- task: DotNetCoreCLI@2
+ displayName: 'Nuget Restore'
+ inputs:
+ command: 'restore'
+ feedsToUse: config
+ projects: '**/*.csproj'
+ nugetConfigPath: nuget.config
+
+- task: DotNetCoreCLI@2
+ inputs:
+ command: 'build'
+ arguments: '--configuration $(buildConfiguration)'
+ displayName: 'dotnet build $(buildConfiguration)'
+
+- task: DotNetCoreCLI@2
+ displayName: 'Run Tests'
+ inputs:
+ command: test
+ projects: '**/*Tests/*.csproj'
+ arguments: '--configuration $(buildConfiguration) --collect "Code coverage" --settings .runsettings'
+
+- task: DotNetCoreCLI@2
+ displayName: 'Publish e-suite.API'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ command: publish
+ projects: 'eSuite.API/eSuite.API.csproj'
+ publishWebProjects: false
+ arguments: -c $(BuildConfiguration) -o build
+ zipAfterPublish: false
+
+- task: Docker@2
+ displayName: Build e-suite API Image
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ command: build
+ repository: $(apiImageName)
+ buildContext: $(apiPublishPath)
+ Dockerfile: $(apiPublishPath)/Dockerfile.Azure
+ containerRegistry: |
+ $(containerRegistry)
+ tags: |
+ $(Build.BuildNumber)
+
+- task: DotNetCoreCLI@2
+ displayName: 'Publish e-suite.Database.Migrator'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ command: publish
+ projects: 'e-suite.Database.Migrator/e-suite.Database.Migrator.csproj'
+ publishWebProjects: false
+ arguments: '--configuration $(BuildConfiguration) --output $(migratorPublishPath)'
+ zipAfterPublish: false
+
+- powershell: |
+ Write-Host "Show all folder content"
+ Get-ChildItem -Path $(migratorPublishPath)\*.* -Recurse -Force | % { $_.FullName }
+ errorActionPreference: continue
+ displayName: 'PowerShell Script List folder structure'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ continueOnError: true
+
+- task: Docker@2
+ displayName: Build e-suite Database Migrator image
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ command: build
+ repository: $(migratorImageName)
+ buildContext: $(migratorPublishPath)/e-suite.Database.Migrator
+ Dockerfile: $(migratorPublishPath)/e-suite.Database.Migrator/Dockerfile.Azure
+ containerRegistry: |
+ $(containerRegistry)
+ tags: |
+ $(Build.BuildNumber)
+
+- task: Docker@2
+ displayName: Push e-suite API Image
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ command: push
+ repository: $(apiImageName)
+ containerRegistry: |
+ $(containerRegistry)
+ tags: |
+ $(Build.BuildNumber)
+
+- task: Docker@2
+ displayName: Push e-suite Database Migrator image
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
+ inputs:
+ command: push
+ repository: $(migratorImageName)
+ containerRegistry: |
+ $(containerRegistry)
+ tags: |
+ $(Build.BuildNumber)
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/.dockerignore b/e-suite.API/e-suite.Database.Migrator/.dockerignore
new file mode 100644
index 0000000..bdca33b
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/.dockerignore
@@ -0,0 +1,25 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Dockerfile b/e-suite.API/e-suite.Database.Migrator/Dockerfile
new file mode 100644
index 0000000..150a431
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Dockerfile
@@ -0,0 +1,21 @@
+#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
+
+FROM mcr.microsoft.com/dotnet/runtime:9.0 AS base
+WORKDIR /app
+
+FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
+WORKDIR /src
+COPY ["nuget.config", "."]
+COPY ["e-suite.Database.Migrator/e-suite.Database.Migrator.csproj", "e-suite.Database.Migrator/"]
+RUN dotnet restore "e-suite.Database.Migrator/e-suite.Database.Migrator.csproj"
+COPY . .
+WORKDIR "/src/e-suite.Database.Migrator"
+RUN dotnet build "e-suite.Database.Migrator.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "e-suite.Database.Migrator.csproj" -c Release -o /app/publish /p:UseAppHost=false
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "e-suite.Database.Migrator.dll"]
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Dockerfile.Azure b/e-suite.API/e-suite.Database.Migrator/Dockerfile.Azure
new file mode 100644
index 0000000..dd29230
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Dockerfile.Azure
@@ -0,0 +1,7 @@
+#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
+
+FROM mcr.microsoft.com/dotnet/aspnet:9.0 as base
+ENV DOTNET_ENVIRONMENT=Development
+WORKDIR /app
+COPY . .
+ENTRYPOINT ["dotnet", "e-suite.Database.Migrator.dll"]
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Migrations/ScriptParser.cs b/e-suite.API/e-suite.Database.Migrator/Migrations/ScriptParser.cs
new file mode 100644
index 0000000..f63bd48
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Migrations/ScriptParser.cs
@@ -0,0 +1,49 @@
+namespace e_suite.Database.Migrator.Migrations;
+
+public class ScriptParser
+{
+ public static string GetScriptText(ScriptType scriptType, string databaseName)
+ {
+ var scriptFileName = Path.Combine(AppContext.BaseDirectory, "Scripts", GetScriptFileName(scriptType));
+
+ if (!File.Exists(scriptFileName))
+ return string.Empty;
+
+ var fileContents = File.ReadAllText(scriptFileName).Trim();
+
+ return ParseScript(fileContents, databaseName);
+ }
+
+ private static string ParseScript(string fileContents, string databaseName)
+ {
+ if (string.IsNullOrWhiteSpace(fileContents))
+ return fileContents;
+
+ var parameterDictionary = new Dictionary
+ {
+ { "SQL_APPLICATION_USER", Environment.GetEnvironmentVariable("SQL_APPLICATION_USER") ?? $"{databaseName}ApplicationUser" },
+ { "SQL_APPLICATION_PASSWORD", Environment.GetEnvironmentVariable("SQL_APPLICATION_PASSWORD") ?? "Plz change me on live, thank you" },
+ { "SQL_DATABASE", Environment.GetEnvironmentVariable("SQL_DATABASE") ?? databaseName },
+ { "SQL_SERVER", Environment.GetEnvironmentVariable("SQL_SERVER") ?? string.Empty },
+ };
+
+ var parsedContents = fileContents;
+ foreach (KeyValuePair keyValuePair in parameterDictionary)
+ {
+ parsedContents = parsedContents.Replace("$(" + keyValuePair.Key + ")", keyValuePair.Value);
+ }
+
+ return parsedContents;
+ }
+
+ private static string GetScriptFileName(ScriptType scriptType)
+ {
+ return scriptType switch
+ {
+ ScriptType.resetDatabase => "ResetDatabaseScript.sql",
+ ScriptType.preMigration => "PreMigrationScript.sql",
+ ScriptType.postMigration => "PostMigrationScript.sql",
+ _ => throw new NotImplementedException()
+ };
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Migrations/ScriptType.cs b/e-suite.API/e-suite.Database.Migrator/Migrations/ScriptType.cs
new file mode 100644
index 0000000..589af31
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Migrations/ScriptType.cs
@@ -0,0 +1,8 @@
+namespace e_suite.Database.Migrator.Migrations;
+
+public enum ScriptType
+{
+ resetDatabase,
+ preMigration,
+ postMigration
+}
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Program.cs b/e-suite.API/e-suite.Database.Migrator/Program.cs
new file mode 100644
index 0000000..00dc6ea
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Program.cs
@@ -0,0 +1,39 @@
+// See https://aka.ms/new-console-template for more information
+
+using e_suite.API.Common.extensions;
+using e_suite.Database.Migrator.Migrations;
+using e_suite.Database.Migrator.SqlWrapper;
+using e_suite.Database.SqlServer;
+using eSuite.Core.Clock;
+using Microsoft.EntityFrameworkCore;
+
+Console.WriteLine($"Environment: {Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT")}");
+
+var configuration = ESuiteDatabaseExtension.BuildConfiguration();
+
+var databaseName = configuration.GetConfigValue("SQL_DATABASE", "database:databaseName", "esuite")!;
+var resetDatabaseName = configuration.GetConfigValue("RESET_DATABASE", "database:resetDatabase", false);
+
+var connectionString = ESuiteDatabaseExtension.BuildConnectionString(configuration);
+
+if (resetDatabaseName)
+ PrePostScriptRunner.RunScripts(connectionString, databaseName, ScriptType.resetDatabase);
+
+PrePostScriptRunner.RunScripts(connectionString, databaseName, ScriptType.preMigration);
+
+Console.WriteLine("Migrating database");
+try
+{
+ var database = await ESuiteDatabaseExtension.CreateDatabase(new UtcClock());
+ database.Database.Migrate();
+
+ Console.WriteLine("Database migration completed");
+}
+catch (Exception e)
+{
+ Console.WriteLine("Database migration failed");
+ Console.WriteLine(e);
+ throw;
+}
+
+PrePostScriptRunner.RunScripts(connectionString, databaseName, ScriptType.postMigration);
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Properties/launchSettings.json b/e-suite.API/e-suite.Database.Migrator/Properties/launchSettings.json
new file mode 100644
index 0000000..d8f23bc
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Properties/launchSettings.json
@@ -0,0 +1,13 @@
+{
+ "profiles": {
+ "e-suite.Database.Migrator": {
+ "commandName": "Project",
+ "environmentVariables": {
+ "DOTNET_ENVIRONMENT": "Development"
+ }
+ },
+ "Docker": {
+ "commandName": "Docker"
+ }
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Scripts/PostMigrationScript.sql b/e-suite.API/e-suite.Database.Migrator/Scripts/PostMigrationScript.sql
new file mode 100644
index 0000000..71dc1cd
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Scripts/PostMigrationScript.sql
@@ -0,0 +1 @@
+ALTER USER [$(SQL_APPLICATION_USER)] WITH PASSWORD=N'$(SQL_APPLICATION_PASSWORD)', DEFAULT_SCHEMA=[dbo]
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Scripts/PreMigrationScript.sql b/e-suite.API/e-suite.Database.Migrator/Scripts/PreMigrationScript.sql
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Scripts/PreMigrationScript.sql
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/Scripts/ResetDatabaseScript.sql b/e-suite.API/e-suite.Database.Migrator/Scripts/ResetDatabaseScript.sql
new file mode 100644
index 0000000..435428f
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/Scripts/ResetDatabaseScript.sql
@@ -0,0 +1,52 @@
+DECLARE @sql NVARCHAR(max)=''
+
+--dropping views
+SET @sql = '';
+
+SELECT @sql += ' Drop view ' + QUOTENAME(s.NAME) + '.' + QUOTENAME(v.NAME) + '; '
+FROM sys.views v
+JOIN sys.schemas s
+ ON v.[schema_id] = s.[schema_id]
+WHERE v.is_ms_shipped = 0
+
+EXEC sp_executesql @sql
+
+--dropping foreign keys
+SET @sql = '';
+SELECT @sql += 'Alter table ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' drop constraint ' + QUOTENAME(fk.name) +'; '
+FROM sys.foreign_keys AS fk
+JOIN sys.tables AS t
+ ON t.object_id = fk.parent_object_id
+JOIN sys.schemas AS s
+ ON s.schema_id = t.schema_id
+
+Exec sp_executesql @sql
+
+--dropping prinary and unique keys
+SET @sql = '';
+SELECT @sql += 'Alter table ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' drop constraint ' + QUOTENAME(kc.name) +'; '
+FROM sys.key_constraints AS kc
+JOIN sys.tables AS t
+ ON t.object_id = kc.parent_object_id
+JOIN sys.schemas AS s
+ ON s.schema_id = t.schema_id
+
+EXEC sp_executesql @sql
+
+--dropping tables
+SET @sql = '';
+SELECT @sql += ' Drop table ' + QUOTENAME(s.NAME) + '.' + QUOTENAME(t.NAME) + '; '
+FROM sys.tables AS t
+JOIN sys.schemas AS s
+ ON t.[schema_id] = s.[schema_id]
+WHERE t.type = 'U'
+
+EXEC sp_executesql @sql
+
+--removing application user
+SET @sql = '';
+SELECT @sql += 'drop user ' + QUOTENAME(p.name)
+FROM sys.database_principals AS p
+WHERE p.authentication_type = 2
+
+EXEC sp_executesql @sql
diff --git a/e-suite.API/e-suite.Database.Migrator/SqlWrapper/ISqlWrapper.cs b/e-suite.API/e-suite.Database.Migrator/SqlWrapper/ISqlWrapper.cs
new file mode 100644
index 0000000..a7d276d
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/SqlWrapper/ISqlWrapper.cs
@@ -0,0 +1,85 @@
+using System.Data;
+using System.Xml;
+using System.Xml.Linq;
+
+namespace e_suite.Database.Migrator.SqlWrapper;
+
+public interface ISqlWrapper : IDisposable
+{
+ ///
+ /// Name of the stored procedure to call.
+ /// Note: can use either CommandName or the CommandText, but not both at the same time.
+ ///
+ string CommandName { get; set; }
+
+ ///
+ /// Adhoc SQL text.
+ /// Note: can use either CommandName or the CommandText, but not both at the same time.
+ ///
+ string CommandText { get; set; }
+
+ void AddParameter(string name, SqlDbType type, object value);
+
+ ///
+ /// Adds a new parameter to the stored procedure
+ ///
+ /// name of the parameter. For example @test the @ symbol is optional
+ /// database type of the parameter
+ /// Specifies if this is an input or output parameter
+ /// actual value for the parameter
+ void AddParameter(string name, SqlDbType type, ParameterDirection direction, object value);
+
+ ///
+ /// Retrieves the current value of a parameter
+ ///
+ /// name of the parameter that you need the value for
+ /// either the value of the named parameter, or null if it does not exist
+ object? GetParamValue(string name);
+
+ ///
+ /// Removes all parameters from the internal parameter list.
+ ///
+ void Clear();
+
+
+ ///
+ /// Execute the stored procedure without the expectation of any result sets being returned.
+ /// note: output parameters will be set when the method completes.
+ ///
+ void Execute();
+
+ ///
+ /// Retrieves the procedures return value
+ ///
+ /// the return value from the procedure call
+ object? GetReturnValue();
+
+ ///
+ /// Execute stored procedure with return an XML reader.
+ ///
+ ///
+ XmlReader ExecuteXmlReader();
+
+ ///
+ /// Execute the stored procedure with the result returned as an XmlDocument
+ ///
+ /// XML document containing the result of the stored procedure call
+ XmlDocument ExecuteXmlDocument();
+
+ ///
+ /// Execute the stored procedure with the result returned as an XDcoument
+ ///
+ ///
+ XDocument ExecuteXDocument();
+
+ IDataReader ExecuteReader();
+
+
+ bool IsDisposed { get; }
+ int ParamCount { get; }
+
+ ///
+ /// Timeout for sql commands. Default is 30 seconds. 0 means no limit.
+ ///
+ int Timeout { get; set; }
+}
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/SqlWrapper/PrePostScriptRunner.cs b/e-suite.API/e-suite.Database.Migrator/SqlWrapper/PrePostScriptRunner.cs
new file mode 100644
index 0000000..a18f27e
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/SqlWrapper/PrePostScriptRunner.cs
@@ -0,0 +1,40 @@
+using e_suite.Database.Migrator.Migrations;
+
+namespace e_suite.Database.Migrator.SqlWrapper;
+
+public static class PrePostScriptRunner
+{
+ public static void RunScripts(string connectionString, string databaseName, ScriptType scriptType)
+ {
+ Console.WriteLine($"Running {scriptType} scripts");
+ try
+ {
+ var parsedScript = ScriptParser.GetScriptText(scriptType, databaseName);
+ if (parsedScript != string.Empty)
+ {
+ try
+ {
+ using var sqlWrapper = new SqlWrapper(connectionString);
+ sqlWrapper.CommandText = parsedScript;
+ sqlWrapper.Execute();
+ Console.WriteLine($"{scriptType} scripts completed");
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"{scriptType} scripts skipped - Unable to connect to sql server");
+ Console.WriteLine(e);
+ }
+ }
+ else
+ {
+ Console.WriteLine($"{scriptType} scripts skipped - no content");
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"{scriptType} scripts failed");
+ Console.WriteLine(e);
+ throw;
+ }
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/SqlWrapper/SecureSqlParameterExtensions.cs b/e-suite.API/e-suite.Database.Migrator/SqlWrapper/SecureSqlParameterExtensions.cs
new file mode 100644
index 0000000..9dac8bc
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/SqlWrapper/SecureSqlParameterExtensions.cs
@@ -0,0 +1,171 @@
+using System.Data;
+using System.Runtime.InteropServices;
+using System.Security;
+using Microsoft.Data.SqlClient;
+
+namespace e_suite.Database.Migrator.SqlWrapper;
+
+public static class SecureSqlParameterExtensions
+{
+ [DllImport("kernel32.dll", EntryPoint = "CopyMemory")]
+ private static extern void CopyMemory(IntPtr dest, IntPtr src, IntPtr count);
+ [DllImport("kernel32.dll", EntryPoint = "RtlZeroMemory")]
+ private static extern void ZeroMemory(IntPtr ptr, IntPtr count);
+
+ ///
+ /// You must dispose the return value as soon as SqlCommand.Execute* is called.
+ ///
+ public static IDisposable AddSecure(this SqlParameterCollection collection, string name, SecureString secureString)
+ {
+ var value = new SecureStringParameterValue(secureString);
+ collection.Add(name, SqlDbType.NVarChar).Value = value;
+ return value;
+ }
+
+ private sealed class SecureStringParameterValue : IConvertible, IDisposable
+ {
+ private readonly SecureString? _secureString;
+ private int _length;
+ private string? _insecureManagedCopy;
+ private GCHandle _insecureManagedCopyGcHandle;
+
+ public SecureStringParameterValue(SecureString secureString)
+ {
+ _secureString = secureString;
+ }
+
+ #region IConvertible
+
+ public TypeCode GetTypeCode()
+ {
+ return TypeCode.String;
+ }
+
+ public string ToString(IFormatProvider? provider)
+ {
+ if (_insecureManagedCopy != null) return _insecureManagedCopy;
+ if (_secureString == null || _secureString.Length == 0) return string.Empty;
+
+ // We waited till the last possible minute.
+
+ // Here's the plan:
+ // 1. Create a new managed string initialized to zero
+ // 2. Pin the managed string so the GC leaves it alone
+ // 3. Copy the contents of the SecureString into the managed string
+ // 4. Use the string as a SqlParameter
+ // 5. Zero the managed string after Execute* is called and free the GC handle
+
+ _length = _secureString.Length;
+ _insecureManagedCopy = new string('\0', _length);
+ _insecureManagedCopyGcHandle = GCHandle.Alloc(_insecureManagedCopy, GCHandleType.Pinned); // Do not allow the GC to move this around and leave copies behind
+
+ try
+ {
+ // This is the only way to read the contents, sadly.
+ // SecureStringToBSTR picks where to put it, so we have to copy it from there and zerofree the unmanaged copy as fast as possible.
+ IntPtr insecureUnmanagedCopy = Marshal.SecureStringToBSTR(_secureString);
+ try
+ {
+ CopyMemory(_insecureManagedCopyGcHandle.AddrOfPinnedObject(), insecureUnmanagedCopy, (IntPtr)(_length * 2));
+ }
+ finally
+ {
+ if (insecureUnmanagedCopy != IntPtr.Zero) Marshal.ZeroFreeBSTR(insecureUnmanagedCopy);
+ }
+
+ // Now the string managed string has the contents in the clear.
+ return _insecureManagedCopy;
+ }
+ catch
+ {
+ Dispose();
+ throw;
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_insecureManagedCopy == null) return;
+ _insecureManagedCopy = null;
+ ZeroMemory(_insecureManagedCopyGcHandle.AddrOfPinnedObject(), (IntPtr)(_length * 2));
+ _insecureManagedCopyGcHandle.Free();
+ }
+
+ public bool ToBoolean(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public char ToChar(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public sbyte ToSByte(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public byte ToByte(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public short ToInt16(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ushort ToUInt16(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public int ToInt32(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public uint ToUInt32(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public long ToInt64(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ulong ToUInt64(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public float ToSingle(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public double ToDouble(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public decimal ToDecimal(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public DateTime ToDateTime(IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public object ToType(Type conversionType, IFormatProvider? provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/SqlWrapper/SqlWrapper.cs b/e-suite.API/e-suite.Database.Migrator/SqlWrapper/SqlWrapper.cs
new file mode 100644
index 0000000..0ad1068
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/SqlWrapper/SqlWrapper.cs
@@ -0,0 +1,313 @@
+using Microsoft.Data.SqlClient;
+using System.Data;
+using System.Security;
+using System.Xml.Linq;
+using System.Xml;
+
+namespace e_suite.Database.Migrator.SqlWrapper;
+
+public class SqlWrapper : ISqlWrapper
+{
+ ///
+ /// contains the details of parameter including the value. Settings the value to null or DBNull.Value will result in a DBNull.Value being passed with the stored procedure call.
+ ///
+ private struct SqlParam
+ {
+ public string Name;
+ public SqlDbType Type;
+ public ParameterDirection Direction;
+ public object Value;
+ }
+
+ private const string ReturnValueParamName = "_ReturnValue";
+ private readonly IDbConnection _sqlConn;
+ private readonly List _sqlParams;
+ private string _commandName = string.Empty;
+ private string _commandText = string.Empty;
+
+ public string CommandName
+ {
+ get => _commandName;
+ set
+ {
+ if (_commandName != value)
+ {
+ _commandName = value;
+ if (!string.IsNullOrEmpty(_commandText))
+ {
+ _commandText = string.Empty;
+ }
+ }
+ }
+ }
+
+ public string CommandText
+ {
+ get => _commandText;
+ set
+ {
+ if (_commandText != value)
+ {
+ _commandText = value;
+ if (!string.IsNullOrEmpty(_commandName))
+ {
+ _commandName = string.Empty;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Creates an instance of the SqlCommand. Wraps setting everything needed to make a stored procedure call based on the parameters supplied.
+ ///
+ /// Prepared SqlCommand object ready for execution.
+ /// FxCop warning has been deliberately suppressed as the CommandText is only allowed to be a stored procedure name.
+ /// This is ensured by the hard coding of the CommandType.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities")]
+ private SqlCommand PrepareSqlCommand()
+ {
+ SqlCommand sqlCommand;
+ SqlCommand? tempSqlCommand = null;
+
+ try
+ {
+ tempSqlCommand = new SqlCommand
+ {
+ CommandTimeout = Timeout,
+ Connection = _sqlConn as SqlConnection
+ };
+
+ if (!string.IsNullOrEmpty(CommandName))
+ {
+ tempSqlCommand.CommandType = CommandType.StoredProcedure;
+ tempSqlCommand.CommandText = CommandName;
+ }
+ else if (!string.IsNullOrEmpty(CommandText))
+ {
+ tempSqlCommand.CommandType = CommandType.Text;
+ tempSqlCommand.CommandText = CommandText;
+ }
+
+ foreach (var sqlParam in _sqlParams)
+ {
+ if (sqlParam.Value is SecureString sqlParamValue)
+ {
+ tempSqlCommand.Parameters.AddSecure(sqlParam.Name, sqlParamValue);
+ }
+ else
+ {
+ var sqlParameter = tempSqlCommand.Parameters.Add(sqlParam.Name, sqlParam.Type);
+
+ if (sqlParam.Type == SqlDbType.VarChar)
+ {
+ sqlParameter.Size = -1;
+ }
+ sqlParameter.Direction = sqlParam.Direction;
+ sqlParameter.Value = sqlParam.Value;
+ }
+ }
+
+ sqlCommand = tempSqlCommand;
+ }
+ catch
+ {
+ tempSqlCommand?.Dispose();
+ throw;
+ }
+
+ return sqlCommand;
+ }
+
+ ///
+ /// retrieves the output parameters putting each into their respective SqlParam contained in the sqlParams list.
+ ///
+ /// the SqlCommand object after execution so that the output parameters may be retrieved.
+ private void RetrieveOutputParams(SqlCommand sqlCommand)
+ {
+ ParameterDirection[] outParamDirections = { ParameterDirection.InputOutput, ParameterDirection.Output, ParameterDirection.ReturnValue };
+
+ foreach (SqlParameter sqlParameter in sqlCommand.Parameters)
+ {
+ if (outParamDirections.Contains(sqlParameter.Direction))
+ {
+ for (int i = 0; i < _sqlParams.Count; i++)
+ {
+ SqlParam sqlParam = _sqlParams[i];
+
+ if (sqlParam.Name == sqlParameter.ParameterName)
+ {
+ sqlParam.Value = sqlParameter.Value;
+ _sqlParams[i] = sqlParam; //will this be needed?
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// class constructor. this will create and immediately open the database connection.
+ ///
+ public SqlWrapper(string connectionString) //constructor
+ {
+ _sqlConn = new SqlConnection(connectionString);
+ _sqlConn.Open();
+ _sqlParams = new List();
+ }
+
+ public SqlWrapper(IDbConnection sqlConnection) //constructor
+ {
+ _sqlConn = sqlConnection;
+ _sqlConn.Open();
+ _sqlParams = new List();
+ }
+
+ ///
+ /// Overrideable Dispose method. This will be called when the object is destroyed, either by ending a uses clause or by the garbage collector.
+ ///
+ /// states that this was called by the garbage collector
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!IsDisposed)
+ {
+ if (disposing)
+ {
+ _sqlConn.Close();
+ }
+ }
+ IsDisposed = true;
+ }
+
+ ///
+ /// object destructor, will call the overridable Dispose method.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ public bool IsDisposed { get; private set; }
+
+ ///
+ /// Removes all parameters from the internal parameter list.
+ ///
+ public void Clear()
+ {
+ _sqlParams.Clear();
+ }
+
+ public int ParamCount => _sqlParams.Count;
+
+ public int Timeout { get; set; } = 30;
+
+ public void AddParameter(string name, SqlDbType type, object value)
+ {
+ AddParameter(name, type, ParameterDirection.Input, value);
+ }
+
+ ///
+ /// Adds a new parameter to the stored procedure
+ ///
+ /// name of the parameter. For example @test the @ symbol is optional
+ /// database type of the parameter
+ /// Specifies if this is an input or output parameter
+ /// actual value for the parameter
+ public void AddParameter(string name, SqlDbType type, ParameterDirection direction, object? value)
+ {
+ if (value == null)
+ {
+ value = DBNull.Value;
+ }
+
+ SqlParam sqlParam = new SqlParam
+ {
+ Name = name,
+ Type = type,
+ Direction = direction,
+ Value = value
+ };
+
+ _sqlParams.Add(sqlParam);
+ }
+
+ ///
+ /// Retrieves the current value of a parameter
+ ///
+ /// name of the parameter that you need the value for
+ /// either the value of the named parameter, or null if it does not exist
+ public object? GetParamValue(string name)
+ {
+ foreach (var sqlParam in _sqlParams)
+ {
+ if (sqlParam.Name == name)
+ {
+ return sqlParam.Value;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Retrieves the procedures return value
+ ///
+ /// the return value from the procedure call
+ public object? GetReturnValue()
+ {
+ return GetParamValue(ReturnValueParamName);
+ }
+
+ ///
+ /// Execute the stored procedure without the expectation of any result sets being returned.
+ /// note: output parameters will be set when the method completes.
+ ///
+ public void Execute()
+ {
+ AddParameter(ReturnValueParamName, SqlDbType.Int, ParameterDirection.ReturnValue, null);
+ using (SqlCommand sqlCommand = PrepareSqlCommand())
+ {
+ sqlCommand.ExecuteNonQuery();
+ RetrieveOutputParams(sqlCommand);
+ }
+ }
+
+
+ public XmlReader ExecuteXmlReader()
+ {
+ using (SqlCommand sqlCommand = PrepareSqlCommand())
+ {
+ return sqlCommand.ExecuteXmlReader();
+ }
+ }
+
+ ///
+ /// Execute the stored procedure with the expection of an XML Stream result set.
+ ///
+ /// XML document containing the result of the stored procedure call
+ public XmlDocument ExecuteXmlDocument()
+ {
+ using (XmlReader xReader = ExecuteXmlReader())
+ {
+ XmlDocument xmlDocument = new XmlDocument();
+ xmlDocument.Load(xReader);
+ return xmlDocument;
+ }
+ }
+
+ public XDocument ExecuteXDocument()
+ {
+ using (XmlReader xReader = ExecuteXmlReader())
+ {
+ XDocument xmlDocument = XDocument.Load(xReader);
+ return xmlDocument;
+ }
+ }
+
+ public IDataReader ExecuteReader()
+ {
+ using (SqlCommand sqlCommand = PrepareSqlCommand())
+ {
+ return sqlCommand.ExecuteReader();
+ }
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API/e-suite.Database.Migrator/appsettings.Development.json b/e-suite.API/e-suite.Database.Migrator/appsettings.Development.json
new file mode 100644
index 0000000..6024f10
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/appsettings.Development.json
@@ -0,0 +1,14 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "database": {
+ "server": "(local)",
+ "databaseName": "esuite",
+ "encrypt": true,
+ "trustServerCertificate": true
+ }
+}
diff --git a/e-suite.API/e-suite.Database.Migrator/appsettings.Production.json b/e-suite.API/e-suite.Database.Migrator/appsettings.Production.json
new file mode 100644
index 0000000..6024f10
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/appsettings.Production.json
@@ -0,0 +1,14 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "database": {
+ "server": "(local)",
+ "databaseName": "esuite",
+ "encrypt": true,
+ "trustServerCertificate": true
+ }
+}
diff --git a/e-suite.API/e-suite.Database.Migrator/appsettings.json b/e-suite.API/e-suite.Database.Migrator/appsettings.json
new file mode 100644
index 0000000..4d56694
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/e-suite.API/e-suite.Database.Migrator/e-suite.Database.Migrator.csproj b/e-suite.API/e-suite.Database.Migrator/e-suite.Database.Migrator.csproj
new file mode 100644
index 0000000..0e72dee
--- /dev/null
+++ b/e-suite.API/e-suite.Database.Migrator/e-suite.Database.Migrator.csproj
@@ -0,0 +1,64 @@
+
+
+
+ Exe
+ net10.0
+ e_suite.Database.Migrator
+ enable
+ enable
+ Linux
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+ true
+ PreserveNewest
+
+
+ Always
+ true
+ PreserveNewest
+
+
+ Always
+ true
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/AccountControllerTestBase.cs b/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/AccountControllerTestBase.cs
new file mode 100644
index 0000000..5c40e72
--- /dev/null
+++ b/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/AccountControllerTestBase.cs
@@ -0,0 +1,49 @@
+using System.Security.Claims;
+using e_suite.API.Common;
+using e_suite.Service.Sentinel;
+using e_suite.UnitTestCore;
+using eSuite.API.Controllers;
+using eSuite.API.SingleSignOn;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Moq;
+
+namespace eSuite.API.UnitTests.Controllers.AccountControllerUnitTests;
+
+public abstract class AccountControllerTestBase : TestBase
+{
+ protected AccountController _accountController = null!;
+ protected Mock _userManagerMock = null!;
+ protected Mock _sentinelMock = null!;
+ protected Mock _singleSignOnMock = null!;
+ protected Mock _cookieManagerMock = null!;
+
+ public override async Task Setup()
+ {
+ await base.Setup();
+
+ _userManagerMock = new Mock();
+ _sentinelMock = new Mock();
+ _singleSignOnMock = new Mock();
+ _cookieManagerMock = new Mock();
+
+ _accountController = new AccountController(_userManagerMock.Object,_sentinelMock.Object, _singleSignOnMock.Object, _cookieManagerMock.Object);
+ }
+
+ protected void AddAuthorisedUserToController(long id, string email, string displayName)
+ {
+ var user = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
+ {
+ new(ClaimTypes.PrimarySid, id.ToString()),
+ new(ClaimTypes.Email, email),
+ new(ClaimTypes.Name, displayName)
+ // other required and custom claims
+ }, "TestAuthentication"));
+
+ _accountController.ControllerContext = new ControllerContext
+ {
+ HttpContext = new DefaultHttpContext { User = user }
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/AuthUnitTests.cs b/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/AuthUnitTests.cs
new file mode 100644
index 0000000..aef9f6b
--- /dev/null
+++ b/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/AuthUnitTests.cs
@@ -0,0 +1,170 @@
+using e_suite.API.Common.models;
+using e_suite.Database.Audit;
+using e_suite.Database.Core.Tables.UserManager;
+using eSuite.API.SingleSignOn;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Moq;
+using NUnit.Framework;
+
+namespace eSuite.API.UnitTests.Controllers.AccountControllerUnitTests;
+
+[TestFixture]
+public class AuthUnitTests : AccountControllerTestBase
+{
+ [SetUp]
+ public override async Task Setup()
+ {
+ await base.Setup();
+ }
+
+ [Test]
+ public async Task Auth_WhenCalledInNormalSsoProcess_LogsInCreatesSessionCookiesAndRedirectsToRoot()
+ {
+ //Arrange
+ var ssoId = 1;
+ var code = "code";
+ var scope = "scope";
+ var authUser = "authUser";
+ var prompt = "prompt";
+
+ var ssoUserId = "ssoUserId123456";
+ _singleSignOnMock.Setup(x => x.ExchangeAuthorisationToken(ssoId, code, It.IsAny()))
+ .ReturnsAsync(() => ssoUserId);
+
+ var loginResponse = new LoginResponse
+ {
+ Result = LoginResult.Success,
+ Token = "Json Web Toke goes here"
+ };
+
+ _userManagerMock.Setup(x => x.LoginSso(ssoId, ssoUserId, It.IsAny()))
+ .ReturnsAsync(() => loginResponse);
+
+ //Act
+ var response = await _accountController.Auth(ssoId, code, scope, authUser, prompt, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var redirectResult = response as RedirectResult;
+ Assert.That(redirectResult?.Url, Is.EqualTo("~/"));
+
+ _cookieManagerMock.Verify(x => x.CreateSessionCookie(It.IsAny(), loginResponse), Times.Once);
+ _cookieManagerMock.Verify(x => x.CreateSsoIdCookie(It.IsAny(), ssoId), Times.Once);
+ _sentinelMock.Verify(x => x.LogBadRequest(_accountController, It.IsAny()), Times.Never);
+ }
+
+ [Test]
+ public async Task Auth_WhenLoginFails_DoesNotLoginRedirectsToRootAndLogsBadRequest()
+ {
+ //Arrange
+ var ssoId = 1;
+ var code = "code";
+ var scope = "scope";
+ var authUser = "authUser";
+ var prompt = "prompt";
+
+ var ssoUserId = "ssoUserId123456";
+ _singleSignOnMock.Setup(x => x.ExchangeAuthorisationToken(ssoId, code, It.IsAny()))
+ .ReturnsAsync(() => ssoUserId);
+
+ var loginResponse = new LoginResponse
+ {
+ Result = LoginResult.Failed
+ };
+
+ _userManagerMock.Setup(x => x.LoginSso(ssoId, ssoUserId, It.IsAny()))
+ .ReturnsAsync(() => loginResponse);
+
+ //Act
+ var response = await _accountController.Auth(ssoId, code, scope, authUser, prompt, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var redirectResult = response as RedirectResult;
+ Assert.That(redirectResult?.Url, Is.EqualTo("~/"));
+
+ _cookieManagerMock.Verify(x => x.CreateSessionCookie(It.IsAny(), loginResponse), Times.Never);
+ _cookieManagerMock.Verify(x => x.CreateSsoIdCookie(It.IsAny(), ssoId), Times.Never);
+ _sentinelMock.Verify( x => x.LogBadRequest(_accountController, It.IsAny()), Times.Once);
+ }
+
+ [TestCase(LoginResult.EmailNotConfirmed)]
+ [TestCase(LoginResult.TwoFactorAuthenticationRemovalRequested)]
+ [TestCase(LoginResult.TwoFactorAuthenticationCodeRequired)]
+ [TestCase(LoginResult.TwoFactorAuthenticationCodeIncorrect)]
+ public async Task Auth_WhenLoginCannotComplete_DoesNotLoginAndRedirectsToRoot(LoginResult loginResult)
+ {
+ //Arrange
+ var ssoId = 1;
+ var code = "code";
+ var scope = "scope";
+ var authUser = "authUser";
+ var prompt = "prompt";
+
+ var ssoUserId = "ssoUserId123456";
+ _singleSignOnMock.Setup(x => x.ExchangeAuthorisationToken(ssoId, code, It.IsAny()))
+ .ReturnsAsync(() => ssoUserId);
+
+ var loginResponse = new LoginResponse
+ {
+ Result = loginResult
+ };
+
+ _userManagerMock.Setup(x => x.LoginSso(ssoId, ssoUserId, It.IsAny()))
+ .ReturnsAsync(() => loginResponse);
+
+ //Act
+ var response = await _accountController.Auth(ssoId, code, scope, authUser, prompt, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var redirectResult = response as RedirectResult;
+ Assert.That(redirectResult?.Url, Is.EqualTo("~/"));
+
+ _cookieManagerMock.Verify(x => x.CreateSessionCookie(It.IsAny(), loginResponse), Times.Never);
+ _cookieManagerMock.Verify(x => x.CreateSsoIdCookie(It.IsAny(), ssoId), Times.Never);
+ _sentinelMock.Verify(x => x.LogBadRequest(_accountController, It.IsAny()), Times.Never);
+ }
+
+ [Test]
+ public async Task Auth_WhenProfileLinkCookieDetected_DeletesSingleUserCookieLinksSsoUserIdToAccountAndRedirectsToProfileEditPage()
+ {
+ //Arrange
+ var userId = 99;
+ var email = "email@mail.test";
+ var displayName = "Testy McTester";
+ AddAuthorisedUserToController(userId, email, displayName);
+
+ var ssoId = 1;
+ var code = "code";
+ var scope = "scope";
+ var authUser = "authUser";
+ var prompt = "prompt";
+
+ var ssoUserId = "ssoUserId123456";
+ _singleSignOnMock.Setup(x => x.ExchangeAuthorisationToken(ssoId, code, It.IsAny()))
+ .ReturnsAsync(() => ssoUserId);
+
+ var user = new User();
+
+ _cookieManagerMock
+ .Setup(x => x.GetUserIdFromLinkCookie(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(() => new CookieLink
+ {
+ User = user, LinkType = LinkType.Profile
+
+ });
+
+ //Act
+ var response = await _accountController.Auth(ssoId, code, scope, authUser, prompt, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var redirectResult = response as RedirectResult;
+ Assert.That(redirectResult?.Url, Is.EqualTo("~/profile"));
+
+ _cookieManagerMock.Verify(x => x.DeleteLinkCookie(It.IsAny()), Times.Once);
+ _userManagerMock.Verify( x => x.LinkSsoProfileToUser(It.IsAny(), It.IsAny(), ssoId, ssoUserId, It.IsAny(),It.IsAny()), Times.Once);
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/LoginGetUnitTests.cs b/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/LoginGetUnitTests.cs
new file mode 100644
index 0000000..f8c2f12
--- /dev/null
+++ b/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/LoginGetUnitTests.cs
@@ -0,0 +1,93 @@
+using e_suite.API.Common.models;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Moq;
+using NUnit.Framework;
+
+namespace eSuite.API.UnitTests.Controllers.AccountControllerUnitTests;
+
+[TestFixture]
+public class LoginGetUnitTests : AccountControllerTestBase
+{
+ [SetUp]
+ public override async Task Setup()
+ {
+ await base.Setup();
+ }
+
+ [Test]
+ public async Task LoginGet_WhenLoginNotPresent_ReturnsLoginViewWithEmptyModel()
+ {
+ //Arrange
+ _cookieManagerMock.Setup(x => x.GetSsoIdFromSsoIdCookie(It.IsAny())).ReturnsAsync(() => null);
+
+ Login? login = null;
+
+ //Act
+ var response = await _accountController.LoginGet(login, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var viewResult = response as ViewResult;
+ Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
+ Assert.That(viewResult?.Model, Is.TypeOf());
+
+ var actualLogin = viewResult?.Model as Login;
+ Assert.That(actualLogin, Is.Not.Null);
+ Assert.That(actualLogin?.Email, Is.EqualTo(string.Empty));
+ Assert.That(actualLogin?.ForgotPassword, Is.EqualTo(false));
+ Assert.That(actualLogin?.Password, Is.EqualTo(string.Empty));
+ Assert.That(actualLogin?.RequestTfaRemoval, Is.EqualTo(false));
+ Assert.That(actualLogin?.SecurityCode, Is.EqualTo(string.Empty));
+ }
+
+ [Test]
+ public async Task LoginGet_WhenLoginIncluded_ReturnsLoginViewWithCorrectModel()
+ {
+ //Arrange
+ _cookieManagerMock.Setup(x => x.GetSsoIdFromSsoIdCookie(It.IsAny())).ReturnsAsync(() => null);
+
+ Login? login = new Login
+ {
+ Email = "TestUser@Test.test"
+ };
+
+ //Act
+ var response = await _accountController.LoginGet(login, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var viewResult = response as ViewResult;
+ Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
+ Assert.That(viewResult?.Model, Is.TypeOf());
+
+ var actualLogin = viewResult?.Model as Login;
+ Assert.That(actualLogin, Is.EqualTo(login));
+ }
+
+
+ [Test]
+ public async Task LoginGet_WhenSsoIdCookieExists_RedirectsToSsoLoginUrl()
+ {
+ //Arrange
+ Login? login = null;
+
+ var ssoProviderId = 1;
+ var ssoUrl = "http://test.test/login";
+
+ _cookieManagerMock.Setup(x => x.GetSsoIdFromSsoIdCookie(It.IsAny())).ReturnsAsync(() => ssoProviderId);
+ _singleSignOnMock.Setup(x => x.StartSingleSignOn(ssoProviderId, It.IsAny()))
+ .ReturnsAsync(() => ssoUrl);
+
+ //Act
+ var response = await _accountController.LoginGet(login, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+
+ var redirectResult = response as RedirectResult;
+ Assert.That( redirectResult?.Url, Is.EqualTo(ssoUrl));
+
+ _cookieManagerMock.Verify( x => x.DeleteSsoIdCookie(It.IsAny()), Times.Once);
+ }
+}
\ No newline at end of file
diff --git a/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/LoginPostUnitTests.cs b/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/LoginPostUnitTests.cs
new file mode 100644
index 0000000..23fb4b6
--- /dev/null
+++ b/e-suite.API/eSuite.API.UnitTests/Controllers/AccountControllerUnitTests/LoginPostUnitTests.cs
@@ -0,0 +1,209 @@
+using e_suite.API.Common.models;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Moq;
+using NUnit.Framework;
+
+namespace eSuite.API.UnitTests.Controllers.AccountControllerUnitTests;
+
+[TestFixture]
+public class LoginPostUnitTests : AccountControllerTestBase
+{
+ [SetUp]
+ public override async Task Setup()
+ {
+ await base.Setup();
+ }
+
+ [Test]
+ public async Task LoginPost_WhenLoginPasswordEmptyAndNotSingleSignOn_ReturnsLoginViewWithEmptyModel()
+ {
+ //Arrange
+ var login = new Login
+ {
+ Email = "Test@test.test"
+ };
+
+ //Act
+ var response = await _accountController.LoginPost(login, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var viewResult = response as ViewResult;
+ Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
+ Assert.That(viewResult?.Model, Is.TypeOf());
+
+ var actualLogin = viewResult?.Model as Login;
+ Assert.That(actualLogin, Is.EqualTo(login));
+ }
+
+ [Test]
+ public async Task LoginPost_WhenSingleSignOnUserPresentsEmail_ReturnsRedirectsToSingleSignOnUrl()
+ {
+ //Arrange
+ var login = new Login
+ {
+ Email = "Test@test.test"
+ };
+
+ var ssoUrl = "http://test.test/login";
+
+ _singleSignOnMock.Setup(x => x.StartSingleSignOn(login.Email, It.IsAny()))
+ .ReturnsAsync(() => ssoUrl);
+
+ //Act
+ var response = await _accountController.LoginPost(login, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+
+ var redirectResult = response as RedirectResult;
+ Assert.That(redirectResult?.Url, Is.EqualTo(ssoUrl));
+ }
+
+ [Test]
+ public async Task LoginPost_WhenForgotPassword_UserManagerForgotPasswordCalled()
+ {
+ //Arrange
+ Login? login = new Login
+ {
+ Email = "TestUser@Test.test",
+ ForgotPassword = true
+ };
+
+ //Act
+ var response = await _accountController.LoginPost(login, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var viewResult = response as ViewResult;
+ Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
+ Assert.That(viewResult?.Model, Is.TypeOf());
+
+ var actualLogin = viewResult?.Model as Login;
+ Assert.That(actualLogin, Is.EqualTo(login));
+
+ _userManagerMock.Verify(x => x.ForgotPassword(login.Email, It.IsAny()), Times.Once);
+ }
+
+ [Test]
+ public async Task LoginPost_WhenForgotPasswordButPasswordHasRubbish_UserManagerForgotPasswordCalled()
+ {
+ //Arrange
+ Login? login = new Login
+ {
+ Email = "TestUser@Test.test",
+ Password = "A",
+ ForgotPassword = true
+ };
+
+ //Act
+ var response = await _accountController.LoginPost(login, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var viewResult = response as ViewResult;
+ Assert.That(viewResult?.ViewName, Is.EqualTo("Login"));
+ Assert.That(viewResult?.Model, Is.TypeOf());
+
+ var actualLogin = viewResult?.Model as Login;
+ Assert.That(actualLogin, Is.EqualTo(login));
+
+ _userManagerMock.Verify(x => x.ForgotPassword(login.Email, It.IsAny()), Times.Once);
+ }
+
+
+ [Test]
+ public async Task LoginPost_WhenPasswordPresentAndCorrect_CreatesSessionCookieAndRedirectToRoot()
+ {
+ //Arrange
+ Login? login = new Login
+ {
+ Email = "TestUser@Test.test",
+ Password = "SuperSecret"
+ };
+
+ var loginResponse = new LoginResponse
+ {
+ Result = LoginResult.Success,
+ Token = "Valid JSON Web Token"
+ };
+
+ _userManagerMock.Setup(x => x.Login(login, It.IsAny())).ReturnsAsync(() => loginResponse);
+
+ //Act
+ var response = await _accountController.LoginPost(login, CancellationToken.None);
+
+ //Assert
+ Assert.That(response, Is.TypeOf());
+ var redirectResult = response as RedirectResult;
+ Assert.That(redirectResult?.Url, Is.EqualTo("/"));
+
+ _cookieManagerMock.Verify( x => x.CreateSessionCookie(It.IsAny