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