diff --git a/FacadeFor3e.Tests/FacadeFor3e.Tests.csproj b/FacadeFor3e.Tests/FacadeFor3e.Tests.csproj
index 1c71f82..6a9b454 100644
--- a/FacadeFor3e.Tests/FacadeFor3e.Tests.csproj
+++ b/FacadeFor3e.Tests/FacadeFor3e.Tests.csproj
@@ -7,13 +7,9 @@
-
-
-
-
-
-
-
+
+
+
diff --git a/FacadeFor3e.Tests/Resources/ExampleProfPresParagraphs.json b/FacadeFor3e.Tests/Resources/ExampleProfPresParagraphs.json
index ec93b71..b261f1e 100644
--- a/FacadeFor3e.Tests/Resources/ExampleProfPresParagraphs.json
+++ b/FacadeFor3e.Tests/Resources/ExampleProfPresParagraphs.json
@@ -1,5 +1,5 @@
{
- "@odata.context": "https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/$metadata#Proforma_ndt(ProfPresParagraphs)",
+ "@odata.context": "https://rdfin91tewa01.elite.global/TE_3E_DEV_EU_REPORT/odata/$metadata#Proforma_ndt(ProfPresParagraphs)",
"value": [
{
"ProfPresParagraphs": [
@@ -14,7 +14,7 @@
"Currency": "EUR",
"PresAmount": 1464.77,
"PresHours": 2.5,
- "Narrative": "505533b2-2df1-4ffa-bf16-31c3351c408e",
+ "Narrative": "ProfPresentationParagraph test",
"SortString": "sorted",
"ProfMaster": 225370
},
diff --git a/FacadeFor3e.Tests/Resources/ExampleResponseFromODataExecute1.json b/FacadeFor3e.Tests/Resources/ExampleResponseFromODataExecute1.json
new file mode 100644
index 0000000..7d7c76c
--- /dev/null
+++ b/FacadeFor3e.Tests/Resources/ExampleResponseFromODataExecute1.json
@@ -0,0 +1,9 @@
+{
+ "@odata.context": "https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/$metadata#Office/$entity",
+ "OfficeID": "29a69ad1-970b-4636-8ede-80d90cc0f222",
+ "Code": "0000",
+ "Description": "Default 2024-02-23T16:11:06",
+ "OfficeTaxCodes": [],
+ "OfficeAddressLangs": [],
+ "OfficeTemplateTexts": []
+}
diff --git a/FacadeFor3e.Tests/Resources/ExampleResponseFromODataExecute2.json b/FacadeFor3e.Tests/Resources/ExampleResponseFromODataExecute2.json
new file mode 100644
index 0000000..f759a5e
--- /dev/null
+++ b/FacadeFor3e.Tests/Resources/ExampleResponseFromODataExecute2.json
@@ -0,0 +1,23 @@
+{
+ "@odata.context":"https://rdfin91tewa01.elite.global/TE_3E_DEV_EU_REPORT/odata/$metadata#Proforma_ndt/$entity",
+ "ProfMasterID":"54421a94-322c-470f-8224-2bda20a2264c",
+ "ProfIndex":225360,
+ "NxAttachments":[],
+ "ProfMatters":[],
+ "TRNValidationResultss":[],
+ "ProfDetailTimes":[],
+ "ProfDetailChrgs":[],
+ "ProfDetailCosts":[],
+ "ProfAdjusts":[],
+ "ProfPayors":[],
+ "ProfTrusts":[],
+ "ProfUnallocateds":[],
+ "ProfBOAs":[],
+ "ProfTemplateOptions":[],
+ "ProfPresParagraphs":[],
+ "ProfMasterDates":[],
+ "ProfTaxArticles":[],
+ "ProfPayorLayers":[],
+ "ProformaContacts_cccs":[],
+ "ProfUDF_cccs":[]
+}
diff --git a/FacadeFor3e.Tests/TestODataDeserialisation.cs b/FacadeFor3e.Tests/TestODataDeserialisation.cs
new file mode 100644
index 0000000..cb2c4dd
--- /dev/null
+++ b/FacadeFor3e.Tests/TestODataDeserialisation.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Text.Json;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+
+namespace FacadeFor3e.Tests
+ {
+ [TestFixture]
+ public class TestODataDeserialisation
+ {
+ [Test]
+ public void TestDeserialisation()
+ {
+ var json = JsonDocument.Parse(Resources.ExampleProfPresParagraphs);
+
+ var element = json.RootElement.GetProperty("value")[0].GetProperty("ProfPresParagraphs")[0];
+ var firstItem = element.JsonDeserialise();
+ ClassicAssert.AreEqual("ProfPresentationParagraph test", firstItem.Narrative);
+ ClassicAssert.AreEqual(2.5m, firstItem.PresHours);
+ ClassicAssert.AreEqual("sorted", firstItem.SortString);
+
+ var result = json.RootElement.GetProperty("value")[0].GetProperty("ProfPresParagraphs").JsonDeserialiseList();
+ ClassicAssert.AreEqual(9, result.Count);
+#if NET6_0_OR_GREATER
+ ClassicAssert.AreEqual(new DateOnly(2023, 09, 19), result[0].PresDate);
+#else
+ ClassicAssert.AreEqual(new DateTime(2023, 09, 19), result[0].PresDate);
+#endif
+ ClassicAssert.AreEqual("EUR", result[1].Currency);
+ ClassicAssert.AreEqual(543.12, result[5].PresAmount);
+ ClassicAssert.AreEqual(new Guid("d07726c1-2a71-4283-b82b-d6bbe2d079f7"), result[6].ProfPresentationParagraphId);
+ ClassicAssert.IsNull(result[2].PresAmount);
+ }
+ }
+
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public class DbPresentationParagraph
+ {
+ // ReSharper disable UnassignedField.Global
+ public Guid ProfPresentationParagraphId;
+#if NET6_0_OR_GREATER
+ public DateOnly PresDate;
+#else
+ public DateTime PresDate;
+#endif
+ public string Currency;
+ public decimal? PresAmount;
+ public decimal? PresHours;
+ public string Narrative;
+ public string SortString;
+ // ReSharper restore UnassignedField.Global
+ }
+ }
diff --git a/FacadeFor3e.Tests/TestODataResponse.cs b/FacadeFor3e.Tests/TestODataResponse.cs
new file mode 100644
index 0000000..4145fb1
--- /dev/null
+++ b/FacadeFor3e.Tests/TestODataResponse.cs
@@ -0,0 +1,41 @@
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using NUnit.Framework;
+using NUnit.Framework.Legacy;
+
+namespace FacadeFor3e.Tests
+ {
+ [TestFixture]
+ public class TestODataResponse
+ {
+ [Test]
+ public void TestStandardError()
+ {
+ var response = @"{""error"":{""code"":"""",""message"":""Entity 'Proforma' with key '-100' does not exist.""}}";
+ var httpResponse = new HttpResponseMessage(HttpStatusCode.BadRequest);
+ httpResponse.Content = new ByteArrayContent(Encoding.UTF8.GetBytes(response));
+ httpResponse.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
+ var serviceResult = new ODataServiceResult(new HttpRequestMessage(HttpMethod.Get, "http://localhost/"), httpResponse);
+ ClassicAssert.IsTrue(serviceResult.IsError);
+ ClassicAssert.AreEqual(1, serviceResult.ErrorMessages.Count());
+ ClassicAssert.AreEqual("Entity 'Proforma' with key '-100' does not exist.", serviceResult.ErrorMessages.First());
+ }
+
+ [Test]
+ public void TestAlternativeError()
+ {
+ var response = @"{ ""statusCode"": 429, ""message"": ""Rate limit is exceeded. Try again in 38 seconds."" }";
+ var httpResponse = new HttpResponseMessage(HttpStatusCode.BadRequest);
+ httpResponse.Content = new ByteArrayContent(Encoding.UTF8.GetBytes(response));
+ httpResponse.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
+ var serviceResult = new ODataServiceResult(new HttpRequestMessage(HttpMethod.Get, "http://localhost/"), httpResponse);
+ ClassicAssert.IsTrue(serviceResult.IsError);
+ ClassicAssert.AreEqual(1, serviceResult.ErrorMessages.Count());
+ ClassicAssert.AreEqual("Rate limit is exceeded. Try again in 38 seconds.", serviceResult.ErrorMessages.First());
+ }
+ }
+ }
+
diff --git a/FacadeFor3e.Tests/TestODataUpdate.cs b/FacadeFor3e.Tests/TestODataUpdate.cs
deleted file mode 100644
index 97d28d9..0000000
--- a/FacadeFor3e.Tests/TestODataUpdate.cs
+++ /dev/null
@@ -1,380 +0,0 @@
-using System;
-using System.Configuration;
-using System.Linq;
-using System.Text.Json;
-using FacadeFor3e.ProcessCommandBuilder;
-using NUnit.Framework;
-using NUnit.Framework.Legacy;
-
-#pragma warning disable OData
-
-namespace FacadeFor3e.Tests
- {
- [TestFixture]
- public class TestODataUpdate
- {
- [OneTimeSetUp]
- public void Setup()
- {
- var cm = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath;
- NLog.LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(cm);
- }
-
- [Test]
- public void Test1()
- {
- var command = new ProcessCommand("ProfMaster_ndt", "Proforma_ndt");
- var e = command.EditRecord(new IdentifyByPrimaryKey(225360));
- e.AddAttribute("InvNarrative", $"Updated narrative {DateTime.Now}");
-
- Uri baseUri = new Uri("https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/");
- var service = new ODataServices(baseUri);
- var response = service.Execute(command, ExecuteParams.Default);
- Console.WriteLine(response);
- }
-
-
- [Test]
- public void Test2()
- {
- var command = new ProcessCommand("ProfMaster_ndt", "Proforma_ndt");
- var e = command.EditRecord(new IdentifyByPrimaryKey(225360));
- e.AddAttribute("InvNarrative", $"Updated narrative {DateTime.Now}");
-
- Uri tranSvc =
- new Uri("https://rdfin91tewa01.dentons.global/te_3e_dev_eu_report/web/transactionservice.asmx");
- var ts = new TransactionServices(tranSvc);
- ts.ExecuteProcess.Execute(command, ExecuteProcessParams.Default);
- }
-
- [Test]
- public void Test3()
- {
- var command = new ProcessCommand("Office", "Office");
- var edit = command.EditRecord(new IdentifyByPrimaryKey("0000"));
- edit.AddAttribute("Description", $"Default {DateTime.Now:s}");
-
- Uri baseUri = new Uri("https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/");
- var service = new ODataServices(baseUri);
- var response = service.Execute(command, ExecuteParams.Default);
- Console.WriteLine(response.ResponseString);
- }
-
- [Test]
- public void TestGetProformaDetails()
- {
- var uri = new Uri("Proforma_ndt(225483)", UriKind.Relative);
-
- Uri baseUri = new Uri("https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/");
- var service = new ODataServices(baseUri);
- var result = service.Select(uri);
- Console.WriteLine(result.ResponseString);
- }
-
- [Test]
- public void TestGetProfStatus()
- {
- var uri = new Uri("Proforma_ndt(225483)?$select=ProfStatus", UriKind.Relative);
-
- Uri baseUri = new Uri("https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/");
- var service = new ODataServices(baseUri);
- var result = service.Select(uri);
- var profStatus = result.ResponseJSonDocument.RootElement.GetProperty("value")[0].GetProperty("ProfStatus").GetString();
- Console.WriteLine(profStatus);
- }
-
- [Test]
- public void TestProfDetResponse()
- {
- var jsonDocument = JsonDocument.Parse(ProfDetResponse);
- var result = jsonDocument.RootElement.GetProperty("value")[0].GetProperty("ProfDetailTimes").EnumerateArray().Select(item => item.GetProperty("ProfDetIndex").GetInt32()).ToList();
- ClassicAssert.AreEqual(2, result.Count);
- ClassicAssert.AreEqual(5117777, result[0]);
- ClassicAssert.AreEqual(5117778, result[1]);
- }
-
- private string ProfDetResponse
- {
- get
- {
- return
- @"{
- ""@odata.context"": ""https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/$metadata#Proforma_ndt(ProfDetailTimes)"",
- ""value"": [
- {
- ""ProfDetailTimes"": [
- {
- ""ProfDetailTimeID"": ""6f411a21-c281-4d1f-ba09-6c0d9c66a455"",
- ""ArchetypeCode"": ""ProfDetailTime"",
- ""LastProcItemID"": ""1558400f-88f6-46f4-bbe5-809b141b23ac"",
- ""OrigProcItemID"": ""906210e9-d970-4a40-af0e-04a91b63dedd"",
- ""HasAttachments"": false,
- ""TimeStamp"": ""2023-08-23T21:58:44.477Z"",
- ""ProfDetIndex"": 5117777,
- ""ProfMaster"": 225370,
- ""Currency"": ""EUR"",
- ""CurrDate"": ""2023-08-23T00:00:00Z"",
- ""TransactionType"": ""FEES"",
- ""IsDisplay"": false,
- ""WorkMatter"": 100445,
- ""BillMatter"": 100445,
- ""WorkTimekeeper"": 8117,
- ""IsSplit"": false,
- ""WorkDate"": ""2023-08-23T00:00:00Z"",
- ""WorkAmt"": 852,
- ""WorkLanguage"": 1031,
- ""WorkNarrative"": ""Additional timecard 1 (5c4b62e9-c9f2-4493-854d-54159ce70a3b)"",
- ""EditAmt"": 852,
- ""PresMatter"": 100445,
- ""PresTimekeeper"": 8117,
- ""PresDate"": ""2023-08-23T00:00:00Z"",
- ""PresAmt"": 0,
- ""PresLanguage"": 1031,
- ""PresNarrative"": ""Additional timecard 1 (5c4b62e9-c9f2-4493-854d-54159ce70a3b)"",
- ""WorkRate"": 710,
- ""EditRate"": 710,
- ""IsExcluded"": false,
- ""IsNB"": false,
- ""IsCalculated"": false,
- ""PresRate"": 0,
- ""WorkWorkType"": ""DFLT"",
- ""WorkQnt"": 1,
- ""EditQnt"": 1,
- ""PresQnt"": 1,
- ""WorkMattEffDate"": ""45be993b-dd7b-4e41-bce9-2520720773de"",
- ""BillMattEffDate"": ""45be993b-dd7b-4e41-bce9-2520720773de"",
- ""WorkTkprEffDate"": ""9987f916-022b-4f83-83b1-194a825df141"",
- ""ProfMatter"": ""9eeeaa7e-4c33-4343-bb7d-78602ed57047"",
- ""WorkHrs"": 1.2,
- ""EditHrs"": 1.2,
- ""PresHrs"": 0,
- ""PresWorkType"": ""DFLT"",
- ""OrigCurrency"": ""EUR"",
- ""ProfDetailArchetype"": ""Timecard"",
- ""Timecard"": 32777779,
- ""WorkTimeType"": ""FEES"",
- ""PresTimeType"": ""FEES"",
- ""IsChangedNarrative"": false,
- ""Disposition"": ""Paragraph"",
- ""Office"": ""8105"",
- ""IsAnticipated"": false,
- ""RateCalcList"": ""MR"",
- ""SpvTimekeeper"": 5862,
- ""IsDoNotSummarize"": false,
- ""MatrixTaxCode"": ""DEOD10"",
- ""WorkStdAmt"": 852,
- ""PresMattEffDate"": ""45be993b-dd7b-4e41-bce9-2520720773de"",
- ""IsEBillVal"": false,
- ""PresIsNoCharge"": false,
- ""EditStatus"": ""X"",
- ""PresTkprEffDate"": ""9987f916-022b-4f83-83b1-194a825df141"",
- ""RefCurrency"": ""EUR"",
- ""RefRate"": 710,
- ""StdCurrency"": ""EUR"",
- ""StdRate"": 710,
- ""StdAmt"": 852,
- ""RefAmt"": 852
- },
- {
- ""ProfDetailTimeID"": ""34ba03df-276c-4464-a624-7fc2d1ec7240"",
- ""ArchetypeCode"": ""ProfDetailTime"",
- ""LastProcItemID"": ""1558400f-88f6-46f4-bbe5-809b141b23ac"",
- ""OrigProcItemID"": ""906210e9-d970-4a40-af0e-04a91b63dedd"",
- ""HasAttachments"": false,
- ""TimeStamp"": ""2023-08-23T21:58:44.48Z"",
- ""ProfDetIndex"": 5117778,
- ""ProfMaster"": 225370,
- ""Currency"": ""EUR"",
- ""CurrDate"": ""2023-08-23T00:00:00Z"",
- ""TransactionType"": ""FEES"",
- ""IsDisplay"": true,
- ""WorkMatter"": 100445,
- ""BillMatter"": 100445,
- ""WorkTimekeeper"": 8117,
- ""IsSplit"": false,
- ""WorkDate"": ""2023-08-23T00:00:00Z"",
- ""WorkAmt"": 1704,
- ""WorkLanguage"": 1031,
- ""WorkNarrative"": ""Additional timecard 2 (6eda14aa-2a83-4354-ae5e-352e6576712c)"",
- ""EditAmt"": 1704,
- ""PresMatter"": 100445,
- ""PresTimekeeper"": 8117,
- ""PresDate"": ""2023-08-23T00:00:00Z"",
- ""PresAmt"": 2556,
- ""PresLanguage"": 1031,
- ""PresNarrative"": ""combined narrative"",
- ""WorkRate"": 710,
- ""EditRate"": 710,
- ""IsExcluded"": false,
- ""IsNB"": false,
- ""IsCalculated"": false,
- ""PresRate"": 710,
- ""WorkWorkType"": ""DFLT"",
- ""WorkQnt"": 1,
- ""EditQnt"": 1,
- ""PresQnt"": 1,
- ""WorkMattEffDate"": ""45be993b-dd7b-4e41-bce9-2520720773de"",
- ""BillMattEffDate"": ""45be993b-dd7b-4e41-bce9-2520720773de"",
- ""WorkTkprEffDate"": ""9987f916-022b-4f83-83b1-194a825df141"",
- ""ProfMatter"": ""9eeeaa7e-4c33-4343-bb7d-78602ed57047"",
- ""WorkHrs"": 2.4,
- ""EditHrs"": 2.4,
- ""PresHrs"": 3.6,
- ""PresWorkType"": ""DFLT"",
- ""OrigCurrency"": ""EUR"",
- ""ProfDetailArchetype"": ""Timecard"",
- ""Timecard"": 32777780,
- ""WorkTimeType"": ""FEES"",
- ""PresTimeType"": ""FEES"",
- ""IsChangedNarrative"": false,
- ""Disposition"": ""Paragraph"",
- ""Office"": ""8105"",
- ""IsAnticipated"": false,
- ""RateCalcList"": ""MR"",
- ""SpvTimekeeper"": 5862,
- ""IsDoNotSummarize"": false,
- ""MatrixTaxCode"": ""DEOD10"",
- ""WorkStdAmt"": 1704,
- ""PresMattEffDate"": ""45be993b-dd7b-4e41-bce9-2520720773de"",
- ""IsEBillVal"": false,
- ""PresIsNoCharge"": false,
- ""EditStatus"": ""X"",
- ""PresTkprEffDate"": ""9987f916-022b-4f83-83b1-194a825df141"",
- ""RefCurrency"": ""EUR"",
- ""RefRate"": 710,
- ""StdCurrency"": ""EUR"",
- ""StdRate"": 710,
- ""StdAmt"": 1704,
- ""RefAmt"": 1704
- }
- ]
- }
- ]
-}";
- }
- }
-
-
- [Test]
- public void TestAddAndUpdate()
- {
- var processCommandForAdd = new ProcessCommand("TimeCardUpdate", "TimeCard");
- var add = processCommandForAdd.AddRecord();
-#if NET6_0_OR_GREATER
- add.AddAttribute("WorkDate", DateOnly.FromDateTime(DateTime.Today));
-#else
- add.AddDateAttribute("WorkDate", DateTime.Today);
-#endif
- add.AddAliasedAttribute("Timekeeper", "Number", "218669");
- add.AddAliasedAttribute("Matter", "Number", "0003778.0039");
- add.AddAttribute("WorkHrs", 2.5m);
- add.AddAttribute("Narrative", $"test at {DateTime.Now:T}");
-
- Uri baseUri = new Uri("https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/");
- var service = new ODataServices(baseUri);
- var response = service.Execute(processCommandForAdd, ExecuteParams.Default);
-
- var origTimeIndex = response.ResponseJSonDocument.RootElement.GetProperty("TimeIndex").GetInt32();
-
- var processCommandForEdit = new ProcessCommand("TimeCardUpdate", "TimeCard");
- var edit = processCommandForEdit.EditRecord(new IdentifyByPrimaryKey(origTimeIndex));
- edit.AddAttribute("WorkRate", 700m);
- edit.AddAttribute("Currency", "GBP");
-
- service.Execute(processCommandForEdit, ExecuteParams.Default);
-
- var uri = new Uri($"TimeCard?$filter=OrigTimeIndex eq {origTimeIndex} and IsActive eq true&$select=TimeIndex", UriKind.Relative);
- response = service.Select(uri);
- var newTimeIndex = response.ResponseJSonDocument.RootElement.GetProperty("value")[0].GetProperty("TimeIndex").GetInt32();
-
- var processCommandForUpdate = new ProcessCommand("TimeCardUpdate", "TimeCard");
- var update = processCommandForUpdate.EditRecord(new IdentifyByPrimaryKey(newTimeIndex));
- update.AddAttribute("Narrative", $"updated at {DateTime.Now:T}");
-
- service.Execute(processCommandForUpdate, ExecuteParams.Default);
-
- response = service.Select(uri);
- newTimeIndex = response.ResponseJSonDocument.RootElement.GetProperty("value")[0].GetProperty("TimeIndex").GetInt32();
-
- uri = new Uri($"TimeCard/{newTimeIndex}?$select=OrigTimeIndex,TimeIndex,IsActive,Narrative,WorkRate,Currency,WorkHrs", UriKind.Relative);
- service.Select(uri);
- }
-
- [Test]
- public void TestDeserialisation()
- {
- var json = JsonDocument.Parse(Resources.ExampleProfPresParagraphs);
- var jsonSerialiserOptions = new JsonSerializerOptions { IncludeFields = true };
-
- var element = json.RootElement.GetProperty("value")[0].GetProperty("ProfPresParagraphs")[0];
- var r = element.Deserialize(jsonSerialiserOptions);
-
- var result = json.RootElement.GetProperty("value")[0].GetProperty("ProfPresParagraphs").EnumerateArray().Select(item => item.Deserialize(jsonSerialiserOptions)).ToList();
- ClassicAssert.AreEqual(9, result.Count);
-#if NET6_0_OR_GREATER
- ClassicAssert.AreEqual(new DateOnly(2023, 09, 19), result[0].PresDate);
-#else
- ClassicAssert.AreEqual(new DateTime(2023, 09, 19), result[0].PresDate);
-#endif
- ClassicAssert.AreEqual("EUR", result[1].Currency);
- ClassicAssert.AreEqual(543.12, result[5].PresAmount);
- ClassicAssert.AreEqual(new Guid("d07726c1-2a71-4283-b82b-d6bbe2d079f7"), result[6].ProfPresentationParagraphID);
- ClassicAssert.IsNull(result[2].PresAmount);
- }
-
- }
-
- public class DbPresentationParagraph
- {
- // ReSharper disable once InconsistentNaming
- public Guid ProfPresentationParagraphID;
-#if NET6_0_OR_GREATER
- public DateOnly PresDate;
-#else
- public DateTime PresDate;
-#endif
- public string Currency;
- public decimal? PresAmount;
- public decimal? PresHours;
- public string Narrative;
- public string SortString;
- }
- }
-
-/*
-{
- "@odata.context": "https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/$metadata#Office/$entity",
- "OfficeID": "29a69ad1-970b-4636-8ede-80d90cc0f222",
- "Code": "0000",
- "Description": "Default 2024-02-23T16:11:06",
- "OfficeTaxCodes": [],
- "OfficeAddressLangs": [],
- "OfficeTemplateTexts": []
-}
-{
- "@odata.context":"https://rdfin91tewa01.dentons.global/TE_3E_DEV_EU_REPORT/odata/$metadata#Proforma_ndt/$entity",
- "ProfMasterID":"54421a94-322c-470f-8224-2bda20a2264c",
- "ProfIndex":225360,
- "NxAttachments":[],
- "ProfMatters":[],
- "TRNValidationResultss":[],
- "ProfDetailTimes":[],
- "ProfDetailChrgs":[],
- "ProfDetailCosts":[],
- "ProfAdjusts":[],
- "ProfPayors":[],
- "ProfTrusts":[],
- "ProfUnallocateds":[],
- "ProfBOAs":[],
- "ProfTemplateOptions":[],
- "ProfPresParagraphs":[],
- "ProfMasterDates":[],
- "ProfTaxArticles":[],
- "ProfPayorLayers":[],
- "ProformaContacts_cccs":[],
- "ProfUDF_cccs":[]
-}
-
-
-
-*/
diff --git a/FacadeFor3e.sln.DotSettings b/FacadeFor3e.sln.DotSettings
index 72080a2..78674a5 100644
--- a/FacadeFor3e.sln.DotSettings
+++ b/FacadeFor3e.sln.DotSettings
@@ -26,6 +26,10 @@
DisabledByUser
DisabledByUser
True
+ True
+ True
+ True
True
+ True
True
True
\ No newline at end of file
diff --git a/FacadeFor3e/CommonLibrary.cs b/FacadeFor3e/CommonLibrary.cs
index ef88df6..33d90fc 100644
--- a/FacadeFor3e/CommonLibrary.cs
+++ b/FacadeFor3e/CommonLibrary.cs
@@ -17,6 +17,11 @@ namespace FacadeFor3e
[PublicAPI]
public static class CommonLibrary
{
+ ///
+ /// Returns the default object used to deserialise the JSON returned by 3E into an object
+ ///
+ public static readonly JsonSerializerOptions DefaultJSonSerializerOptions = BuildDefaultJSonSerializerOptions();
+
///
/// Outputs an XML document as a formatted string for easy reading
///
@@ -68,14 +73,10 @@ internal static void EnsureValid(string? id)
/// Specifies the JSON to convert
/// An object of the specified type
/// If it was not possible to deserialise the JSON
+ [Pure]
public static T JsonDeserialise(this JsonElement element)
{
- var options = new JsonSerializerOptions { IncludeFields = true };
-#if NET6_0_OR_GREATER
- options.Converters.Add(new DateOnlyJsonConverter());
-#endif
- options.Converters.Add(new DateTimeJsonConverter());
- var result = element.Deserialize(options);
+ var result = element.Deserialize(DefaultJSonSerializerOptions);
if (result == null)
throw new InvalidOperationException("Failed to deserialise json.");
return result;
@@ -88,6 +89,7 @@ public static T JsonDeserialise(this JsonElement element)
/// Specifies the JSON to convert
/// A list of objects of the specified type
/// If it was not possible to deserialise the JSON
+ [Pure]
public static List JsonDeserialiseList(this JsonElement arrayElement)
{
if (arrayElement.ValueKind != JsonValueKind.Array)
@@ -96,6 +98,17 @@ public static List JsonDeserialiseList(this JsonElement arrayElement)
return result;
}
+ private static JsonSerializerOptions BuildDefaultJSonSerializerOptions()
+ {
+ var options = new JsonSerializerOptions { IncludeFields = true, PropertyNameCaseInsensitive = true, };
+#if NET6_0_OR_GREATER
+ options.Converters.Add(new DateOnlyJsonConverter());
+#endif
+ options.Converters.Add(new DateTimeJsonConverter());
+ options.MakeReadOnly(populateMissingResolver:true);
+ return options;
+ }
+
#if NET6_0_OR_GREATER
///
/// Converter for the 3E representation of dates
diff --git a/FacadeFor3e/FacadeFor3e.csproj b/FacadeFor3e/FacadeFor3e.csproj
index 4105694..5f832f1 100644
--- a/FacadeFor3e/FacadeFor3e.csproj
+++ b/FacadeFor3e/FacadeFor3e.csproj
@@ -5,7 +5,7 @@
FacadeFor3E
J Saffron Consulting
FacadeFor3E
- 4.0.0-rc2
+ 4.0.0-rc3
A library that makes integrating with Elite 3E a little easier
Copyright © J Saffron Consulting Ltd 2014 - 2024
https://github.com/JonSaffron/FacadeFor3e
@@ -19,7 +19,7 @@
True
A library to help you build apps that use Elite 3E
README.md
- Elite;3E;TransactionService
+ Elite;3E;TransactionService;OData
$(VersionPrefix)
$(VersionPrefix)
git
@@ -31,7 +31,7 @@
all
-
+
@@ -41,7 +41,7 @@
-
+
diff --git a/FacadeFor3e/ODataServiceResult.cs b/FacadeFor3e/ODataServiceResult.cs
index 00197a0..3b87f25 100644
--- a/FacadeFor3e/ODataServiceResult.cs
+++ b/FacadeFor3e/ODataServiceResult.cs
@@ -107,15 +107,35 @@ public IEnumerable ErrorMessages
return Array.Empty();
if (!this.IsResponseJSon)
- return new[] { this.ResponseString };
+ {
+ return new[] { $"HTTP status code {this.Response.StatusCode:D}" };
+ }
+
+ var result = new List();
+ var root = this.ResponseJSonDocument.RootElement;
+ if (root.TryGetProperty("message", out var messageElement))
+ {
+ var message = messageElement.GetString();
+ if (message != null && !string.IsNullOrWhiteSpace(message))
+ {
+ result.Add(message);
+ }
+ }
+
+ if (root.TryGetProperty("error", out var errorElement) && errorElement.TryGetProperty("message", out messageElement))
+ {
+ var errors = messageElement.GetString();
+ if (errors != null && !string.IsNullOrWhiteSpace(errors))
+ {
+ result.AddRange(errors.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries));
+ }
+ }
- var errors = this.ResponseJSonDocument.RootElement.GetProperty("error").GetProperty("message").GetString();
- if (errors == null || string.IsNullOrWhiteSpace(errors))
+ if (result.Count == 0)
{
- return new[] { $"Unknown error - HTTP status code {this.Response.StatusCode}" };
+ result.Add($"Unknown error - HTTP status code {this.Response.StatusCode:D}");
}
- var result = errors.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
return result;
}
}
diff --git a/FacadeFor3e/ODataServices.cs b/FacadeFor3e/ODataServices.cs
index cf607d4..6086b21 100644
--- a/FacadeFor3e/ODataServices.cs
+++ b/FacadeFor3e/ODataServices.cs
@@ -170,11 +170,6 @@ public ODataServiceResult Select(Uri relativeUri)
var result = new ODataServiceResult(request, response);
LogForDebug(result.ResponseString);
- if (!result.IsResponseJSon)
- {
- throw new ExecuteProcessException("Received an invalid response during authentication.");
- }
-
if (result.IsError)
{
var errorMessages = new List { "An error occurred whilst trying to query 3E data through OData:" };
@@ -220,7 +215,6 @@ public ODataServiceResult Execute(ProcessCommand command, ODataExecuteOptions op
/// Specifies a request to execute
/// A that contains the response
/// If the parameters specified have null values
- /// If an invalid response is returned
public ODataServiceResult Execute(ODataRequest requestDetails)
{
if (requestDetails == null) throw new ArgumentNullException(nameof(requestDetails));
@@ -238,11 +232,6 @@ public ODataServiceResult Execute(ODataRequest requestDetails)
var result = new ODataServiceResult(request, response);
LogForDebug(result.ResponseString);
- if (!result.IsResponseJSon)
- {
- throw new ExecuteProcessException("Received an invalid response from running a process.");
- }
-
return result;
}
@@ -299,7 +288,6 @@ internal void LogDetailsOfTheJob(HttpRequestMessage request)
sb.AppendFormat("\t{0} {1}", method, new Uri(this._httpClient.BaseAddress!, request.RequestUri!));
sb.AppendLine();
sb.AppendFormat("\tauthentication: {0}", authenticationMethod);
- sb.AppendLine();
Logger.Info(sb.ToString());
}
diff --git a/FacadeFor3e/ReleaseHistory.md b/FacadeFor3e/ReleaseHistory.md
index 2ab9034..d3a365e 100644
--- a/FacadeFor3e/ReleaseHistory.md
+++ b/FacadeFor3e/ReleaseHistory.md
@@ -1,14 +1,14 @@
-4.0.0-rc4
+4.0.0-rc3
---------
-Update to support new syntax for specifying attribute values by alias
-Add ODataServices.Execute overload to alllow a process to be called without a ProcessCommand
-
-4.0.0-rc3
----------
-Add Value property to ODataServiceResult to make it easier to consume results from Select requests
+Change the ODataServiceResult.ErrorMesssages property to make it less brittle and enable it to cope with errors caused by rate limiting
+Updated the json deserialiser to be case-insensitive and to cache the options object
+Removed some over zealous checks that the response from the OData service was in JSON format - for 404 errors it won't be
4.0.0-rc2
---------
+Update to support new syntax for specifying attribute values by alias
+Add ODataServices.Execute overload to alllow a process to be called without a ProcessCommand
+Add Value property to ODataServiceResult to make it easier to consume results from Select requests
Improve code analysis for consumers of the library
4.0.0-rc1
diff --git a/devnotes.txt b/devnotes.txt
index a21b87b..a13fcba 100644
--- a/devnotes.txt
+++ b/devnotes.txt
@@ -131,3 +131,20 @@ Predicate String No No
String Yes Yes
Text String No No
URL String Yes Yes
+
+----
+
+Dependencies:
+
+System.Net.Http
+Contains basics like HttpRequestMessage and HttpResponseMessage
+Appears to be stuck on version 4.3.4 since 2018
+
+System.ServiceModel.Http
+Contains BasicHttpBinding for WCF
+Last version for .net framework and .net standard is 4.10.3
+Last version for .net 6 is 6.2.0
+
+System.ServiceModel.Security
+Contains WindowsClientCredential to set WCF impersonation level
+Last version for .net framework and .net standard is 4.10.3