From 516fc6ff1e913cf2bd53800e9ec9e769a5efda37 Mon Sep 17 00:00:00 2001 From: Thiago Benvenuto Date: Wed, 10 Aug 2022 11:34:03 -0300 Subject: [PATCH 01/13] Fixed object handling for single object responses. Added support for v1.9 --- RetsSdk/Models/Enums/SupportedRetsVersion.cs | 3 +- RetsSdk/RetsSdk.csproj | 8 +++--- RetsSdk/Services/RetsClient.cs | 29 ++++++++++++++++++++ RetsSdk/Services/RetsWebRequester.cs | 3 +- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/RetsSdk/Models/Enums/SupportedRetsVersion.cs b/RetsSdk/Models/Enums/SupportedRetsVersion.cs index fdc0496..07bb93c 100644 --- a/RetsSdk/Models/Enums/SupportedRetsVersion.cs +++ b/RetsSdk/Models/Enums/SupportedRetsVersion.cs @@ -6,6 +6,7 @@ public enum SupportedRetsVersion Version_1_7, Version_1_7_1, Version_1_7_2, - Version_1_8 + Version_1_8, + Version_1_9, } } diff --git a/RetsSdk/RetsSdk.csproj b/RetsSdk/RetsSdk.csproj index e130837..3a60029 100644 --- a/RetsSdk/RetsSdk.csproj +++ b/RetsSdk/RetsSdk.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + net6.0 true true CrestApps @@ -19,11 +19,11 @@ - - + + - + diff --git a/RetsSdk/Services/RetsClient.cs b/RetsSdk/Services/RetsClient.cs index c43fc6b..c3fddba 100644 --- a/RetsSdk/Services/RetsClient.cs +++ b/RetsSdk/Services/RetsClient.cs @@ -381,6 +381,35 @@ public async Task> GetObject(string resource, string typ if (entity is MimePart message) { + if (!message.Headers.Contains("Object-ID") && response.Headers.TryGetValues("Object-ID", out var objectIds)) + { + message.Headers.Add("Object-ID", objectIds.FirstOrDefault()); + } + if (!message.Headers.Contains("Content-Description") && response.Headers.TryGetValues("Content-Description", out var contentDescriptions)) + { + message.Headers.Add("Content-Description", contentDescriptions.FirstOrDefault()); + } + if (!message.Headers.Contains("Content-Sub-Description") && response.Headers.TryGetValues("Content-Sub-Description", out var contentSubDescriptions)) + { + message.Headers.Add("Content-Sub-Description", contentSubDescriptions.FirstOrDefault()); + } + if (!message.Headers.Contains("MIME-Version") && response.Headers.TryGetValues("MIME-Version", out var mimeVersions)) + { + message.Headers.Add("MIME-Version", mimeVersions.FirstOrDefault()); + } + if (!message.Headers.Contains("Preferred") && response.Headers.TryGetValues("Preferred", out var preferreds)) + { + message.Headers.Add("Preferred", preferreds.FirstOrDefault()); + } + if (message.ContentId == null && response.Headers.TryGetValues("Content-Id", out var contentIds)) + { + message.ContentId = contentIds.FirstOrDefault(); + } + if (message.ContentLocation == null && response.Headers.TryGetValues("Content-Location", out var contentLocations)) + { + message.ContentLocation = new Uri(contentLocations.FirstOrDefault()); + } + // At this point we know this is a single image response files.Add(ProcessMessage(message)); } diff --git a/RetsSdk/Services/RetsWebRequester.cs b/RetsSdk/Services/RetsWebRequester.cs index 449f4a3..b55c866 100644 --- a/RetsSdk/Services/RetsWebRequester.cs +++ b/RetsSdk/Services/RetsWebRequester.cs @@ -70,7 +70,8 @@ protected virtual HttpClient GetClient(SessionResource resource) if (resource != null && !string.IsNullOrWhiteSpace(resource.Cookie)) { - client.DefaultRequestHeaders.Add("Set-Cookie", resource.Cookie); + //client.DefaultRequestHeaders.Add("Set-Cookie", resource.Cookie); + client.DefaultRequestHeaders.Add("Cookie", resource.Cookie); } if (resource != null && !string.IsNullOrWhiteSpace(resource.SessionId)) From 5ac1487c895f7360bbe6f74c5a85f444fd7133af Mon Sep 17 00:00:00 2001 From: Thiago Benvenuto Date: Thu, 11 Aug 2022 10:57:31 -0300 Subject: [PATCH 02/13] Fixed Location and Photo FK for RETS 1.9 --- RetsSdk/Models/PhotoId.cs | 4 ++-- RetsSdk/Services/RetsClient.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/RetsSdk/Models/PhotoId.cs b/RetsSdk/Models/PhotoId.cs index f244284..fe3f40e 100644 --- a/RetsSdk/Models/PhotoId.cs +++ b/RetsSdk/Models/PhotoId.cs @@ -2,7 +2,7 @@ { public class PhotoId { - public long Id { get; set; } + public string Id { get; set; } public int? ObjectId { get; set; } public PhotoId() @@ -10,7 +10,7 @@ public PhotoId() } - public PhotoId(long id, int? objectId = null) + public PhotoId(string id, int? objectId = null) { Id = id; ObjectId = objectId; diff --git a/RetsSdk/Services/RetsClient.cs b/RetsSdk/Services/RetsClient.cs index c3fddba..479f496 100644 --- a/RetsSdk/Services/RetsClient.cs +++ b/RetsSdk/Services/RetsClient.cs @@ -405,6 +405,7 @@ public async Task> GetObject(string resource, string typ { message.ContentId = contentIds.FirstOrDefault(); } + if (message.ContentLocation == null && response.Headers.TryGetValues("Content-Location", out var contentLocations)) { message.ContentLocation = new Uri(contentLocations.FirstOrDefault()); @@ -545,7 +546,7 @@ protected FileObject ProcessMessage(MimePart message) ContentType = new System.Net.Mime.ContentType(message.ContentType.MimeType), ContentDescription = message.Headers["Content-Description"], ContentSubDescription = message.Headers["Content-Sub-Description"], - ContentLocation = message.ContentLocation, + ContentLocation = message.ContentLocation ?? (message.Headers["Location"] != null ? new Uri(message.Headers["Location"]) : null), MemeVersion = message.Headers["MIME-Version"], Extension = MimeTypeMap.GetExtension(message.ContentType.MimeType) }; From a909d6a5c2c4241194b2a6dd4ae35fbf96b12ede Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Sun, 18 Sep 2022 18:04:11 -0700 Subject: [PATCH 03/13] Fix Compile --- RetsConnector/Example.cs | 6 +++--- RetsConnector/RetsConnector.csproj | 14 +++++++------- RetsSdk/RetsSdk.csproj | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/RetsConnector/Example.cs b/RetsConnector/Example.cs index b503581..6743d6e 100644 --- a/RetsConnector/Example.cs +++ b/RetsConnector/Example.cs @@ -102,7 +102,7 @@ public async Task Execute() // We can also download photos // This will return all photos for property with the primarykey 1234 - IEnumerable files = await Client.GetObject("Property", "Photo", new PhotoId(1234), false); + IEnumerable files = await Client.GetObject("Property", "Photo", new PhotoId("1234"), false); // Here is how we can iterate over the fields foreach (FileObject file in files) @@ -120,11 +120,11 @@ public async Task Execute() } // you can get a specific image for a given primary key like so - IEnumerable files2 = await Client.GetObject("Property", "Photo", new PhotoId(1234, 1), false); + IEnumerable files2 = await Client.GetObject("Property", "Photo", new PhotoId("1234", 1), false); // you can get also get images for multiple primary keys at the same time like this - List photoIds = new List() { new PhotoId(1234), new PhotoId(5678), new PhotoId(2255) }; + List photoIds = new List() { new PhotoId("1234"), new PhotoId("5678"), new PhotoId("2255") }; IEnumerable files3 = await Client.GetObject("Property", "Photo", photoIds, false); diff --git a/RetsConnector/RetsConnector.csproj b/RetsConnector/RetsConnector.csproj index 20ef869..abb2ef7 100644 --- a/RetsConnector/RetsConnector.csproj +++ b/RetsConnector/RetsConnector.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net6 latest @@ -15,12 +15,12 @@ - - - - - - + + + + + + diff --git a/RetsSdk/RetsSdk.csproj b/RetsSdk/RetsSdk.csproj index 3a60029..d19fc3e 100644 --- a/RetsSdk/RetsSdk.csproj +++ b/RetsSdk/RetsSdk.csproj @@ -21,7 +21,7 @@ - + From e5f89c01e1e00de7e5e14bfbfbc99f85ecdc6233 Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Sun, 25 Sep 2022 18:29:06 -0700 Subject: [PATCH 04/13] Add IEnumerator --- RetsSdk/Models/RetsCollection.cs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/RetsSdk/Models/RetsCollection.cs b/RetsSdk/Models/RetsCollection.cs index 3d06489..8b8a53e 100644 --- a/RetsSdk/Models/RetsCollection.cs +++ b/RetsSdk/Models/RetsCollection.cs @@ -1,6 +1,7 @@ using CrestApps.RetsSdk.Contracts; using CrestApps.RetsSdk.Helpers.Extensions; using System; +using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -14,8 +15,8 @@ namespace CrestApps.RetsSdk.Models public string Version { get; set; } public DateTime Date { get; set; } - private List Items = new List(); - private Type _Type; + private readonly List Items = new List(); + private Type Type; public void Add(T item) { @@ -34,12 +35,12 @@ public IEnumerable Get() public Type GetGenericType() { - if (_Type == null) + if (Type == null) { - _Type = typeof(T); + Type = typeof(T); } - return _Type; + return Type; } public void Remove(T item) @@ -182,5 +183,14 @@ private static void SetValueSafely(object entity, PropertyInfo property, string public abstract void Load(XElement xElement); public abstract T Get(object value); + public IEnumerator GetEnumerator() + { + return Items.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Items.GetEnumerator(); + } } } From e335a27701a2df0523ae1a61913b9d6ae1006902 Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Sun, 25 Sep 2022 18:29:28 -0700 Subject: [PATCH 05/13] Use netstandard2.1 --- RetsSdk/RetsSdk.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RetsSdk/RetsSdk.csproj b/RetsSdk/RetsSdk.csproj index d19fc3e..67f0f7d 100644 --- a/RetsSdk/RetsSdk.csproj +++ b/RetsSdk/RetsSdk.csproj @@ -1,7 +1,7 @@ - net6.0 + netstandard2.1 true true CrestApps From b3f9c49d536a97866c42db0c83c2ab6503304092 Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Sun, 25 Sep 2022 18:29:35 -0700 Subject: [PATCH 06/13] Add IEnumerable --- RetsSdk/Contracts/IMetadataCollection.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RetsSdk/Contracts/IMetadataCollection.cs b/RetsSdk/Contracts/IMetadataCollection.cs index a4c5230..4efc8d2 100644 --- a/RetsSdk/Contracts/IMetadataCollection.cs +++ b/RetsSdk/Contracts/IMetadataCollection.cs @@ -1,10 +1,11 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Xml.Linq; namespace CrestApps.RetsSdk.Contracts { - public interface IMetadataCollection where T : class + public interface IMetadataCollection : IEnumerable where T : class { void Add(T resource); void Remove(T resource); @@ -14,11 +15,10 @@ public interface IMetadataCollection where T : class Type GetGenericType(); } - public interface IMetadataCollection + public interface IMetadataCollection : IEnumerable { string Version { get; set; } DateTime Date { get; set; } - } public interface IMetadataCollectionLoad From e0ec09bdd62cd81c95ca7269287279b097fb8743 Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Fri, 18 Nov 2022 23:45:02 -0800 Subject: [PATCH 07/13] Updates --- RetsSdk/Models/Enums/RetsDataType.cs | 3 ++- RetsSdk/Models/RetsField.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/RetsSdk/Models/Enums/RetsDataType.cs b/RetsSdk/Models/Enums/RetsDataType.cs index 0eac649..ab4e1e2 100644 --- a/RetsSdk/Models/Enums/RetsDataType.cs +++ b/RetsSdk/Models/Enums/RetsDataType.cs @@ -11,6 +11,7 @@ public enum RetsDataType Small, Int, Long, - Decimal + Decimal, + @object } } diff --git a/RetsSdk/Models/RetsField.cs b/RetsSdk/Models/RetsField.cs index 469e67b..528001f 100644 --- a/RetsSdk/Models/RetsField.cs +++ b/RetsSdk/Models/RetsField.cs @@ -13,7 +13,7 @@ public class RetsField public string LongName { get; set; } public string DbName { get; set; } public string ShortName { get; set; } - public int MaximumLength { get; set; } + public decimal MaximumLength { get; set; } public RetsDataType? DataType { get; set; } public int? Precision { get; set; } public bool Searchable { get; set; } From 87ac81c7c47f89bb5e82598989dd8490ac183806 Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Sat, 19 Nov 2022 00:02:21 -0800 Subject: [PATCH 08/13] Fix Typo --- RetsSdk/Models/FileObject.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RetsSdk/Models/FileObject.cs b/RetsSdk/Models/FileObject.cs index d1bc70a..a71fb3f 100644 --- a/RetsSdk/Models/FileObject.cs +++ b/RetsSdk/Models/FileObject.cs @@ -12,10 +12,11 @@ public class FileObject : IDisposable public string ContentDescription { get; set; } public string ContentSubDescription { get; set; } public Uri ContentLocation { get; set; } - public string MemeVersion { get; set; } + public string MimeVersion { get; set; } public bool IsPreferred { get; set; } public string Extension { get; set; } public Stream Content { get; set; } + private bool IsDisposed; public bool IsImage => ContentType?.MediaType.StartsWith("image", StringComparison.CurrentCultureIgnoreCase) ?? false; From 3a41c0bb72321b0f35e4e4dac7bb23b816c9bcac Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Sat, 19 Nov 2022 00:02:38 -0800 Subject: [PATCH 09/13] Use netstandard2.0 --- RetsSdk/RetsSdk.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/RetsSdk/RetsSdk.csproj b/RetsSdk/RetsSdk.csproj index 67f0f7d..76f3366 100644 --- a/RetsSdk/RetsSdk.csproj +++ b/RetsSdk/RetsSdk.csproj @@ -1,7 +1,7 @@ - + - netstandard2.1 + netstandard2.0 true true CrestApps @@ -16,6 +16,7 @@ https://github.com/CrestApps/RetsConnector https://avatars1.githubusercontent.com/u/24724371?s=460&v=4 1.0.1 + AnyCPU;x86 From fd3d987493a7ad2802ad531aa36f98c7b9668620 Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Sat, 19 Nov 2022 00:19:05 -0800 Subject: [PATCH 10/13] Fix Rets Autentication on net48 Return if Connection was Successfully. Allow have a RAW rests query, that will be used insted the generated query by the ParameterGroup SearchResult can tell if more Results avaliable on the server after a result. (Server return paginated results) Allow SearchResult return the Total Server Count of a Query. (No data, just the number of results will be avalible) Allow Request have Offset to return the next page of results. Refactor Code Format Easy code to debug --- RetsSdk/Models/SearchRequest.cs | 51 +++++++++++++++++------ RetsSdk/Models/SearchResult.cs | 25 ++++++++---- RetsSdk/Models/SearchResultRow.cs | 5 ++- RetsSdk/Models/SessionResource.cs | 7 +++- RetsSdk/Services/IRetsClient.cs | 4 +- RetsSdk/Services/RetsClient.cs | 60 ++++++++++++++++++---------- RetsSdk/Services/RetsWebRequester.cs | 2 +- 7 files changed, 108 insertions(+), 46 deletions(-) diff --git a/RetsSdk/Models/SearchRequest.cs b/RetsSdk/Models/SearchRequest.cs index 8d74dc4..9633198 100644 --- a/RetsSdk/Models/SearchRequest.cs +++ b/RetsSdk/Models/SearchRequest.cs @@ -1,4 +1,5 @@ -using System; +using JetBrains.Annotations; +using System; using System.Collections.Generic; using System.Linq; @@ -9,11 +10,17 @@ public class SearchRequest public string SearchType { get; set; } public string Class { get; set; } public string QueryType { get; set; } = "DMQL2"; - public int Count { get; set; } = 0; + public CountType Count { get; set; } = CountType.NoCount; public string Format { get; set; } = "COMPACT-DECODED"; // COMPACT-DECODED public string RestrictedIndicator { get; set; } = "****"; public int Limit { get; set; } = int.MaxValue; + public int Offset { get; set; } = 1; + public int StandardNames { get; set; } = 0; + + [CanBeNull] + public string RawQuery { get; set; } = null; + public QueryParameterGroup ParameterGroup { get; set; } private List Columns = new List(); @@ -62,22 +69,42 @@ public void RemoveColumns(IEnumerable columnNames) Columns = Columns.Where(x => !columnNames.Contains(x)).ToList(); } - public bool HasColumns() - { - return Columns.Any(); - } - - + public bool HasColumns() => Columns.Any(); + public bool HasColumn(string columnName) { - bool exists = Columns.Any(x => x.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)); - + var exists = Columns.Any(x => x.Equals(columnName, StringComparison.CurrentCultureIgnoreCase)); return exists; } - public IEnumerable GetColumns() + public IEnumerable GetColumns() => Columns.Distinct(); + + public SearchRequest Clone() { - return Columns.Distinct(); + var newSr = new SearchRequest() + { + SearchType = this.SearchType, + Class = this.Class, + QueryType = this.QueryType, + Count = this.Count, + Format = this.Format, + RestrictedIndicator = this.RestrictedIndicator, + Limit = this.Limit, + Offset = this.Offset, + StandardNames = this.StandardNames, + RawQuery = this.RawQuery, + ParameterGroup = this.ParameterGroup, + }; + newSr.AddColumns(this.Columns); + return newSr; } } + + public enum CountType + { + NoCount = 0, + CountWithData = 1, + OnlyCount = 2 + } + } diff --git a/RetsSdk/Models/SearchResult.cs b/RetsSdk/Models/SearchResult.cs index 7dcd2fb..9ae204c 100644 --- a/RetsSdk/Models/SearchResult.cs +++ b/RetsSdk/Models/SearchResult.cs @@ -6,11 +6,14 @@ namespace CrestApps.RetsSdk.Models { public class SearchResult { - public RetsResource Resource { get; private set; } - public string ClassName { get; private set; } + public RetsResource Resource { get; } + public string ClassName { get; } + private string RestrictedValue; private string[] Columns { get; set; } - private Dictionary Rows { get; set; } + private Dictionary Rows { get; } + public bool HasMoreRows { get; internal set; } + public int? ServerCount { get; internal set; } public SearchResult(RetsResource resource, string className, string restrictedValue) { @@ -38,7 +41,17 @@ public bool AddRow(SearchResultRow row) throw new ArgumentNullException($"{nameof(row)} cannot be null."); } - return Rows.TryAdd(row.PrimaryKeyValue, row); + //return Rows.TryAdd(row.PrimaryKeyValue, row); + + if (Rows.ContainsKey(row.PrimaryKeyValue)) + { + return false; + } + else + { + Rows.Add(row.PrimaryKeyValue, row); + return true; + } } public bool RemoveRow(string primaryKeyValue) @@ -68,7 +81,6 @@ public bool RemoveRow(SearchResultRow row) public IEnumerable Pluck(string columnName) { var values = Rows.Select(x => x.Value.Get(columnName)); - return values; } @@ -76,7 +88,6 @@ public IEnumerable Pluck(string columnName) where T : struct { IEnumerable values = Rows.Select(x => x.Value.Get(columnName).Get()); - return values; } @@ -84,7 +95,6 @@ public IEnumerable Pluck(string columnName) where T : struct { IEnumerable values = Rows.Select(x => x.Value.Get(columnName).GetNullable()); - return values; } @@ -103,7 +113,6 @@ public void SetColumns(string[] columns) Columns = columns ?? throw new ArgumentNullException($"{nameof(columns)} cannot be null."); } - public void SetColumns(IEnumerable columns) { SetColumns(columns?.AsEnumerable()); diff --git a/RetsSdk/Models/SearchResultRow.cs b/RetsSdk/Models/SearchResultRow.cs index 6103a6b..4dce175 100644 --- a/RetsSdk/Models/SearchResultRow.cs +++ b/RetsSdk/Models/SearchResultRow.cs @@ -50,7 +50,10 @@ public SearchResultRow(string[] columns, string[] values, string primaryKeyColum value.SetIsPrimaryKeyValue(keyIndex == index); value.SetIsRestricted(RestrictedValue); - Values.TryAdd(columns[index].ToLower(), value); + if (!Values.ContainsKey(columns[index].ToLower())) + { + Values.Add(columns[index].ToLower(), value); + } } } diff --git a/RetsSdk/Models/SessionResource.cs b/RetsSdk/Models/SessionResource.cs index ce3b4d3..4ec5a16 100644 --- a/RetsSdk/Models/SessionResource.cs +++ b/RetsSdk/Models/SessionResource.cs @@ -25,8 +25,11 @@ public void AddCapability(Capability name, string url) { return; } - - Capabilities.TryAdd(name, uri); + + if (!Capabilities.ContainsKey(name)) + { + Capabilities.Add(name, uri); + } } public Uri GetCapability(Capability name) diff --git a/RetsSdk/Services/IRetsClient.cs b/RetsSdk/Services/IRetsClient.cs index 3d1d58f..3c8f305 100644 --- a/RetsSdk/Services/IRetsClient.cs +++ b/RetsSdk/Services/IRetsClient.cs @@ -8,7 +8,9 @@ namespace CrestApps.RetsSdk.Services { public interface IRetsClient { - Task Connect(); + bool IsConnected { get; } + + Task Connect(); Task Disconnect(); Task Search(SearchRequest request); diff --git a/RetsSdk/Services/RetsClient.cs b/RetsSdk/Services/RetsClient.cs index 479f496..1f5cecd 100644 --- a/RetsSdk/Services/RetsClient.cs +++ b/RetsSdk/Services/RetsClient.cs @@ -33,9 +33,14 @@ public RetsClient(IRetsSession session, IRetsRequester requester, ILogger Session.IsStarted(); + + public async Task Connect() { - await Session.Start(); + if (Session.IsStarted()) + return true; + + return await Session.Start(); } public async Task Disconnect() @@ -54,8 +59,7 @@ public async Task Search(SearchRequest request) if (resource == null) { - string message = string.Format("The provided '{0}' is not valid. You can get a list of all valid value by calling '{1}' method on the Session object.", nameof(SearchRequest.SearchType), nameof(GetResourcesMetadata)); - + var message = $"The provided '{nameof(SearchRequest.SearchType)}' is not valid. You can get a list of all valid value by calling '{nameof(GetResourcesMetadata)}' method on the Session object."; throw new Exception(message); } @@ -65,12 +69,15 @@ public async Task Search(SearchRequest request) query.Add("SearchType", request.SearchType); query.Add("Class", request.Class); query.Add("QueryType", request.QueryType); - query.Add("Count", request.Count.ToString()); query.Add("Format", request.Format); + + query.Add("Count", ((int) request.Count).ToString()); query.Add("Limit", request.Limit.ToString()); + query.Add("Offset", request.Offset.ToString()); + query.Add("StandardNames", request.StandardNames.ToString()); query.Add("RestrictedIndicator", request.RestrictedIndicator); - query.Add("Query", request.ParameterGroup.ToString()); + query.Add("Query", request.RawQuery ?? request.ParameterGroup.ToString()); if (request.HasColumns()) { @@ -88,9 +95,9 @@ public async Task Search(SearchRequest request) return await Requester.Get(uriBuilder.Uri, async (response) => { - using (Stream stream = await GetStream(response)) + using (var stream = await GetStream(response)) { - XDocument doc = XDocument.Load(stream); + var doc = XDocument.Load(stream); int code = GetReplayCode(doc.Root); @@ -98,28 +105,35 @@ public async Task Search(SearchRequest request) var result = new SearchResult(resource, request.Class, request.RestrictedIndicator); - if (code == 0) - { - char delimiterValue = GetCompactDelimiter(doc); + if (code != 0) + return result; - XNamespace ns = doc.Root.GetDefaultNamespace(); - XElement columns = doc.Descendants(ns + "COLUMNS").FirstOrDefault(); + char delimiterValue = GetCompactDelimiter(doc); + + XNamespace ns = doc.Root.GetDefaultNamespace(); + XElement columns = doc.Descendants(ns + "COLUMNS").FirstOrDefault(); - IEnumerable records = doc.Descendants(ns + "DATA"); + var records = doc.Descendants(ns + "DATA"); + if (columns != null) + { string[] tableColumns = columns.Value.Split(delimiterValue); result.SetColumns(tableColumns); foreach (var record in records) { string[] fields = record.Value.Split(delimiterValue); - SearchResultRow row = new SearchResultRow(tableColumns, fields, resource.KeyField, request.RestrictedIndicator); - result.AddRow(row); } } + var maxRows = doc.Descendants(ns + "MAXROWS").ToArray(); + result.HasMoreRows = maxRows.Length > 0; // + + var count = doc.Descendants(ns + "COUNT").ToArray(); + result.ServerCount = count.Length == 0 ? (int?) null : int.Parse(count[0].Attribute("Records").Value); // + return result; } }, Session.Resource); @@ -337,7 +351,7 @@ public async Task> GetObject(string resource, string typ var query = HttpUtility.ParseQueryString(uriBuilder.Query); query.Add("Resource", resource); query.Add("Type", type); - query.Add("ID", string.Join(',', ids.Select(x => x.ToString()))); + query.Add("ID", string.Join(",", ids.Select(x => x.ToString()))); query.Add("Location", useLocation ? "1" : "0"); uriBuilder.Query = query.ToString(); @@ -400,12 +414,16 @@ public async Task> GetObject(string resource, string typ if (!message.Headers.Contains("Preferred") && response.Headers.TryGetValues("Preferred", out var preferreds)) { message.Headers.Add("Preferred", preferreds.FirstOrDefault()); - } + } + if (!message.Headers.Contains("Location") && response.Headers.TryGetValues("Location", out var locations)) + { + message.Headers.Add("Location", locations.FirstOrDefault()); + } + if (message.ContentId == null && response.Headers.TryGetValues("Content-Id", out var contentIds)) { message.ContentId = contentIds.FirstOrDefault(); } - if (message.ContentLocation == null && response.Headers.TryGetValues("Content-Location", out var contentLocations)) { message.ContentLocation = new Uri(contentLocations.FirstOrDefault()); @@ -547,7 +565,7 @@ protected FileObject ProcessMessage(MimePart message) ContentDescription = message.Headers["Content-Description"], ContentSubDescription = message.Headers["Content-Sub-Description"], ContentLocation = message.ContentLocation ?? (message.Headers["Location"] != null ? new Uri(message.Headers["Location"]) : null), - MemeVersion = message.Headers["MIME-Version"], + MimeVersion = message.Headers["MIME-Version"], Extension = MimeTypeMap.GetExtension(message.ContentType.MimeType) }; @@ -566,7 +584,7 @@ protected FileObject ProcessMessage(MimePart message) file.IsPreferred = isPreferred; } - if (message.ContentLocation == null) + if (file.ContentLocation == null) { file.Content = new MemoryStream(); message.Content.DecodeTo(file.Content); diff --git a/RetsSdk/Services/RetsWebRequester.cs b/RetsSdk/Services/RetsWebRequester.cs index b55c866..061a1df 100644 --- a/RetsSdk/Services/RetsWebRequester.cs +++ b/RetsSdk/Services/RetsWebRequester.cs @@ -89,7 +89,7 @@ private HttpClient GetAuthenticatedClient() var credCache = new CredentialCache(); credCache.Add(new Uri(Options.LoginUrl), Options.Type.ToString(), new NetworkCredential(Options.Username, Options.Password)); - return new HttpClient(new HttpClientHandler { Credentials = credCache }); + return new HttpClient(new HttpClientHandler { Credentials = credCache, UseCookies = false }); } HttpClient client = HttpClientFactory.CreateClient(); From 51c5cdf95e50ece0825420f416be7435015a3cd8 Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Sat, 19 Nov 2022 00:19:23 -0800 Subject: [PATCH 11/13] Use netstandard2.0 --- RetsSdk/Models/RetsVersion.cs | 2 +- RetsSdk/Services/RetsSession.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/RetsSdk/Models/RetsVersion.cs b/RetsSdk/Models/RetsVersion.cs index 1ac4e8e..4a13588 100644 --- a/RetsSdk/Models/RetsVersion.cs +++ b/RetsSdk/Models/RetsVersion.cs @@ -54,7 +54,7 @@ public static SupportedRetsVersion Make(string version) var castable = Str.PrependOnce(v, "Version_"); - return Enum.Parse(castable); + return (SupportedRetsVersion) Enum.Parse(typeof(SupportedRetsVersion), castable); } } diff --git a/RetsSdk/Services/RetsSession.cs b/RetsSdk/Services/RetsSession.cs index 783b27e..bc5cbc9 100644 --- a/RetsSdk/Services/RetsSession.cs +++ b/RetsSdk/Services/RetsSession.cs @@ -53,7 +53,7 @@ public async Task Start() XElement element = doc.Descendants(ns + "RETS-RESPONSE").FirstOrDefault() ?? throw new RetsParsingException("Unable to find the RETS-RESPONSE element in the response."); - var parts = element.FirstNode.ToString().Split(Environment.NewLine); + var parts = element.FirstNode.ToString().Split(new []{'\r', '\n'}); var cookie = response.Headers.GetValues("Set-Cookie").FirstOrDefault(); return GetRetsResource(parts, cookie); @@ -133,7 +133,6 @@ protected string ExtractSessionId(string cookie) return null; } - public bool IsStarted() { return _Resource != null; From a5576224026fd3a24dc4d34213edf420ced02daf Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Wed, 30 Nov 2022 22:37:11 -0800 Subject: [PATCH 12/13] Add Comment --- RetsSdk/Services/RetsWebRequester.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RetsSdk/Services/RetsWebRequester.cs b/RetsSdk/Services/RetsWebRequester.cs index 061a1df..e0594de 100644 --- a/RetsSdk/Services/RetsWebRequester.cs +++ b/RetsSdk/Services/RetsWebRequester.cs @@ -89,6 +89,10 @@ private HttpClient GetAuthenticatedClient() var credCache = new CredentialCache(); credCache.Add(new Uri(Options.LoginUrl), Options.Type.ToString(), new NetworkCredential(Options.Username, Options.Password)); + // The UseCookies and DefaultRequestHeaders.Add("Cookie", ...) have different behavior in net48 and net6. + // We need to force UseCookies = false to both have the same expect behavior + // See: https://stackoverflow.com/a/13287224 + return new HttpClient(new HttpClientHandler { Credentials = credCache, UseCookies = false }); } From 306ea76d39464f6f42f6ca2bb4c28d611deec557 Mon Sep 17 00:00:00 2001 From: Rafael Cordeiro Date: Wed, 30 Nov 2022 22:38:54 -0800 Subject: [PATCH 13/13] Remove Dead Code --- RetsSdk/Models/SearchResult.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/RetsSdk/Models/SearchResult.cs b/RetsSdk/Models/SearchResult.cs index 9ae204c..080ab42 100644 --- a/RetsSdk/Models/SearchResult.cs +++ b/RetsSdk/Models/SearchResult.cs @@ -41,8 +41,6 @@ public bool AddRow(SearchResultRow row) throw new ArgumentNullException($"{nameof(row)} cannot be null."); } - //return Rows.TryAdd(row.PrimaryKeyValue, row); - if (Rows.ContainsKey(row.PrimaryKeyValue)) { return false;