From ad268b09dbc6c108904841b210a11194cec934d4 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 10 Feb 2026 09:49:19 -0500 Subject: [PATCH 01/10] BX-68163: Adds Events and SemanticsVector endpoints --- examples/Events.cs | 51 +++++++++++++++++++++++++ examples/SemanticsVector.cs | 52 ++++++++++++++++++++++++++ rosette_api/EventsEndpoint.cs | 12 ++++++ rosette_api/RosetteAPI.cs | 2 +- rosette_api/SemanticsVectorEndpoint.cs | 12 ++++++ tests/TestForValidEndpoint.cs | 16 ++++++++ 6 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 examples/Events.cs create mode 100644 examples/SemanticsVector.cs create mode 100644 rosette_api/EventsEndpoint.cs create mode 100644 rosette_api/SemanticsVectorEndpoint.cs diff --git a/examples/Events.cs b/examples/Events.cs new file mode 100644 index 0000000..28ef620 --- /dev/null +++ b/examples/Events.cs @@ -0,0 +1,51 @@ +using rosette_api; + +namespace examples { + class Events + { + /// + /// RunEndpoint runs the example. By default the endpoint will be run against the Rosette Cloud Service. + /// An optional alternate URL may be provided, i.e. for an on-premise solution. + /// + /// Required api key (obtained from Basis Technology) + /// Optional alternate URL + private void RunEndpoint(string apiKey, string? altUrl = null) { + try { + RosetteAPI api = new RosetteAPI(apiKey); + if (!string.IsNullOrEmpty(altUrl)) { + api.UseAlternateURL(altUrl); + } + string events_text_data = @"I am looking for flights to Super Bowl 2022 in Inglewood, LA."; + + EventsEndpoint endpoint = new EventsEndpoint(events_text_data); + RosetteResponse response = endpoint.Call(api); + + // Print out the response headers + foreach (KeyValuePair h in response.Headers) { + Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); + } + // Print out the content in JSON format. The Content property returns an IDictionary. + Console.WriteLine(response.ContentAsJson(pretty: true)); + + // Retrieve the Events with full ADM + response = endpoint.SetUrlParameter("output", "rosette").Call(api); + Console.WriteLine(response.ContentAsJson(pretty: true)); + } + catch (Exception e) { + Console.WriteLine("Exception: " + e.Message); + } + } + /// + /// Main is a simple entrypoint for command line calling of the endpoint examples + /// + /// Command line args, expects API Key, (optional) alt URL + static void Main(string[] args) { + if (args.Length != 0) { + new Events().RunEndpoint(args[0], args.Length > 1 ? args[1] : null); + } + else { + Console.WriteLine("An API Key is required"); + } + } + } +} \ No newline at end of file diff --git a/examples/SemanticsVector.cs b/examples/SemanticsVector.cs new file mode 100644 index 0000000..68dfb14 --- /dev/null +++ b/examples/SemanticsVector.cs @@ -0,0 +1,52 @@ +using rosette_api; + +namespace examples +{ + class SemanticsVector + { + /// + /// RunEndpoint runs the example. By default the endpoint will be run against the Rosette Cloud Service. + /// An optional alternate URL may be provided, i.e. for an on-premise solution. + /// + /// Required api key (obtained from Basis Technology) + /// Optional alternate URL + private void RunEndpoint(string apiKey, string? altUrl = null) + { + try + { + RosetteAPI api = new RosetteAPI(apiKey); + if (!string.IsNullOrEmpty(altUrl)) + { + api.UseAlternateURL(altUrl); + } + string semantic_vectors_data = @"Cambridge, Massachusetts"; + SemanticsVectorEndpoint endpoint = new SemanticsVectorEndpoint(semantic_vectors_data); + RosetteResponse response = endpoint.Call(api); + foreach (KeyValuePair h in response.Headers) + { + Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); + } + Console.WriteLine(response.ContentAsJson(pretty: true)); + } + catch (Exception e) + { + Console.WriteLine("Exception: " + e.Message); + } + } + /// + /// Main is a simple entrypoint for command line calling of the endpoint examples + /// + /// Command line args, expects API Key, (optional) alt URL + static void Main(string[] args) + { + if (args.Length != 0) + { + new SemanticsVector().RunEndpoint(args[0], args.Length > 1 ? args[1] : null); + } + else + { + Console.WriteLine("An API Key is required"); + } + } + } +} diff --git a/rosette_api/EventsEndpoint.cs b/rosette_api/EventsEndpoint.cs new file mode 100644 index 0000000..18bc02d --- /dev/null +++ b/rosette_api/EventsEndpoint.cs @@ -0,0 +1,12 @@ +namespace rosette_api; + +public class EventsEndpoint : ContentBasedEndpoint +{ + /// + /// EventsEndpoint returns the events extracted from the endpoint + /// + /// text, Uri object or FileStream + public EventsEndpoint(object content) : base("events", content) + { + } +} \ No newline at end of file diff --git a/rosette_api/RosetteAPI.cs b/rosette_api/RosetteAPI.cs index 7e25f0b..13ea213 100755 --- a/rosette_api/RosetteAPI.cs +++ b/rosette_api/RosetteAPI.cs @@ -176,7 +176,7 @@ private RosetteAPI Prepare(bool forceUpdate=false) { } // Standard headers, which are required for Rosette API - AddRequestHeader("X-RosetteAPI-Key", APIKey); + AddRequestHeader("X-BabelStreetAPI-Key", APIKey); AddRequestHeader("User-Agent", string.Format("RosetteAPICsharp/{0}-{1}", Version, Environment.Version.ToString())); AddRequestHeader("X-RosetteAPI-Binding", "csharp"); AddRequestHeader("X-RosetteAPI-Binding-Version", Version); diff --git a/rosette_api/SemanticsVectorEndpoint.cs b/rosette_api/SemanticsVectorEndpoint.cs new file mode 100644 index 0000000..423e736 --- /dev/null +++ b/rosette_api/SemanticsVectorEndpoint.cs @@ -0,0 +1,12 @@ +namespace rosette_api; + +public class SemanticsVectorEndpoint : ContentBasedEndpoint +{ + /// + /// SemanticVectorsEndpoint returns the relationships between entities in the input text + /// + /// text, Uri object or FileStream + public SemanticsVectorEndpoint(object content) : base("semantics/vector", content) + { + } +} \ No newline at end of file diff --git a/tests/TestForValidEndpoint.cs b/tests/TestForValidEndpoint.cs index 08dee56..fd3fca0 100755 --- a/tests/TestForValidEndpoint.cs +++ b/tests/TestForValidEndpoint.cs @@ -19,6 +19,14 @@ public void EntitiesEndpoint() { Assert.Equal("foo", e.Content); } + [Fact] + public void EventsEndpoint() + { + EventsEndpoint e = new EventsEndpoint("foo"); + Assert.Equal("events", e.Endpoint); + Assert.Equal("foo", e.Content); + } + [Fact] public void InfoEndpoint() { InfoEndpoint i = new InfoEndpoint(); @@ -67,6 +75,14 @@ public void RelationshipsEndpoint() { Assert.Equal("foo", r.Content); } + [Fact] + public void SemanticVectorsEndpoint() + { + SemanticsVectorEndpoint s = new SemanticsVectorEndpoint("foo"); + Assert.Equal("semantics/vector", s.Endpoint); + Assert.Equal("foo", s.Content); + } + [Fact] public void SentencesEndpoint() { SentencesEndpoint s = new SentencesEndpoint("foo"); From d4195c89b1145b4f5b54e0c2e4c949c96e5cf0cc Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 10 Feb 2026 11:43:29 -0500 Subject: [PATCH 02/10] BX-68163: Namespace and filename changes --- examples/Categories.cs | 8 +- examples/ConcurrencyTest.cs | 8 +- examples/Entities.cs | 8 +- examples/Events.cs | 8 +- examples/Info.cs | 8 +- examples/Language.cs | 8 +- examples/LanguageMultilingual.cs | 8 +- examples/MorphologyComplete.cs | 9 +- examples/MorphologyCompoundComponents.cs | 11 +- examples/MorphologyHanReadings.cs | 9 +- examples/MorphologyLemmas.cs | 9 +- examples/MorphologyPartsOfSpeech.cs | 9 +- examples/NameDeduplication.cs | 10 +- examples/NameMatch.cs | 9 +- examples/NameTranslation.cs | 8 +- examples/Ping.cs | 6 +- examples/README.md | 8 +- examples/Relationships.cs | 8 +- examples/SemanticsVector.cs | 8 +- examples/Sentences.cs | 8 +- examples/Sentiment.cs | 8 +- examples/SyntaxDependencies.cs | 8 +- examples/TextEmbedding.cs | 8 +- examples/Tokens.cs | 8 +- examples/Topics.cs | 8 +- examples/Transliteration.cs | 8 +- rosette_api/{RosetteAPI.cs => ApiClient.cs} | 462 ++++++++--------- .../Categories.cs} | 26 +- .../Core/ContentEndpointBase.cs} | 8 +- .../Core/EndpointBase.cs} | 182 +++---- .../Core/EndpointExecutor.cs} | 486 +++++++++--------- .../Entities.cs} | 26 +- .../Events.cs} | 8 +- rosette_api/Endpoints/Info.cs | 11 + .../Language.cs} | 26 +- .../Morphology.cs} | 208 ++++---- .../NameDeduplication.cs} | 154 +++--- .../NameSimilarity.cs} | 42 +- .../NameTranslation.cs} | 288 +++++------ rosette_api/Endpoints/Ping.cs | 11 + .../Relationships.cs} | 24 +- rosette_api/Endpoints/SemanticsVector.cs | 14 + .../Sentences.cs} | 24 +- .../Sentiment.cs} | 26 +- .../SyntaxDependencies.cs} | 22 +- .../TextEmbedding.cs} | 140 ++--- rosette_api/Endpoints/Tokens.cs | 12 + rosette_api/Endpoints/Topics.cs | 12 + rosette_api/Endpoints/Transliteration.cs | 12 + rosette_api/InfoEndpoint.cs | 9 - rosette_api/{RosetteName.cs => Name.cs} | 160 +++--- rosette_api/PingEndpoint.cs | 9 - .../{RosetteResponse.cs => Response.cs} | 6 +- rosette_api/SemanticsVectorEndpoint.cs | 12 - rosette_api/TokensEndpoint.cs | 10 - rosette_api/TopicsEndpoint.cs | 10 - rosette_api/TransliterationEndpoint.cs | 10 - tests/{TestAPI.cs => ApiClientTests.cs} | 195 ++++--- ...EndpointCommon.cs => EndpointBaseTests.cs} | 18 +- ...tFunctions.cs => EndpointExecutorTests.cs} | 279 +++++----- tests/TestForValidEndpoint.cs | 51 +- tests/TestNameDeduplication.cs | 29 +- tests/TestNameSimilarity.cs | 11 +- tests/TestNameTranslation.cs | 9 +- tests/TestRosetteName.cs | 15 +- tests/TestRosetteResponse.cs | 8 +- 66 files changed, 1656 insertions(+), 1625 deletions(-) rename rosette_api/{RosetteAPI.cs => ApiClient.cs} (90%) mode change 100755 => 100644 rename rosette_api/{CategoriesEndpoint.cs => Endpoints/Categories.cs} (50%) mode change 100755 => 100644 rename rosette_api/{ContentBasedEndpoint.cs => Endpoints/Core/ContentEndpointBase.cs} (91%) rename rosette_api/{EndpointCommon.cs => Endpoints/Core/EndpointBase.cs} (86%) mode change 100755 => 100644 rename rosette_api/{EndpointFunctions.cs => Endpoints/Core/EndpointExecutor.cs} (92%) mode change 100755 => 100644 rename rosette_api/{EntitiesEndpoint.cs => Endpoints/Entities.cs} (51%) mode change 100755 => 100644 rename rosette_api/{EventsEndpoint.cs => Endpoints/Events.cs} (51%) create mode 100644 rosette_api/Endpoints/Info.cs rename rosette_api/{LanguageEndpoint.cs => Endpoints/Language.cs} (51%) mode change 100755 => 100644 rename rosette_api/{MorphologyEndpoint.cs => Endpoints/Morphology.cs} (84%) mode change 100755 => 100644 rename rosette_api/{NameDeduplicationEndpoint.cs => Endpoints/NameDeduplication.cs} (78%) mode change 100755 => 100644 rename rosette_api/{NameSimilarityEndpoint.cs => Endpoints/NameSimilarity.cs} (63%) mode change 100755 => 100644 rename rosette_api/{NameTranslationEndpoint.cs => Endpoints/NameTranslation.cs} (82%) mode change 100755 => 100644 create mode 100644 rosette_api/Endpoints/Ping.cs rename rosette_api/{RelationshipsEndpoint.cs => Endpoints/Relationships.cs} (50%) mode change 100755 => 100644 create mode 100644 rosette_api/Endpoints/SemanticsVector.cs rename rosette_api/{SentencesEndpoint.cs => Endpoints/Sentences.cs} (51%) mode change 100755 => 100644 rename rosette_api/{SentimentEndpoint.cs => Endpoints/Sentiment.cs} (55%) mode change 100755 => 100644 rename rosette_api/{SyntaxDependenciesEndpoint.cs => Endpoints/SyntaxDependencies.cs} (55%) mode change 100755 => 100644 rename rosette_api/{TextEmbeddingEndpoint.cs => Endpoints/TextEmbedding.cs} (78%) mode change 100755 => 100644 create mode 100644 rosette_api/Endpoints/Tokens.cs create mode 100644 rosette_api/Endpoints/Topics.cs create mode 100644 rosette_api/Endpoints/Transliteration.cs delete mode 100755 rosette_api/InfoEndpoint.cs rename rosette_api/{RosetteName.cs => Name.cs} (85%) mode change 100755 => 100644 delete mode 100755 rosette_api/PingEndpoint.cs rename rosette_api/{RosetteResponse.cs => Response.cs} (96%) mode change 100755 => 100644 delete mode 100644 rosette_api/SemanticsVectorEndpoint.cs delete mode 100755 rosette_api/TokensEndpoint.cs delete mode 100755 rosette_api/TopicsEndpoint.cs delete mode 100755 rosette_api/TransliterationEndpoint.cs rename tests/{TestAPI.cs => ApiClientTests.cs} (82%) mode change 100755 => 100644 rename tests/{TestEndpointCommon.cs => EndpointBaseTests.cs} (62%) mode change 100755 => 100644 rename tests/{TestEndpointFunctions.cs => EndpointExecutorTests.cs} (71%) mode change 100755 => 100644 diff --git a/examples/Categories.cs b/examples/Categories.cs index 4eaa3f7..463e912 100644 --- a/examples/Categories.cs +++ b/examples/Categories.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Categories @@ -11,15 +11,15 @@ class Categories /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string categories_text_data = @"Sony Pictures is planning to shoot a good portion of the new ""Ghostbusters"" in Boston as well."; - CategoriesEndpoint endpoint = new CategoriesEndpoint(categories_text_data); + Rosette.Api.Endpoints.Categories endpoint = new Rosette.Api.Endpoints.Categories(categories_text_data); - RosetteResponse response = endpoint.Call(api); + Response response = endpoint.Call(api); //The results of the API call will come back in the form of a Dictionary Console.WriteLine(response.ContentAsJson(pretty: true)); } diff --git a/examples/ConcurrencyTest.cs b/examples/ConcurrencyTest.cs index 37eedca..c3520aa 100644 --- a/examples/ConcurrencyTest.cs +++ b/examples/ConcurrencyTest.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { public class ConcurrencyTest { @@ -15,7 +15,7 @@ public class ConcurrencyTest { /// Optional alternate URL private static async Task TestConcurrency(string apiKey, string altUrl) { var tasks = new List(); - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } @@ -29,9 +29,9 @@ private static async Task TestConcurrency(string apiKey, string altUrl) { Console.WriteLine("Test complete"); } - private static Task runLookup(int taskId, RosetteAPI api) { + private static Task runLookup(int taskId, ApiClient api) { string entities_text_data = @"The Securities and Exchange Commission today announced the leadership of the agency’s trial unit. Bridget Fitzpatrick has been named Chief Litigation Counsel of the SEC and David Gottesman will continue to serve as the agency’s Deputy Chief Litigation Counsel. Since December 2016, Ms. Fitzpatrick and Mr. Gottesman have served as Co-Acting Chief Litigation Counsel. In that role, they were jointly responsible for supervising the trial unit at the agency’s Washington D.C. headquarters as well as coordinating with litigators in the SEC’s 11 regional offices around the country."; - EntitiesEndpoint endpoint = new EntitiesEndpoint(entities_text_data); + Rosette.Api.Endpoints.Entities endpoint = new Rosette.Api.Endpoints.Entities(entities_text_data); foreach (int call in Enumerable.Range(0, calls)) { Console.WriteLine("Task ID: {0} call {1}", taskId, call); try { diff --git a/examples/Entities.cs b/examples/Entities.cs index 4b2d85d..6314ea2 100755 --- a/examples/Entities.cs +++ b/examples/Entities.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Entities @@ -11,14 +11,14 @@ class Entities /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string entities_text_data = @"The Securities and Exchange Commission today announced the leadership of the agency’s trial unit. Bridget Fitzpatrick has been named Chief Litigation Counsel of the SEC and David Gottesman will continue to serve as the agency’s Deputy Chief Litigation Counsel. Since December 2016, Ms. Fitzpatrick and Mr. Gottesman have served as Co-Acting Chief Litigation Counsel. In that role, they were jointly responsible for supervising the trial unit at the agency’s Washington D.C. headquarters as well as coordinating with litigators in the SEC’s 11 regional offices around the country."; - EntitiesEndpoint endpoint = new EntitiesEndpoint(entities_text_data).SetGenre("social-media"); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.Entities endpoint = new Rosette.Api.Endpoints.Entities(entities_text_data).SetGenre("social-media"); + Response response = endpoint.Call(api); // Print out the response headers foreach (KeyValuePair h in response.Headers) { diff --git a/examples/Events.cs b/examples/Events.cs index 28ef620..9aaa774 100644 --- a/examples/Events.cs +++ b/examples/Events.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Events @@ -11,14 +11,14 @@ class Events /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl = null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string events_text_data = @"I am looking for flights to Super Bowl 2022 in Inglewood, LA."; - EventsEndpoint endpoint = new EventsEndpoint(events_text_data); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.Events endpoint = new Rosette.Api.Endpoints.Events(events_text_data); + Response response = endpoint.Call(api); // Print out the response headers foreach (KeyValuePair h in response.Headers) { diff --git a/examples/Info.cs b/examples/Info.cs index 97dc514..3702f6f 100644 --- a/examples/Info.cs +++ b/examples/Info.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Info @@ -11,12 +11,12 @@ class Info /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } - InfoEndpoint endpoint = new InfoEndpoint(); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.Info endpoint = new Rosette.Api.Endpoints.Info(); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); diff --git a/examples/Language.cs b/examples/Language.cs index 000c16a..9c1f0fb 100644 --- a/examples/Language.cs +++ b/examples/Language.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Language @@ -11,7 +11,7 @@ class Language /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } @@ -20,9 +20,9 @@ private void RunEndpoint(string apiKey, string? altUrl =null) { string language_data = @"Por favor Señorita, says the man."; - LanguageEndpoint endpoint = new LanguageEndpoint(language_data); + Rosette.Api.Endpoints.Language endpoint = new Rosette.Api.Endpoints.Language(language_data); //The results of the API call will come back in the form of a Dictionary - RosetteResponse response = endpoint.Call(api); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/LanguageMultilingual.cs b/examples/LanguageMultilingual.cs index 1a0c378..bbf840b 100644 --- a/examples/LanguageMultilingual.cs +++ b/examples/LanguageMultilingual.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class LanguageMultilingual @@ -11,16 +11,16 @@ class LanguageMultilingual /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string language_multilingual_data = @"On Thursday, as protesters gathered in Washington D.C., the United States Federal Communications Commission under Chairman Ajit Pai voted 3-2 to overturn a 2015 decision, commonly called Net Neutrality, that forbade Internet service providers (ISPs) such as Verizon, Comcast, and AT&T from blocking individual websites or charging websites or customers more for faster load times. Quatre femmes ont été nommées au Conseil de rédaction de la loi du Qatar. Jeudi, le décret royal du Qatar a annoncé que 28 nouveaux membres ont été nommés pour le Conseil de la Choura du pays. ذكرت مصادر أمنية يونانية، أن 9 موقوفين من منظمة ""د هـ ك ب ج"" الذين كانت قد أوقفتهم الشرطة اليونانية في وقت سابق كانوا يخططون لاغتيال الرئيس التركي رجب طيب أردوغان."; - LanguageEndpoint endpoint = new LanguageEndpoint(language_multilingual_data).SetOption("multilingual", true); + Rosette.Api.Endpoints.Language endpoint = new Rosette.Api.Endpoints.Language(language_multilingual_data).SetOption("multilingual", true); //The results of the API call will come back in the form of a Dictionary - RosetteResponse response = endpoint.Call(api); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/MorphologyComplete.cs b/examples/MorphologyComplete.cs index 03dce4e..03bba34 100644 --- a/examples/MorphologyComplete.cs +++ b/examples/MorphologyComplete.cs @@ -1,4 +1,5 @@ -using rosette_api; +using Rosette.Api; +using Rosette.Api.Endpoints; namespace examples { class MorphologyComplete @@ -11,15 +12,15 @@ class MorphologyComplete /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string morphology_complete_data = @"The quick brown fox jumped over the lazy dog. 👍🏾 Yes he did. B)"; //The results of the API call will come back in the form of a Dictionary - MorphologyEndpoint endpoint = new MorphologyEndpoint(morphology_complete_data, MorphologyFeature.complete); + Morphology endpoint = new Morphology(morphology_complete_data, MorphologyFeature.complete); - RosetteResponse response = endpoint.Call(api); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/MorphologyCompoundComponents.cs b/examples/MorphologyCompoundComponents.cs index b553e8f..9f903a4 100644 --- a/examples/MorphologyCompoundComponents.cs +++ b/examples/MorphologyCompoundComponents.cs @@ -1,6 +1,7 @@ -using rosette_api; +using Rosette.Api; +using Rosette.Api.Endpoints; -namespace rosette_apiExamples { +namespace Rosette.ApiExamples { class MorphologyCompoundComponents { /// @@ -11,14 +12,14 @@ class MorphologyCompoundComponents /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string morphology_compound_components_data = @"Rechtsschutzversicherungsgesellschaften"; //The results of the API call will come back in the form of a Dictionary - MorphologyEndpoint endpoint = new MorphologyEndpoint(morphology_compound_components_data, MorphologyFeature.compoundComponents); - RosetteResponse response = endpoint.Call(api); + Morphology endpoint = new Morphology(morphology_compound_components_data, MorphologyFeature.compoundComponents); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/MorphologyHanReadings.cs b/examples/MorphologyHanReadings.cs index 575b56a..4b6403d 100644 --- a/examples/MorphologyHanReadings.cs +++ b/examples/MorphologyHanReadings.cs @@ -1,4 +1,5 @@ -using rosette_api; +using Rosette.Api; +using Rosette.Api.Endpoints; namespace examples { class MorphologyHanReadings @@ -12,13 +13,13 @@ class MorphologyHanReadings private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string morphology_han_readings_data = @"北京大学生物系主任办公室内部会议"; - MorphologyEndpoint endpoint = new MorphologyEndpoint(morphology_han_readings_data, MorphologyFeature.hanReadings); - RosetteResponse response = endpoint.Call(api); + Morphology endpoint = new Morphology(morphology_han_readings_data, MorphologyFeature.hanReadings); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/MorphologyLemmas.cs b/examples/MorphologyLemmas.cs index 7dd53af..2882b66 100644 --- a/examples/MorphologyLemmas.cs +++ b/examples/MorphologyLemmas.cs @@ -1,4 +1,5 @@ -using rosette_api; +using Rosette.Api; +using Rosette.Api.Endpoints; namespace examples { class MorphologyLemmas @@ -11,14 +12,14 @@ class MorphologyLemmas /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string morphology_lemmas_data = @"The fact is that the geese just went back to get a rest and I'm not banking on their return soon"; //The results of the API call will come back in the form of a Dictionary - MorphologyEndpoint endpoint = new MorphologyEndpoint(morphology_lemmas_data, MorphologyFeature.lemmas); - RosetteResponse response = endpoint.Call(api); + Morphology endpoint = new Morphology(morphology_lemmas_data, MorphologyFeature.lemmas); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/MorphologyPartsOfSpeech.cs b/examples/MorphologyPartsOfSpeech.cs index d0c3f30..dec48dc 100644 --- a/examples/MorphologyPartsOfSpeech.cs +++ b/examples/MorphologyPartsOfSpeech.cs @@ -1,4 +1,5 @@ -using rosette_api; +using Rosette.Api; +using Rosette.Api.Endpoints; namespace examples { class MorphologyPartsOfSpeech @@ -11,13 +12,13 @@ class MorphologyPartsOfSpeech /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string morphology_parts_of_speech_data = @"The fact is that the geese just went back to get a rest and I'm not banking on their return soon"; - MorphologyEndpoint endpoint = new MorphologyEndpoint(morphology_parts_of_speech_data, MorphologyFeature.partsOfSpeech); - RosetteResponse response = endpoint.Call(api); + Morphology endpoint = new Morphology(morphology_parts_of_speech_data, MorphologyFeature.partsOfSpeech); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/NameDeduplication.cs b/examples/NameDeduplication.cs index 56ede2b..6bc256f 100644 --- a/examples/NameDeduplication.cs +++ b/examples/NameDeduplication.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class NameDeduplication @@ -11,17 +11,17 @@ class NameDeduplication /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string name_dedupe_data = @"Alice Terry,Alice Thierry,Betty Grable,Betty Gable,Norma Shearer,Norm Shearer,Brigitte Helm,Bridget Helem,Judy Holliday,Julie Halliday"; List dedupe_names = name_dedupe_data.Split(',').ToList(); - List names = dedupe_names.Select(name => new RosetteName(name)).ToList(); + List names = dedupe_names.Select(name => new Name(name)).ToList(); - NameDeduplicationEndpoint endpoint = new NameDeduplicationEndpoint(names).SetThreshold(0.75f); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.NameDeduplication endpoint = new Rosette.Api.Endpoints.NameDeduplication(names).SetThreshold(0.75f); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/NameMatch.cs b/examples/NameMatch.cs index 0dc8570..f6ae460 100644 --- a/examples/NameMatch.cs +++ b/examples/NameMatch.cs @@ -1,4 +1,5 @@ -using rosette_api; +using Rosette.Api; +using Rosette.Api.Endpoints; namespace examples { class NameMatch @@ -11,14 +12,14 @@ class NameMatch /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string matched_name_data1 = @"Michael Jackson"; string matched_name_data2 = @"迈克尔·杰克逊"; - NameSimilarityEndpoint endpoint = new NameSimilarityEndpoint(new RosetteName(matched_name_data1), new RosetteName(matched_name_data2)); - RosetteResponse response = endpoint.Call(api); + NameSimilarity endpoint = new NameSimilarity(new Name(matched_name_data1), new Name(matched_name_data2)); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/NameTranslation.cs b/examples/NameTranslation.cs index 1f4cef7..77fc243 100644 --- a/examples/NameTranslation.cs +++ b/examples/NameTranslation.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class NameTranslation @@ -11,13 +11,13 @@ class NameTranslation /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string translated_name_data = @"معمر محمد أبو منيار القذاف"; - NameTranslationEndpoint endpoint = new NameTranslationEndpoint(translated_name_data, "eng"); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.NameTranslation endpoint = new Rosette.Api.Endpoints.NameTranslation(translated_name_data, "eng"); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/Ping.cs b/examples/Ping.cs index b57e2ca..45fd238 100644 --- a/examples/Ping.cs +++ b/examples/Ping.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Ping @@ -11,11 +11,11 @@ class Ping /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } - RosetteResponse response = new PingEndpoint().Call(api); + Response response = new Rosette.Api.Endpoints.Ping().Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/README.md b/examples/README.md index 3e1b78d..f2321ef 100644 --- a/examples/README.md +++ b/examples/README.md @@ -39,9 +39,9 @@ Here is one way to run the examples. - Build the package from source. ``` cd /dotnet - dotnet restore rosette_api.slnx - dotnet build /p:Configuration=Release rosette_api.slnx - dotnet build /p:Configuration=Debug rosette_api.slnx + dotnet restore Rosette.Api.slnx + dotnet build /p:Configuration=Release Rosette.Api.slnx + dotnet build /p:Configuration=Debug Rosette.Api.slnx ``` @@ -60,7 +60,7 @@ Here is one way to run the examples. dotnet new console --framework net10.0 cp ../Language.cs ./Program.cs - dotnet add reference ../../rosette_api/rosette_api.csproj + dotnet add reference ../../rosette_api/Rosette.Api.csproj ``` diff --git a/examples/Relationships.cs b/examples/Relationships.cs index 8ec12de..7d73973 100644 --- a/examples/Relationships.cs +++ b/examples/Relationships.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Relationships @@ -11,13 +11,13 @@ class Relationships /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string relationships_text_data = @"FLIR Systems is headquartered in Oregon and produces thermal imaging, night vision, and infrared cameras and sensor systems. According to the SEC’s order instituting a settled administrative proceeding, FLIR entered into a multi-million dollar contract to provide thermal binoculars to the Saudi government in November 2008. Timms and Ramahi were the primary sales employees responsible for the contract, and also were involved in negotiations to sell FLIR’s security cameras to the same government officials. At the time, Timms was the head of FLIR’s Middle East office in Dubai."; - RelationshipsEndpoint endpoint = new RelationshipsEndpoint(relationships_text_data); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.Relationships endpoint = new Rosette.Api.Endpoints.Relationships(relationships_text_data); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/SemanticsVector.cs b/examples/SemanticsVector.cs index 68dfb14..f56377a 100644 --- a/examples/SemanticsVector.cs +++ b/examples/SemanticsVector.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { @@ -14,14 +14,14 @@ private void RunEndpoint(string apiKey, string? altUrl = null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string semantic_vectors_data = @"Cambridge, Massachusetts"; - SemanticsVectorEndpoint endpoint = new SemanticsVectorEndpoint(semantic_vectors_data); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.SemanticsVector endpoint = new Rosette.Api.Endpoints.SemanticsVector(semantic_vectors_data); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); diff --git a/examples/Sentences.cs b/examples/Sentences.cs index 17d3dce..dae27b4 100644 --- a/examples/Sentences.cs +++ b/examples/Sentences.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Sentences @@ -11,13 +11,13 @@ class Sentences /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string sentences_data = @"This land is your land. This land is my land, from California to the New York island; from the red wood forest to the Gulf Stream waters. This land was made for you and Me. As I was walking that ribbon of highway, I saw above me that endless skyway: I saw below me that golden valley: This land was made for you and me."; - SentencesEndpoint endpoint = new SentencesEndpoint(sentences_data); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.Sentences endpoint = new Rosette.Api.Endpoints.Sentences(sentences_data); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/Sentiment.cs b/examples/Sentiment.cs index 01f80cc..f416379 100644 --- a/examples/Sentiment.cs +++ b/examples/Sentiment.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Sentiment @@ -11,7 +11,7 @@ class Sentiment /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } @@ -24,10 +24,10 @@ private void RunEndpoint(string apiKey, string? altUrl =null) { sw.Close(); using (FileStream fs = File.OpenRead(newFile)) { - SentimentEndpoint endpoint = new SentimentEndpoint(fs) + Rosette.Api.Endpoints.Sentiment endpoint = new Rosette.Api.Endpoints.Sentiment(fs) .SetFileContentType(@"application/octet-stream") .SetLanguage("eng"); - RosetteResponse response = endpoint.Call(api); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/SyntaxDependencies.cs b/examples/SyntaxDependencies.cs index 0c01c17..dcecf61 100644 --- a/examples/SyntaxDependencies.cs +++ b/examples/SyntaxDependencies.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class SyntaxDependencies @@ -11,13 +11,13 @@ class SyntaxDependencies /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string syntax_dependencies_data = "Yoshinori Ohsumi, a Japanese cell biologist, was awarded the Nobel Prize in Physiology or Medicine on Monday."; - SyntaxDependenciesEndpoint endpoint = new SyntaxDependenciesEndpoint(syntax_dependencies_data); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.SyntaxDependencies endpoint = new Rosette.Api.Endpoints.SyntaxDependencies(syntax_dependencies_data); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/TextEmbedding.cs b/examples/TextEmbedding.cs index d96f5fa..38151cf 100644 --- a/examples/TextEmbedding.cs +++ b/examples/TextEmbedding.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class TextEmbedding @@ -11,13 +11,13 @@ class TextEmbedding /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string embedding_data = @"Cambridge, Massachusetts"; - TextEmbeddingEndpoint endpoint = new TextEmbeddingEndpoint(embedding_data); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.TextEmbedding endpoint = new Rosette.Api.Endpoints.TextEmbedding(embedding_data); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); diff --git a/examples/Tokens.cs b/examples/Tokens.cs index 8adac22..63dcc94 100644 --- a/examples/Tokens.cs +++ b/examples/Tokens.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Tokens @@ -11,13 +11,13 @@ class Tokens /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string tokens_data = @"北京大学生物系主任办公室内部会议"; - TokensEndpoint endpoint = new TokensEndpoint(tokens_data); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.Tokens endpoint = new Rosette.Api.Endpoints.Tokens(tokens_data); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/Topics.cs b/examples/Topics.cs index 75d8431..cf0a01f 100644 --- a/examples/Topics.cs +++ b/examples/Topics.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Topics @@ -11,13 +11,13 @@ class Topics /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string topics_data = @"Lily Collins is in talks to join Nicholas Hoult in Chernin Entertainment and Fox Searchlight's J.R.R. Tolkien biopic Tolkien. Anthony Boyle, known for playing Scorpius Malfoy in the British play Harry Potter and the Cursed Child, also has signed on for the film centered on the famed author. In Tolkien, Hoult will play the author of the Hobbit and Lord of the Rings book series that were later adapted into two Hollywood trilogies from Peter Jackson. Dome Karukoski is directing the project."; - TopicsEndpoint endpoint = new TopicsEndpoint(topics_data); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.Topics endpoint = new Rosette.Api.Endpoints.Topics(topics_data); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/Transliteration.cs b/examples/Transliteration.cs index 3b8e631..055eccc 100644 --- a/examples/Transliteration.cs +++ b/examples/Transliteration.cs @@ -1,4 +1,4 @@ -using rosette_api; +using Rosette.Api; namespace examples { class Transliteration @@ -11,14 +11,14 @@ class Transliteration /// Optional alternate URL private void RunEndpoint(string apiKey, string? altUrl =null) { try { - RosetteAPI api = new RosetteAPI(apiKey); + ApiClient api = new ApiClient(apiKey); if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } string transliteration_data = "ana r2ye7 el gam3a el sa3a 3 el 3asr"; - TransliterationEndpoint endpoint = new TransliterationEndpoint(transliteration_data).SetLanguage("ara"); - RosetteResponse response = endpoint.Call(api); + Rosette.Api.Endpoints.Transliteration endpoint = new Rosette.Api.Endpoints.Transliteration(transliteration_data).SetLanguage("ara"); + Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); diff --git a/rosette_api/RosetteAPI.cs b/rosette_api/ApiClient.cs old mode 100755 new mode 100644 similarity index 90% rename from rosette_api/RosetteAPI.cs rename to rosette_api/ApiClient.cs index 13ea213..20562fe --- a/rosette_api/RosetteAPI.cs +++ b/rosette_api/ApiClient.cs @@ -1,231 +1,231 @@ -using System.Net; - -namespace rosette_api; - -public class RosetteAPI : IDisposable -{ - private readonly Dictionary _customHeaders; - private bool _disposeClient = false; - - /// - /// APIKey is the Rosette API key provided by Basis Technology - /// - public string APIKey { get; private set; } - - /// - /// Client is the HttpClient to be used for communication with the server. - /// - public HttpClient? Client { get; private set; } - - /// - /// URI is the uri of the Rosette API server. - /// Default: https://api.rosette.com/rest/v1/ - /// - public string URI { get; private set; } - - /// - /// ConcurrentConnections is the maximum number of connections allowed by the Rosette API Server. - /// Default: 2 - /// - public int ConcurrentConnections { get; private set; } - - /// - /// Timeout is the receive data timeout for the http client. - /// Default: 30 seconds - /// - public int Timeout { get; private set; } - - /// - /// Version returns the version of the assembly - /// - public static string Version => - typeof(RosetteAPI).Assembly.GetName().Version?.ToString() ?? "0.0.0.0"; - - /// - /// Debug turns on extended debug information - /// Default: false - /// - public bool Debug { get; private set; } - - /// - /// RosetteAPI provides the connection to be used for all of the endpoints. - /// - /// Required Rosette API key - public RosetteAPI(string apiKey) { - ArgumentNullException.ThrowIfNull(apiKey); - APIKey = apiKey; - URI = "https://api.rosette.com/rest/v1/"; - Client = null; - ConcurrentConnections = 2; - Timeout = 300; - Debug = false; - _customHeaders = new Dictionary(); - - Prepare(); - } - - /// - /// UseAlternateURL allows the user to specify a different URL for connection to the Rosette API server - /// - /// Destination URL string - /// RosetteAPI object - public RosetteAPI UseAlternateURL(string urlString) { - URI = urlString.EndsWith("/") ? urlString : urlString + "/"; - - return Prepare(); - } - - /// - /// AssignClient allows the user to assign a Client object of their choosing. Additional Rosette API headers - /// will be added to this prior to operation. - /// - /// A valid HttpClient - /// RosetteAPI object - public RosetteAPI AssignClient(HttpClient client) { - Client = client; - - return Prepare(); - } - - /// - /// AssignConcurrentConnections allows the user to specify the number of connections to use. This number, default 2, - /// is based on the user's plan with Basis Technology. - /// - /// Maximum number of connections to allow - /// RosetteAPI object - public RosetteAPI AssignConcurrentConnections(int connections) { - ConcurrentConnections = connections < 2 ? 2 : connections; - - return Prepare(true); - } - - /// - /// AssignTimeoutInMS sets the Client receive data timeout in seconds - /// Default: 30 seconds - /// - /// Timeout in seconds - /// RosetteAPI object - public RosetteAPI AssignTimeout(int timeout) { - Timeout = timeout < 0 ? 0 : timeout; - - return Prepare(); - } - - /// - /// SetDebug sets the debug state of the server response. - /// Default: false - /// - /// Debug state - /// RosetteAPI object - public RosetteAPI SetDebug(bool state=true) { - Debug = state; - - return Prepare(); - } - - /// - /// AddCustomHeader allows the user to add custom headers to the Client. Header names must be prefixed with - /// 'X-RosetteAPI-' - /// - /// Name of header, prefixed with 'X-RosetteAPI-' - /// Value of header - /// RosetteAPI object - public RosetteAPI AddCustomHeader(string headerName, string headerValue) { - if (!headerName.StartsWith("X-RosetteAPI-", StringComparison.OrdinalIgnoreCase)) - { - throw new ArgumentException( - $"Custom header name must begin with 'X-RosetteAPI-'. Provided: {headerName}", - nameof(headerName)); - } - if (_customHeaders.ContainsKey(headerName) && headerValue == null) - { - _customHeaders.Remove(headerName); - } - else - { - _customHeaders[headerName] = headerValue; - } - - return Prepare(); - } - - /// - /// Prepare constructs the Client, with Rosette specific headers, Timeout, ConcurrentConnections, etc. - /// If an outside client is provided, timeout, header and custom header values will still be applied. - /// Warning: Changing the concurrent connections requires a new client, which will replace the user provided one with an - /// internal one. It is advisable that if a user wants to control their own Http Client, they should set the concurrent - /// connections on that object prior to assigning it to the RosetteAPI. - /// - /// Forces the client to refresh. This is necessary if the concurrent connections are changed. - /// RosetteAPI object - private RosetteAPI Prepare(bool forceUpdate=false) { - if (Client == null || forceUpdate) { - Client = new HttpClient( - new SocketsHttpHandler { - AutomaticDecompression = DecompressionMethods.All, - MaxConnectionsPerServer = ConcurrentConnections, - PooledConnectionLifetime = TimeSpan.FromMinutes(2), - PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1) - }); - _disposeClient = true; - } - Client.Timeout = TimeSpan.FromSeconds(Timeout); - - if (Client.BaseAddress == null) { - Client.BaseAddress = new Uri(URI); // base address must be the rosette URI regardless of whether the client is external or internal - } - - // Standard headers, which are required for Rosette API - AddRequestHeader("X-BabelStreetAPI-Key", APIKey); - AddRequestHeader("User-Agent", string.Format("RosetteAPICsharp/{0}-{1}", Version, Environment.Version.ToString())); - AddRequestHeader("X-RosetteAPI-Binding", "csharp"); - AddRequestHeader("X-RosetteAPI-Binding-Version", Version); - if (Debug) { - AddRequestHeader("X-RosetteAPI-Devel", Debug.ToString()); - } - - var acceptHeader = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"); - if (!Client.DefaultRequestHeaders.Accept.Contains(acceptHeader)) { - Client.DefaultRequestHeaders.Accept.Add(acceptHeader); - } - - foreach (string encodingType in new List() { "gzip", "deflate" }) { - var encodingHeader = new System.Net.Http.Headers.StringWithQualityHeaderValue(encodingType); - if (!Client.DefaultRequestHeaders.AcceptEncoding.Contains(encodingHeader)) { - Client.DefaultRequestHeaders.AcceptEncoding.Add(encodingHeader); - } - } - - // Custom headers provided by the user - if (_customHeaders.Count > 0) { - foreach (KeyValuePair entry in _customHeaders) { - AddRequestHeader(entry.Key, entry.Value); - } - } - - return this; - } - - /// - /// AddRequestHeader is a helper method to add a header value to the client - /// - /// header name - /// header value - private void AddRequestHeader(string name, string value) - { - ArgumentNullException.ThrowIfNull(Client); - - if (!Client.DefaultRequestHeaders.Contains(name)) - { - Client.DefaultRequestHeaders.Add(name, value); - } - } - - public void Dispose() - { - if (_disposeClient && Client != null) - { - Client.Dispose(); - } - } -} +using System.Net; + +namespace Rosette.Api; + +public class ApiClient : IDisposable +{ + private readonly Dictionary _customHeaders; + private bool _disposeClient = false; + + /// + /// APIKey is the Rosette API key provided by Basis Technology + /// + public string APIKey { get; private set; } + + /// + /// Client is the HttpClient to be used for communication with the server. + /// + public HttpClient? Client { get; private set; } + + /// + /// URI is the uri of the Rosette API server. + /// Default: https://api.rosette.com/rest/v1/ + /// + public string URI { get; private set; } + + /// + /// ConcurrentConnections is the maximum number of connections allowed by the Rosette API Server. + /// Default: 2 + /// + public int ConcurrentConnections { get; private set; } + + /// + /// Timeout is the receive data timeout for the http client. + /// Default: 30 seconds + /// + public int Timeout { get; private set; } + + /// + /// Version returns the version of the assembly + /// + public static string Version => + typeof(ApiClient).Assembly.GetName().Version?.ToString() ?? "0.0.0.0"; + + /// + /// Debug turns on extended debug information + /// Default: false + /// + public bool Debug { get; private set; } + + /// + /// RosetteAPI provides the connection to be used for all of the endpoints. + /// + /// Required Rosette API key + public ApiClient(string apiKey) { + ArgumentNullException.ThrowIfNull(apiKey); + APIKey = apiKey; + URI = "https://api.rosette.com/rest/v1/"; + Client = null; + ConcurrentConnections = 2; + Timeout = 300; + Debug = false; + _customHeaders = new Dictionary(); + + Prepare(); + } + + /// + /// UseAlternateURL allows the user to specify a different URL for connection to the Rosette API server + /// + /// Destination URL string + /// RosetteAPI object + public ApiClient UseAlternateURL(string urlString) { + URI = urlString.EndsWith("/") ? urlString : urlString + "/"; + + return Prepare(); + } + + /// + /// AssignClient allows the user to assign a Client object of their choosing. Additional Rosette API headers + /// will be added to this prior to operation. + /// + /// A valid HttpClient + /// RosetteAPI object + public ApiClient AssignClient(HttpClient client) { + Client = client; + + return Prepare(); + } + + /// + /// AssignConcurrentConnections allows the user to specify the number of connections to use. This number, default 2, + /// is based on the user's plan with Basis Technology. + /// + /// Maximum number of connections to allow + /// RosetteAPI object + public ApiClient AssignConcurrentConnections(int connections) { + ConcurrentConnections = connections < 2 ? 2 : connections; + + return Prepare(true); + } + + /// + /// AssignTimeoutInMS sets the Client receive data timeout in seconds + /// Default: 30 seconds + /// + /// Timeout in seconds + /// RosetteAPI object + public ApiClient AssignTimeout(int timeout) { + Timeout = timeout < 0 ? 0 : timeout; + + return Prepare(); + } + + /// + /// SetDebug sets the debug state of the server response. + /// Default: false + /// + /// Debug state + /// RosetteAPI object + public ApiClient SetDebug(bool state=true) { + Debug = state; + + return Prepare(); + } + + /// + /// AddCustomHeader allows the user to add custom headers to the Client. Header names must be prefixed with + /// 'X-RosetteAPI-' + /// + /// Name of header, prefixed with 'X-RosetteAPI-' + /// Value of header + /// RosetteAPI object + public ApiClient AddCustomHeader(string headerName, string headerValue) { + if (!headerName.StartsWith("X-RosetteAPI-", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException( + $"Custom header name must begin with 'X-RosetteAPI-'. Provided: {headerName}", + nameof(headerName)); + } + if (_customHeaders.ContainsKey(headerName) && headerValue == null) + { + _customHeaders.Remove(headerName); + } + else + { + _customHeaders[headerName] = headerValue; + } + + return Prepare(); + } + + /// + /// Prepare constructs the Client, with Rosette specific headers, Timeout, ConcurrentConnections, etc. + /// If an outside client is provided, timeout, header and custom header values will still be applied. + /// Warning: Changing the concurrent connections requires a new client, which will replace the user provided one with an + /// internal one. It is advisable that if a user wants to control their own Http Client, they should set the concurrent + /// connections on that object prior to assigning it to the RosetteAPI. + /// + /// Forces the client to refresh. This is necessary if the concurrent connections are changed. + /// RosetteAPI object + private ApiClient Prepare(bool forceUpdate=false) { + if (Client == null || forceUpdate) { + Client = new HttpClient( + new SocketsHttpHandler { + AutomaticDecompression = DecompressionMethods.All, + MaxConnectionsPerServer = ConcurrentConnections, + PooledConnectionLifetime = TimeSpan.FromMinutes(2), + PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1) + }); + _disposeClient = true; + } + Client.Timeout = TimeSpan.FromSeconds(Timeout); + + if (Client.BaseAddress == null) { + Client.BaseAddress = new Uri(URI); // base address must be the rosette URI regardless of whether the client is external or internal + } + + // Standard headers, which are required for Rosette API + AddRequestHeader("X-BabelStreetAPI-Key", APIKey); + AddRequestHeader("User-Agent", string.Format("RosetteAPICsharp/{0}-{1}", Version, Environment.Version.ToString())); + AddRequestHeader("X-RosetteAPI-Binding", "csharp"); + AddRequestHeader("X-RosetteAPI-Binding-Version", Version); + if (Debug) { + AddRequestHeader("X-RosetteAPI-Devel", Debug.ToString()); + } + + var acceptHeader = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"); + if (!Client.DefaultRequestHeaders.Accept.Contains(acceptHeader)) { + Client.DefaultRequestHeaders.Accept.Add(acceptHeader); + } + + foreach (string encodingType in new List() { "gzip", "deflate" }) { + var encodingHeader = new System.Net.Http.Headers.StringWithQualityHeaderValue(encodingType); + if (!Client.DefaultRequestHeaders.AcceptEncoding.Contains(encodingHeader)) { + Client.DefaultRequestHeaders.AcceptEncoding.Add(encodingHeader); + } + } + + // Custom headers provided by the user + if (_customHeaders.Count > 0) { + foreach (KeyValuePair entry in _customHeaders) { + AddRequestHeader(entry.Key, entry.Value); + } + } + + return this; + } + + /// + /// AddRequestHeader is a helper method to add a header value to the client + /// + /// header name + /// header value + private void AddRequestHeader(string name, string value) + { + ArgumentNullException.ThrowIfNull(Client); + + if (!Client.DefaultRequestHeaders.Contains(name)) + { + Client.DefaultRequestHeaders.Add(name, value); + } + } + + public void Dispose() + { + if (_disposeClient && Client != null) + { + Client.Dispose(); + } + } +} diff --git a/rosette_api/CategoriesEndpoint.cs b/rosette_api/Endpoints/Categories.cs old mode 100755 new mode 100644 similarity index 50% rename from rosette_api/CategoriesEndpoint.cs rename to rosette_api/Endpoints/Categories.cs index 45b9373..25768db --- a/rosette_api/CategoriesEndpoint.cs +++ b/rosette_api/Endpoints/Categories.cs @@ -1,12 +1,14 @@ -namespace rosette_api; - -public class CategoriesEndpoint : ContentBasedEndpoint -{ - /// - /// CategoriesEndpoint returns the categories extracted from the endpoint - /// - /// text, Uri object or FileStream - public CategoriesEndpoint(object content) : base("categories", content) - { - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class Categories : ContentEndpointBase +{ + /// + /// CategoriesEndpoint returns the categories extracted from the endpoint + /// + /// text, Uri object or FileStream + public Categories(object content) : base("categories", content) + { + } +} diff --git a/rosette_api/ContentBasedEndpoint.cs b/rosette_api/Endpoints/Core/ContentEndpointBase.cs similarity index 91% rename from rosette_api/ContentBasedEndpoint.cs rename to rosette_api/Endpoints/Core/ContentEndpointBase.cs index be404fb..7fd6d43 100644 --- a/rosette_api/ContentBasedEndpoint.cs +++ b/rosette_api/Endpoints/Core/ContentEndpointBase.cs @@ -1,17 +1,17 @@ -namespace rosette_api; +namespace Rosette.Api.Endpoints.Core; /// /// Abstract base class for Rosette API endpoints that accept content and common parameters /// /// The concrete endpoint type for fluent API support -public abstract class ContentBasedEndpoint : EndpointCommon where T : ContentBasedEndpoint +public abstract class ContentEndpointBase : EndpointBase where T : ContentEndpointBase { /// /// Constructor for content-based endpoints /// /// The API endpoint name (e.g., "language", "categories", "entities") /// text, Uri object or FileStream - protected ContentBasedEndpoint(string endpointName, object content) : base(endpointName) { + protected ContentEndpointBase(string endpointName, object content) : base(endpointName) { ArgumentNullException.ThrowIfNull(content); SetContent(content); } @@ -91,7 +91,7 @@ public T SetFileContentType(string contentType) { /// /// RosetteAPI object /// RosetteResponse - public RosetteResponse Call(RosetteAPI api) { + public Response Call(ApiClient api) { return Funcs.PostCall(api); } } \ No newline at end of file diff --git a/rosette_api/EndpointCommon.cs b/rosette_api/Endpoints/Core/EndpointBase.cs old mode 100755 new mode 100644 similarity index 86% rename from rosette_api/EndpointCommon.cs rename to rosette_api/Endpoints/Core/EndpointBase.cs index 7c5f4e1..49b6b4a --- a/rosette_api/EndpointCommon.cs +++ b/rosette_api/Endpoints/Core/EndpointBase.cs @@ -1,91 +1,91 @@ -using System.Collections.Specialized; - -namespace rosette_api; - -public class EndpointCommon where T : EndpointCommon -{ - private EndpointFunctions _funcs = null; - public Dictionary Options { get; private set; } - public Dictionary Params { get; set; } - public NameValueCollection UrlParameters { get; private set; } - public string Endpoint { get; protected set; } - public EndpointFunctions Funcs { - get { - if (_funcs == null) { - _funcs = new EndpointFunctions(Params, Options, UrlParameters, Endpoint); - } - return _funcs; - } - } - - public EndpointCommon(string endpoint) { - Options = []; - Params = []; - UrlParameters = []; - Endpoint = endpoint; - } - - /// - /// SetOption sets an option and value - /// - /// Name of option - /// Value of option - /// Update object - public T SetOption(string optionName, object optionValue) - { - ArgumentException.ThrowIfNullOrWhiteSpace(optionName); - ArgumentNullException.ThrowIfNull(optionValue); - - Options[optionName] = optionValue; - return (T)this; - } - - /// - /// RemoveOption removes an option - /// - /// Name of option - /// Update object - public T RemoveOption(string optionName) - { - ArgumentException.ThrowIfNullOrWhiteSpace(optionName); - Options.Remove(optionName); - return (T)this; - } - - /// - /// ClearOptions removes all options - /// - /// Updated object - public T ClearOptions() { - Options.Clear(); - return (T)this; - } - - /// - /// SetUrlParameter sets a key/value for a query string, e.g. ?output=rosette - /// - /// parameter key - /// parameter value - /// Updated object - public T SetUrlParameter(string parameterKey, string parameterValue) - { - ArgumentException.ThrowIfNullOrWhiteSpace(parameterKey); - ArgumentException.ThrowIfNullOrWhiteSpace(parameterValue); - - UrlParameters.Add(parameterKey, parameterValue); - return (T)this; - } - - /// - /// RemoveUrlParameter removes all values associated with a specified key - /// - /// parameter key - /// Update object - public T RemoveUrlParameter(string parameterKey) - { - ArgumentException.ThrowIfNullOrWhiteSpace(parameterKey); - UrlParameters.Remove(parameterKey); - return (T)this; - } - -} +using System.Collections.Specialized; + +namespace Rosette.Api.Endpoints.Core; + +public class EndpointBase where T : EndpointBase +{ + private EndpointExecutor _funcs = null; + public Dictionary Options { get; private set; } + public Dictionary Params { get; set; } + public NameValueCollection UrlParameters { get; private set; } + public string Endpoint { get; protected set; } + public EndpointExecutor Funcs { + get { + if (_funcs == null) { + _funcs = new EndpointExecutor(Params, Options, UrlParameters, Endpoint); + } + return _funcs; + } + } + + public EndpointBase(string endpoint) { + Options = []; + Params = []; + UrlParameters = []; + Endpoint = endpoint; + } + + /// + /// SetOption sets an option and value + /// + /// Name of option + /// Value of option + /// Update object + public T SetOption(string optionName, object optionValue) + { + ArgumentException.ThrowIfNullOrWhiteSpace(optionName); + ArgumentNullException.ThrowIfNull(optionValue); + + Options[optionName] = optionValue; + return (T)this; + } + + /// + /// RemoveOption removes an option + /// + /// Name of option + /// Update object + public T RemoveOption(string optionName) + { + ArgumentException.ThrowIfNullOrWhiteSpace(optionName); + Options.Remove(optionName); + return (T)this; + } + + /// + /// ClearOptions removes all options + /// + /// Updated object + public T ClearOptions() { + Options.Clear(); + return (T)this; + } + + /// + /// SetUrlParameter sets a key/value for a query string, e.g. ?output=rosette + /// + /// parameter key + /// parameter value + /// Updated object + public T SetUrlParameter(string parameterKey, string parameterValue) + { + ArgumentException.ThrowIfNullOrWhiteSpace(parameterKey); + ArgumentException.ThrowIfNullOrWhiteSpace(parameterValue); + + UrlParameters.Add(parameterKey, parameterValue); + return (T)this; + } + + /// + /// RemoveUrlParameter removes all values associated with a specified key + /// + /// parameter key + /// Update object + public T RemoveUrlParameter(string parameterKey) + { + ArgumentException.ThrowIfNullOrWhiteSpace(parameterKey); + UrlParameters.Remove(parameterKey); + return (T)this; + } + +} diff --git a/rosette_api/EndpointFunctions.cs b/rosette_api/Endpoints/Core/EndpointExecutor.cs old mode 100755 new mode 100644 similarity index 92% rename from rosette_api/EndpointFunctions.cs rename to rosette_api/Endpoints/Core/EndpointExecutor.cs index 0b1aa6c..550be2a --- a/rosette_api/EndpointFunctions.cs +++ b/rosette_api/Endpoints/Core/EndpointExecutor.cs @@ -1,243 +1,243 @@ -using System.Collections.Specialized; -using System.Text; -using System.Text.Json; - -namespace rosette_api; - -/// -/// EndpointProcessor provices the compilation and processing of the endpoint -/// through the server. It includes functions that may be used by the different endpoints -/// as needed. -/// -public class EndpointFunctions { - private const string CONTENT = "content"; - private const string CONTENTURI = "contenturi"; - private const string LANGUAGE = "language"; - private const string GENRE = "genre"; - private const string OPTIONS = "options"; - - /// - /// _params contains the parameters to be sent to the server - /// - private readonly Dictionary _params; - - /// - /// _options contains user provided options - /// - private readonly Dictionary _options; - - /// - /// _urlParameters is a NameValueCollection to provide URL query string parameters to the call - /// - private readonly NameValueCollection _urlParameters; - - /// - /// BaseEndpoint is the constructor - /// - public EndpointFunctions( - Dictionary parameters, - Dictionary options, - NameValueCollection urlParameters, - string endpoint) -{ - ArgumentNullException.ThrowIfNull(parameters); - ArgumentNullException.ThrowIfNull(options); - ArgumentNullException.ThrowIfNull(urlParameters); - ArgumentException.ThrowIfNullOrWhiteSpace(endpoint); - - _params = parameters; - _options = options; - _urlParameters = urlParameters; - Endpoint = endpoint; - FileContentType = "text/plain"; -} - /// - /// Endpoint returns the assigned endpoint - /// - public string Endpoint { get; private set; } - - /// - /// Filename returns the provided FileStream's filename - /// - public string Filename { get => Filestream == null ? string.Empty : Filestream.Name; } - - /// - /// Filestream contains the user provided FileStream or null - /// - /// - public FileStream Filestream {get; private set; } - - /// - /// FileContentType returns the assigned Content-Type for a multipart file upload - /// - public string FileContentType { get; set; } - - /// - /// Parameters return the paramters dictionary - /// - /// - public Dictionary Parameters { get => _params; } - - /// - /// Options returns the option dictionary - /// - public Dictionary Options { get=> _options; } - - /// - /// UrlParameters returns any parameters to be used for query string - /// - public NameValueCollection UrlParameters { get => _urlParameters; } - - /// - /// Content returns the textual content, the URI (as a string) or an empty string - /// - public object Content { - get => _params.ContainsKey(CONTENT) ? _params[CONTENT] - : _params.ContainsKey(CONTENTURI) ? _params[CONTENTURI] - : string.Empty; - set { - if (value.GetType() == typeof(FileStream)) { - Filestream = (FileStream)value; - ClearKey(CONTENT); - ClearKey(CONTENTURI); - } - else if (value.GetType() == typeof(Uri)) { - _params[CONTENTURI] = ((Uri)value).AbsoluteUri; - ClearKey(CONTENT); - Filestream = null; - } - else { - _params[CONTENT] = value; - ClearKey(CONTENTURI); - Filestream = null; - } - } - } - /// - /// Language returns the provided 3-letter language code or an empty string - /// - public string? Language { - get => _params.ContainsKey(LANGUAGE) ? _params[LANGUAGE].ToString() : string.Empty; - set => _params[LANGUAGE] = value; - } - - /// - /// Genre returns the provided genre or an empty string - /// - public string? Genre { - get => _params.ContainsKey(GENRE) ? _params[GENRE].ToString() : string.Empty; - set => _params[GENRE] = value; - } - - /// - /// GetCall executes the endpoint against the server using GetAsync - /// - /// RosetteAPI object - /// RosetteResponse - public RosetteResponse GetCall(RosetteAPI api) { - string url = api.URI + Endpoint; - Task task = Task.Run(async () => await api.Client.GetAsync(url)); - var response = task.Result; - - return new RosetteResponse(response); - } - - /// - /// Call calls the server with the provided data using PostAsync - /// - /// RosetteAPI object - /// Rosette Response - public virtual RosetteResponse PostCall(RosetteAPI api) { - string url = api.URI + Endpoint + ToQueryString(); - if (Filestream == null) { - HttpContent content = new StringContent(JsonSerializer.Serialize(AppendOptions(_params))); - content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - Task task = Task.Run(async () => await api.Client.PostAsync(url, content)); - var response = task.Result; - - return new RosetteResponse(response); - } - else { - return PostAsMultipart(api, url); - } - } - - /// - /// AppendOptions appends any provided options to the parameter dictionary - /// - /// Dictionary of options - /// Dictionary of string, object - private Dictionary AppendOptions(Dictionary dict) { - if (_options.Count > 0) { - dict[OPTIONS] = _options; - } - else { - dict.Remove(OPTIONS); // Remove returns false if key doesn't exist, no need to check - } - return dict; - } - - /// - /// PostAsMultipart handles processing of files as a multipart upload - /// - /// RosetteAPI object - /// Endpoint URL - /// RosetteResponse object - private RosetteResponse PostAsMultipart(RosetteAPI api, string url) { - - using (var _multiPartContent = new MultipartFormDataContent()) { - var streamContent = new StreamContent(Filestream); - streamContent.Headers.Add("Content-Type", FileContentType); - streamContent.Headers.Add("Content-Disposition", "mixed; name=\"content\"; filename=\"" + Path.GetFileName(Filestream.Name) + "\""); - _multiPartContent.Add(streamContent, "content", Path.GetFileName(Filestream.Name)); - - if (_options.Count > 0 || _params.Count > 0) { - var stringContent = new StringContent(JsonSerializer.Serialize(AppendOptions(_params)), Encoding.UTF8, "application/json"); - stringContent.Headers.Add("Content-Disposition", "mixed; name=\"request\""); - _multiPartContent.Add(stringContent, "request"); - } - Task task = Task.Run(async () => await api.Client.PostAsync(url, _multiPartContent)); - - var response = task.Result; - return new RosetteResponse(response); - } - } - - /// - /// clearKey removes the specified key from the _params dictionary - /// - /// key name - private void ClearKey(string key) => _params.Remove(key); - - /// - /// ToQueryString is a helper to generate the query string to append to the URL - /// - /// query string - private string ToQueryString() { - if (UrlParameters.Count == 0) { - return String.Empty; - } - StringBuilder sb = new StringBuilder("?"); - - bool first = true; - foreach (string key in UrlParameters.AllKeys) { - foreach (string value in UrlParameters.GetValues(key)) { - if (!first) { - sb.Append("&"); - } - sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value)); - first = false; - } - } - - return sb.ToString(); - } - - /// - /// HasContent checks for content to send - /// - /// bool if content, contenturi or filename - private bool HasContent() { - return _params.ContainsKey(CONTENT) || _params.ContainsKey(CONTENTURI) || !string.IsNullOrEmpty(Filename); - } -} +using System.Collections.Specialized; +using System.Text; +using System.Text.Json; + +namespace Rosette.Api.Endpoints.Core; + +/// +/// EndpointProcessor provices the compilation and processing of the endpoint +/// through the server. It includes functions that may be used by the different endpoints +/// as needed. +/// +public class EndpointExecutor { + private const string CONTENT = "content"; + private const string CONTENTURI = "contenturi"; + private const string LANGUAGE = "language"; + private const string GENRE = "genre"; + private const string OPTIONS = "options"; + + /// + /// _params contains the parameters to be sent to the server + /// + private readonly Dictionary _params; + + /// + /// _options contains user provided options + /// + private readonly Dictionary _options; + + /// + /// _urlParameters is a NameValueCollection to provide URL query string parameters to the call + /// + private readonly NameValueCollection _urlParameters; + + /// + /// BaseEndpoint is the constructor + /// + public EndpointExecutor( + Dictionary parameters, + Dictionary options, + NameValueCollection urlParameters, + string endpoint) +{ + ArgumentNullException.ThrowIfNull(parameters); + ArgumentNullException.ThrowIfNull(options); + ArgumentNullException.ThrowIfNull(urlParameters); + ArgumentException.ThrowIfNullOrWhiteSpace(endpoint); + + _params = parameters; + _options = options; + _urlParameters = urlParameters; + Endpoint = endpoint; + FileContentType = "text/plain"; +} + /// + /// Endpoint returns the assigned endpoint + /// + public string Endpoint { get; private set; } + + /// + /// Filename returns the provided FileStream's filename + /// + public string Filename { get => Filestream == null ? string.Empty : Filestream.Name; } + + /// + /// Filestream contains the user provided FileStream or null + /// + /// + public FileStream Filestream {get; private set; } + + /// + /// FileContentType returns the assigned Content-Type for a multipart file upload + /// + public string FileContentType { get; set; } + + /// + /// Parameters return the paramters dictionary + /// + /// + public Dictionary Parameters { get => _params; } + + /// + /// Options returns the option dictionary + /// + public Dictionary Options { get=> _options; } + + /// + /// UrlParameters returns any parameters to be used for query string + /// + public NameValueCollection UrlParameters { get => _urlParameters; } + + /// + /// Content returns the textual content, the URI (as a string) or an empty string + /// + public object Content { + get => _params.ContainsKey(CONTENT) ? _params[CONTENT] + : _params.ContainsKey(CONTENTURI) ? _params[CONTENTURI] + : string.Empty; + set { + if (value.GetType() == typeof(FileStream)) { + Filestream = (FileStream)value; + ClearKey(CONTENT); + ClearKey(CONTENTURI); + } + else if (value.GetType() == typeof(Uri)) { + _params[CONTENTURI] = ((Uri)value).AbsoluteUri; + ClearKey(CONTENT); + Filestream = null; + } + else { + _params[CONTENT] = value; + ClearKey(CONTENTURI); + Filestream = null; + } + } + } + /// + /// Language returns the provided 3-letter language code or an empty string + /// + public string? Language { + get => _params.ContainsKey(LANGUAGE) ? _params[LANGUAGE].ToString() : string.Empty; + set => _params[LANGUAGE] = value; + } + + /// + /// Genre returns the provided genre or an empty string + /// + public string? Genre { + get => _params.ContainsKey(GENRE) ? _params[GENRE].ToString() : string.Empty; + set => _params[GENRE] = value; + } + + /// + /// GetCall executes the endpoint against the server using GetAsync + /// + /// RosetteAPI object + /// RosetteResponse + public Response GetCall(ApiClient api) { + string url = api.URI + Endpoint; + Task task = Task.Run(async () => await api.Client.GetAsync(url)); + var response = task.Result; + + return new Response(response); + } + + /// + /// Call calls the server with the provided data using PostAsync + /// + /// RosetteAPI object + /// Rosette Response + public virtual Response PostCall(ApiClient api) { + string url = api.URI + Endpoint + ToQueryString(); + if (Filestream == null) { + HttpContent content = new StringContent(JsonSerializer.Serialize(AppendOptions(_params))); + content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + Task task = Task.Run(async () => await api.Client.PostAsync(url, content)); + var response = task.Result; + + return new Response(response); + } + else { + return PostAsMultipart(api, url); + } + } + + /// + /// AppendOptions appends any provided options to the parameter dictionary + /// + /// Dictionary of options + /// Dictionary of string, object + private Dictionary AppendOptions(Dictionary dict) { + if (_options.Count > 0) { + dict[OPTIONS] = _options; + } + else { + dict.Remove(OPTIONS); // Remove returns false if key doesn't exist, no need to check + } + return dict; + } + + /// + /// PostAsMultipart handles processing of files as a multipart upload + /// + /// RosetteAPI object + /// Endpoint URL + /// RosetteResponse object + private Response PostAsMultipart(ApiClient api, string url) { + + using (var _multiPartContent = new MultipartFormDataContent()) { + var streamContent = new StreamContent(Filestream); + streamContent.Headers.Add("Content-Type", FileContentType); + streamContent.Headers.Add("Content-Disposition", "mixed; name=\"content\"; filename=\"" + Path.GetFileName(Filestream.Name) + "\""); + _multiPartContent.Add(streamContent, "content", Path.GetFileName(Filestream.Name)); + + if (_options.Count > 0 || _params.Count > 0) { + var stringContent = new StringContent(JsonSerializer.Serialize(AppendOptions(_params)), Encoding.UTF8, "application/json"); + stringContent.Headers.Add("Content-Disposition", "mixed; name=\"request\""); + _multiPartContent.Add(stringContent, "request"); + } + Task task = Task.Run(async () => await api.Client.PostAsync(url, _multiPartContent)); + + var response = task.Result; + return new Response(response); + } + } + + /// + /// clearKey removes the specified key from the _params dictionary + /// + /// key name + private void ClearKey(string key) => _params.Remove(key); + + /// + /// ToQueryString is a helper to generate the query string to append to the URL + /// + /// query string + private string ToQueryString() { + if (UrlParameters.Count == 0) { + return String.Empty; + } + StringBuilder sb = new StringBuilder("?"); + + bool first = true; + foreach (string key in UrlParameters.AllKeys) { + foreach (string value in UrlParameters.GetValues(key)) { + if (!first) { + sb.Append("&"); + } + sb.AppendFormat("{0}={1}", Uri.EscapeDataString(key), Uri.EscapeDataString(value)); + first = false; + } + } + + return sb.ToString(); + } + + /// + /// HasContent checks for content to send + /// + /// bool if content, contenturi or filename + private bool HasContent() { + return _params.ContainsKey(CONTENT) || _params.ContainsKey(CONTENTURI) || !string.IsNullOrEmpty(Filename); + } +} diff --git a/rosette_api/EntitiesEndpoint.cs b/rosette_api/Endpoints/Entities.cs old mode 100755 new mode 100644 similarity index 51% rename from rosette_api/EntitiesEndpoint.cs rename to rosette_api/Endpoints/Entities.cs index 6d1f324..9a06295 --- a/rosette_api/EntitiesEndpoint.cs +++ b/rosette_api/Endpoints/Entities.cs @@ -1,12 +1,14 @@ -namespace rosette_api; - -public class EntitiesEndpoint : ContentBasedEndpoint -{ - /// - /// EntitiesEndpoint returns the entities extracted from the endpoint - /// - /// text, Uri object or FileStream - public EntitiesEndpoint(object content) : base("entities", content) - { - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class Entities : ContentEndpointBase +{ + /// + /// EntitiesEndpoint returns the entities extracted from the endpoint + /// + /// text, Uri object or FileStream + public Entities(object content) : base("entities", content) + { + } +} diff --git a/rosette_api/EventsEndpoint.cs b/rosette_api/Endpoints/Events.cs similarity index 51% rename from rosette_api/EventsEndpoint.cs rename to rosette_api/Endpoints/Events.cs index 18bc02d..9d9c19f 100644 --- a/rosette_api/EventsEndpoint.cs +++ b/rosette_api/Endpoints/Events.cs @@ -1,12 +1,14 @@ -namespace rosette_api; +using Rosette.Api.Endpoints.Core; -public class EventsEndpoint : ContentBasedEndpoint +namespace Rosette.Api.Endpoints; + +public class Events : ContentEndpointBase { /// /// EventsEndpoint returns the events extracted from the endpoint /// /// text, Uri object or FileStream - public EventsEndpoint(object content) : base("events", content) + public Events(object content) : base("events", content) { } } \ No newline at end of file diff --git a/rosette_api/Endpoints/Info.cs b/rosette_api/Endpoints/Info.cs new file mode 100644 index 0000000..a6e3a9c --- /dev/null +++ b/rosette_api/Endpoints/Info.cs @@ -0,0 +1,11 @@ +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public class Info : EndpointBase + { + public Info() : base("info") { } + + public Response Call(ApiClient api) => Funcs.GetCall(api); + } +} diff --git a/rosette_api/LanguageEndpoint.cs b/rosette_api/Endpoints/Language.cs old mode 100755 new mode 100644 similarity index 51% rename from rosette_api/LanguageEndpoint.cs rename to rosette_api/Endpoints/Language.cs index abbe6d3..ec07ee9 --- a/rosette_api/LanguageEndpoint.cs +++ b/rosette_api/Endpoints/Language.cs @@ -1,12 +1,14 @@ -namespace rosette_api; - -public class LanguageEndpoint : ContentBasedEndpoint -{ - /// - /// LanguageEndpoint returns the language extracted from the endpoint - /// - /// text, Uri object or FileStream - public LanguageEndpoint(object content) : base("language", content) - { - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class Language : ContentEndpointBase +{ + /// + /// LanguageEndpoint returns the language extracted from the endpoint + /// + /// text, Uri object or FileStream + public Language(object content) : base("language", content) + { + } +} diff --git a/rosette_api/MorphologyEndpoint.cs b/rosette_api/Endpoints/Morphology.cs old mode 100755 new mode 100644 similarity index 84% rename from rosette_api/MorphologyEndpoint.cs rename to rosette_api/Endpoints/Morphology.cs index e7f50e9..3bf1251 --- a/rosette_api/MorphologyEndpoint.cs +++ b/rosette_api/Endpoints/Morphology.cs @@ -1,103 +1,105 @@ -namespace rosette_api -{ - public enum MorphologyFeature { - /// provide complete morphology - complete, - /// provide lemmas - lemmas, - /// provide parts of speech - partsOfSpeech, - /// provide compound components - compoundComponents, - /// provide han readings - hanReadings - }; - public class MorphologyEndpoint : EndpointCommon { - - - private Dictionary _featureEndpoint; - /// - /// MorphologyEndpoint returns morphological analysis of input - /// - /// text, Uri object or FileStream - /// feature to use - public MorphologyEndpoint(object content, MorphologyFeature feature = MorphologyFeature.complete) : base("morphology") { - Endpoint = "morphology/" + FeatureAsString(feature); - SetContent(content); - } - /// - /// SetContent sets the content to be reviewed - /// - /// text, Uri object or FileStream - /// update Morphology endpoint - public MorphologyEndpoint SetContent(object content) { - Funcs.Content = content; - - return this; - } - - public object Content => Funcs.Content; - /// - /// SetLanguage sets the optional ISO 639-3 language code - /// - /// ISO 639-3 language code - /// updated Morphology endpoint - public MorphologyEndpoint SetLanguage(string language) { - Funcs.Language = language; - - return this; - } - - public string? Language => Funcs.Language; - /// - /// SetGenre sets the optional document genre, e.g. social-media - /// - /// document genre - /// updated Morphology endpoint - public MorphologyEndpoint SetGenre(string genre) { - Funcs.Genre = genre; - - return this; - } - - public string? Genre => Funcs.Genre; - /// - /// SetFileContentType sets the content type of the file contents. Note that - /// it only applies when the content is a filename - /// - /// Content-Type - /// updated Morphology endpoint - public MorphologyEndpoint SetFileContentType(string contentType) { - Funcs.FileContentType = contentType; - - return this; - } - public string FileContentType => Funcs.FileContentType; - public string Filename => Funcs.Filename; - /// - /// Call passes the data to the server and returns the response - /// - /// RosetteAPI object - /// RosetteResponse - public RosetteResponse Call(RosetteAPI api) { - return Funcs.PostCall(api); - } - /// - /// FeatureAsString returns the feature enum in its endpoint string form - /// - /// feature - /// endpoint equivalent - public string FeatureAsString(MorphologyFeature feature) { - _featureEndpoint = new Dictionary() { - { MorphologyFeature.complete, "complete" }, - { MorphologyFeature.lemmas, "lemmas" }, - { MorphologyFeature.partsOfSpeech, "parts-of-speech" }, - { MorphologyFeature.compoundComponents, "compound-components" }, - { MorphologyFeature.hanReadings, "han-readings" } - }; - - return _featureEndpoint[feature]; - } - - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public enum MorphologyFeature { + /// provide complete morphology + complete, + /// provide lemmas + lemmas, + /// provide parts of speech + partsOfSpeech, + /// provide compound components + compoundComponents, + /// provide han readings + hanReadings + }; + public class Morphology : EndpointBase { + + + private Dictionary _featureEndpoint; + /// + /// MorphologyEndpoint returns morphological analysis of input + /// + /// text, Uri object or FileStream + /// feature to use + public Morphology(object content, MorphologyFeature feature = MorphologyFeature.complete) : base("morphology") { + Endpoint = "morphology/" + FeatureAsString(feature); + SetContent(content); + } + /// + /// SetContent sets the content to be reviewed + /// + /// text, Uri object or FileStream + /// update Morphology endpoint + public Morphology SetContent(object content) { + Funcs.Content = content; + + return this; + } + + public object Content => Funcs.Content; + /// + /// SetLanguage sets the optional ISO 639-3 language code + /// + /// ISO 639-3 language code + /// updated Morphology endpoint + public Morphology SetLanguage(string language) { + Funcs.Language = language; + + return this; + } + + public string? Language => Funcs.Language; + /// + /// SetGenre sets the optional document genre, e.g. social-media + /// + /// document genre + /// updated Morphology endpoint + public Morphology SetGenre(string genre) { + Funcs.Genre = genre; + + return this; + } + + public string? Genre => Funcs.Genre; + /// + /// SetFileContentType sets the content type of the file contents. Note that + /// it only applies when the content is a filename + /// + /// Content-Type + /// updated Morphology endpoint + public Morphology SetFileContentType(string contentType) { + Funcs.FileContentType = contentType; + + return this; + } + public string FileContentType => Funcs.FileContentType; + public string Filename => Funcs.Filename; + /// + /// Call passes the data to the server and returns the response + /// + /// RosetteAPI object + /// RosetteResponse + public Response Call(ApiClient api) { + return Funcs.PostCall(api); + } + /// + /// FeatureAsString returns the feature enum in its endpoint string form + /// + /// feature + /// endpoint equivalent + public string FeatureAsString(MorphologyFeature feature) { + _featureEndpoint = new Dictionary() { + { MorphologyFeature.complete, "complete" }, + { MorphologyFeature.lemmas, "lemmas" }, + { MorphologyFeature.partsOfSpeech, "parts-of-speech" }, + { MorphologyFeature.compoundComponents, "compound-components" }, + { MorphologyFeature.hanReadings, "han-readings" } + }; + + return _featureEndpoint[feature]; + } + + } +} diff --git a/rosette_api/NameDeduplicationEndpoint.cs b/rosette_api/Endpoints/NameDeduplication.cs old mode 100755 new mode 100644 similarity index 78% rename from rosette_api/NameDeduplicationEndpoint.cs rename to rosette_api/Endpoints/NameDeduplication.cs index c01ff2a..13a84fa --- a/rosette_api/NameDeduplicationEndpoint.cs +++ b/rosette_api/Endpoints/NameDeduplication.cs @@ -1,76 +1,78 @@ -namespace rosette_api -{ - public class NameDeduplicationEndpoint : EndpointCommon - { - /// - /// NameDeduplicationEndpoint constructs an object to deduplicate names through the Rosette API - /// - /// List of RosetteName objects - public NameDeduplicationEndpoint(List names) : base("name-deduplication") { - SetNames(names); - } - /// - /// SetNames assigns a list of RosetteName objects to be processed - /// - /// List of RosetteName - /// updated NameDeduplicationEndpoint object - public NameDeduplicationEndpoint SetNames(List names) { - if (names == null || names.Count == 0) { - throw new ArgumentException("Names must contain at least 1 RosetteName object."); - } - Params["names"] = names; - return this; - } - /// - /// Names returns the list of RosetteName objects - /// - public IList Names { get => - Params.ContainsKey("names") ? - Params["names"] as IList : - null; - } - /// - /// SetProfileID sets a profile ID to be used during processing - /// - /// profile ID - /// updated NameDeduplicationEndpoint object - public NameDeduplicationEndpoint SetProfileID(string profileID) { - Params["profileid"] = profileID; - return this; - } - /// - /// ProfileID returns the profile ID or empty string - /// - public string ProfileID { get => - Params.ContainsKey("profileid") ? - Params["profileid"].ToString() : - string.Empty; - } - /// - /// SetThreshold sets the threshold to be used for determining deduplication cluster sizing - /// - /// float value of threshold, default 0.75 - /// updated NameDeduplicationEndpoint object - public NameDeduplicationEndpoint SetThreshold(float threshold) { - Params["threshold"] = threshold; - return this; - } - /// - /// Threshold returns the threshold or the default, 0.75f - /// - public float Threshold { get => - Params.ContainsKey("threshold") ? - (float)Params["threshold"] : - 0.75f; - } - /// - /// Call returns the response from the server - /// - /// RosetteAPI object - /// RosetteResponse - public RosetteResponse Call(RosetteAPI api) { - return Funcs.PostCall(api); - } - - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public class NameDeduplication : EndpointBase + { + /// + /// NameDeduplicationEndpoint constructs an object to deduplicate names through the Rosette API + /// + /// List of RosetteName objects + public NameDeduplication(List names) : base("name-deduplication") { + SetNames(names); + } + /// + /// SetNames assigns a list of RosetteName objects to be processed + /// + /// List of RosetteName + /// updated NameDeduplicationEndpoint object + public NameDeduplication SetNames(List names) { + if (names == null || names.Count == 0) { + throw new ArgumentException("Names must contain at least 1 RosetteName object."); + } + Params["names"] = names; + return this; + } + /// + /// Names returns the list of RosetteName objects + /// + public IList Names { get => + Params.ContainsKey("names") ? + Params["names"] as IList : + null; + } + /// + /// SetProfileID sets a profile ID to be used during processing + /// + /// profile ID + /// updated NameDeduplicationEndpoint object + public NameDeduplication SetProfileID(string profileID) { + Params["profileid"] = profileID; + return this; + } + /// + /// ProfileID returns the profile ID or empty string + /// + public string ProfileID { get => + Params.ContainsKey("profileid") ? + Params["profileid"].ToString() : + string.Empty; + } + /// + /// SetThreshold sets the threshold to be used for determining deduplication cluster sizing + /// + /// float value of threshold, default 0.75 + /// updated NameDeduplicationEndpoint object + public NameDeduplication SetThreshold(float threshold) { + Params["threshold"] = threshold; + return this; + } + /// + /// Threshold returns the threshold or the default, 0.75f + /// + public float Threshold { get => + Params.ContainsKey("threshold") ? + (float)Params["threshold"] : + 0.75f; + } + /// + /// Call returns the response from the server + /// + /// RosetteAPI object + /// RosetteResponse + public Response Call(ApiClient api) { + return Funcs.PostCall(api); + } + + } +} diff --git a/rosette_api/NameSimilarityEndpoint.cs b/rosette_api/Endpoints/NameSimilarity.cs old mode 100755 new mode 100644 similarity index 63% rename from rosette_api/NameSimilarityEndpoint.cs rename to rosette_api/Endpoints/NameSimilarity.cs index 9dfbbb5..588217a --- a/rosette_api/NameSimilarityEndpoint.cs +++ b/rosette_api/Endpoints/NameSimilarity.cs @@ -1,20 +1,22 @@ -namespace rosette_api -{ - public class NameSimilarityEndpoint : EndpointCommon { - /// - /// NameSimilarityEndpoint checks the similarity between two RosetteName objects - /// - /// RosetteName object - /// RosetteName object - public NameSimilarityEndpoint(RosetteName? name1, RosetteName? name2) : base("name-similarity") { - ArgumentNullException.ThrowIfNull(name1); - Params["name1"] = name1; - ArgumentNullException.ThrowIfNull(name2); - Params["name2"] = name2; - } - - public RosetteResponse Call(RosetteAPI api) { - return Funcs.PostCall(api); - } - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public class NameSimilarity : EndpointBase { + /// + /// NameSimilarityEndpoint checks the similarity between two RosetteName objects + /// + /// RosetteName object + /// RosetteName object + public NameSimilarity(Name? name1, Name? name2) : base("name-similarity") { + ArgumentNullException.ThrowIfNull(name1); + Params["name1"] = name1; + ArgumentNullException.ThrowIfNull(name2); + Params["name2"] = name2; + } + + public Response Call(ApiClient api) { + return Funcs.PostCall(api); + } + } +} diff --git a/rosette_api/NameTranslationEndpoint.cs b/rosette_api/Endpoints/NameTranslation.cs old mode 100755 new mode 100644 similarity index 82% rename from rosette_api/NameTranslationEndpoint.cs rename to rosette_api/Endpoints/NameTranslation.cs index 8d0a350..e0d3855 --- a/rosette_api/NameTranslationEndpoint.cs +++ b/rosette_api/Endpoints/NameTranslation.cs @@ -1,143 +1,145 @@ -namespace rosette_api -{ - public class NameTranslationEndpoint : EndpointCommon - { - private const string NAME = "name"; - private const string ENTITY_TYPE = "entityType"; - private const string SOURCE_LANGUAGE_OF_ORIGIN = "sourceLanguageOfOrigin"; - private const string SOURCE_LANGUAGE_OF_USE = "sourceLanguageOfUse"; - private const string SOURCE_SCRIPT = "sourceScript"; - private const string TARGET_LANGUAGE = "targetLanguage"; - private const string TARGET_SCHEME = "targetScheme"; - private const string TARGET_SCRIPT = "targetScript"; - - public NameTranslationEndpoint(string name, string targetLanguage="eng") : base("name-translation") { - SetName(name); - SetTargetLanguage(targetLanguage); - } - /// - /// SetName defines the name to be translated - /// - /// name to be translated - /// this - public NameTranslationEndpoint SetName(string name) { - Params[NAME] = name; - - return this; - } - public string? Name { get => - Params.ContainsKey(NAME) ? - Params[NAME].ToString() : - string.Empty; - } - /// - /// SetEntityType sets the optional entity type, PERSON, LOCATION or ORGANIZATION - /// - /// PERSON, LOCATION, or ORGANIZATION - /// this - public NameTranslationEndpoint SetEntityType(string entityType) { - Params[ENTITY_TYPE] = entityType; - - return this; - } - public string? EntityType { get => - Params.ContainsKey(ENTITY_TYPE) ? - Params[ENTITY_TYPE].ToString() : - string.Empty; - } - /// - /// SetSourceLanguageOfOrigin sets the optional ISO 639-3 code for the name's language of origin - /// - /// ISO 639-3 language code - /// this - public NameTranslationEndpoint SetSourceLanguageOfOrigin(string sourceLanguageOfOrigin) { - Params[SOURCE_LANGUAGE_OF_ORIGIN] = sourceLanguageOfOrigin; - - return this; - } - public string? SourceLanguageOfOrigin { get => - Params.ContainsKey(SOURCE_LANGUAGE_OF_ORIGIN) ? - Params[SOURCE_LANGUAGE_OF_ORIGIN].ToString() : - string.Empty; - } - /// - /// SetSourceLanguageOfUse sets the optional ISO 639-3 code for the name's language of use - /// - /// ISO 639-3 language code - /// this - public NameTranslationEndpoint SetSourceLanguageOfUse(string sourceLanguageOfUse) { - Params[SOURCE_LANGUAGE_OF_USE] = sourceLanguageOfUse; - - return this; - } - public string? SourceLanguageOfUse { get => - Params.ContainsKey(SOURCE_LANGUAGE_OF_USE) ? - Params[SOURCE_LANGUAGE_OF_USE].ToString() : - String.Empty; - } - /// - /// SetSourceScript sets the optional ISO 15924 code for the name's script - /// - /// ISO 15294 script code - /// this - public NameTranslationEndpoint SetSourceScript(string sourceScript) { - Params[SOURCE_SCRIPT] = sourceScript; - - return this; - } - public string? SourceScript { get => - Params.ContainsKey(SOURCE_SCRIPT) ? - Params[SOURCE_SCRIPT].ToString() : - string.Empty; - } - /// - /// SetTargetLanguage sets the ISO 639-3 code for the translation language. - /// Defaults to "eng" - /// - /// ISO 639-3 language code - /// this - public NameTranslationEndpoint SetTargetLanguage(string targetLanguage) { - Params[TARGET_LANGUAGE] = targetLanguage; - - return this; - } - public string? TargetLanguage { get => - Params.ContainsKey(TARGET_LANGUAGE) ? - Params[TARGET_LANGUAGE].ToString() : - String.Empty; - } - /// - /// SetTargetScheme sets the optional transliteration scheme for the translation - /// - /// transliteration scheme - /// this - public NameTranslationEndpoint SetTargetScheme(string targetScheme) { - Params[TARGET_SCHEME] = targetScheme; - - return this; - } - public string? TargetScheme { get => - Params.ContainsKey(TARGET_SCHEME) ? - Params[TARGET_SCHEME].ToString() : - string.Empty; - } - /// - /// SetTargetScript sets the optional ISO 15924 code for the translation script - /// - /// ISO 15924 script code - /// this - public NameTranslationEndpoint SetTargetScript(string targetScript) { - Params[TARGET_SCRIPT] = targetScript; - - return this; - } - public string? TargetScript { get => Params.ContainsKey(TARGET_SCRIPT) ? - Params[TARGET_SCRIPT].ToString() : - string.Empty; - } - - public RosetteResponse Call(RosetteAPI api) { - return Funcs.PostCall(api); - } - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public class NameTranslation : EndpointBase + { + private const string NAME = "name"; + private const string ENTITY_TYPE = "entityType"; + private const string SOURCE_LANGUAGE_OF_ORIGIN = "sourceLanguageOfOrigin"; + private const string SOURCE_LANGUAGE_OF_USE = "sourceLanguageOfUse"; + private const string SOURCE_SCRIPT = "sourceScript"; + private const string TARGET_LANGUAGE = "targetLanguage"; + private const string TARGET_SCHEME = "targetScheme"; + private const string TARGET_SCRIPT = "targetScript"; + + public NameTranslation(string name, string targetLanguage="eng") : base("name-translation") { + SetName(name); + SetTargetLanguage(targetLanguage); + } + /// + /// SetName defines the name to be translated + /// + /// name to be translated + /// this + public NameTranslation SetName(string name) { + Params[NAME] = name; + + return this; + } + public string? Name { get => + Params.ContainsKey(NAME) ? + Params[NAME].ToString() : + string.Empty; + } + /// + /// SetEntityType sets the optional entity type, PERSON, LOCATION or ORGANIZATION + /// + /// PERSON, LOCATION, or ORGANIZATION + /// this + public NameTranslation SetEntityType(string entityType) { + Params[ENTITY_TYPE] = entityType; + + return this; + } + public string? EntityType { get => + Params.ContainsKey(ENTITY_TYPE) ? + Params[ENTITY_TYPE].ToString() : + string.Empty; + } + /// + /// SetSourceLanguageOfOrigin sets the optional ISO 639-3 code for the name's language of origin + /// + /// ISO 639-3 language code + /// this + public NameTranslation SetSourceLanguageOfOrigin(string sourceLanguageOfOrigin) { + Params[SOURCE_LANGUAGE_OF_ORIGIN] = sourceLanguageOfOrigin; + + return this; + } + public string? SourceLanguageOfOrigin { get => + Params.ContainsKey(SOURCE_LANGUAGE_OF_ORIGIN) ? + Params[SOURCE_LANGUAGE_OF_ORIGIN].ToString() : + string.Empty; + } + /// + /// SetSourceLanguageOfUse sets the optional ISO 639-3 code for the name's language of use + /// + /// ISO 639-3 language code + /// this + public NameTranslation SetSourceLanguageOfUse(string sourceLanguageOfUse) { + Params[SOURCE_LANGUAGE_OF_USE] = sourceLanguageOfUse; + + return this; + } + public string? SourceLanguageOfUse { get => + Params.ContainsKey(SOURCE_LANGUAGE_OF_USE) ? + Params[SOURCE_LANGUAGE_OF_USE].ToString() : + String.Empty; + } + /// + /// SetSourceScript sets the optional ISO 15924 code for the name's script + /// + /// ISO 15294 script code + /// this + public NameTranslation SetSourceScript(string sourceScript) { + Params[SOURCE_SCRIPT] = sourceScript; + + return this; + } + public string? SourceScript { get => + Params.ContainsKey(SOURCE_SCRIPT) ? + Params[SOURCE_SCRIPT].ToString() : + string.Empty; + } + /// + /// SetTargetLanguage sets the ISO 639-3 code for the translation language. + /// Defaults to "eng" + /// + /// ISO 639-3 language code + /// this + public NameTranslation SetTargetLanguage(string targetLanguage) { + Params[TARGET_LANGUAGE] = targetLanguage; + + return this; + } + public string? TargetLanguage { get => + Params.ContainsKey(TARGET_LANGUAGE) ? + Params[TARGET_LANGUAGE].ToString() : + String.Empty; + } + /// + /// SetTargetScheme sets the optional transliteration scheme for the translation + /// + /// transliteration scheme + /// this + public NameTranslation SetTargetScheme(string targetScheme) { + Params[TARGET_SCHEME] = targetScheme; + + return this; + } + public string? TargetScheme { get => + Params.ContainsKey(TARGET_SCHEME) ? + Params[TARGET_SCHEME].ToString() : + string.Empty; + } + /// + /// SetTargetScript sets the optional ISO 15924 code for the translation script + /// + /// ISO 15924 script code + /// this + public NameTranslation SetTargetScript(string targetScript) { + Params[TARGET_SCRIPT] = targetScript; + + return this; + } + public string? TargetScript { get => Params.ContainsKey(TARGET_SCRIPT) ? + Params[TARGET_SCRIPT].ToString() : + string.Empty; + } + + public Response Call(ApiClient api) { + return Funcs.PostCall(api); + } + } +} diff --git a/rosette_api/Endpoints/Ping.cs b/rosette_api/Endpoints/Ping.cs new file mode 100644 index 0000000..d3209ce --- /dev/null +++ b/rosette_api/Endpoints/Ping.cs @@ -0,0 +1,11 @@ +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public class Ping : EndpointBase + { + public Ping() : base("ping") { } + + public Response Call(ApiClient api) => Funcs.GetCall(api); + } +} diff --git a/rosette_api/RelationshipsEndpoint.cs b/rosette_api/Endpoints/Relationships.cs old mode 100755 new mode 100644 similarity index 50% rename from rosette_api/RelationshipsEndpoint.cs rename to rosette_api/Endpoints/Relationships.cs index 8593926..e1a94c9 --- a/rosette_api/RelationshipsEndpoint.cs +++ b/rosette_api/Endpoints/Relationships.cs @@ -1,12 +1,14 @@ -namespace rosette_api; - -public class RelationshipsEndpoint : ContentBasedEndpoint -{ - /// - /// RelationshipsEndpoint returns the relationships between entities in the input text - /// - /// text, Uri object or FileStream - public RelationshipsEndpoint(object content) : base("relationships", content) - { - } +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class Relationships : ContentEndpointBase +{ + /// + /// RelationshipsEndpoint returns the relationships between entities in the input text + /// + /// text, Uri object or FileStream + public Relationships(object content) : base("relationships", content) + { + } } \ No newline at end of file diff --git a/rosette_api/Endpoints/SemanticsVector.cs b/rosette_api/Endpoints/SemanticsVector.cs new file mode 100644 index 0000000..484b953 --- /dev/null +++ b/rosette_api/Endpoints/SemanticsVector.cs @@ -0,0 +1,14 @@ +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class SemanticsVector : ContentEndpointBase +{ + /// + /// SemanticVectorsEndpoint returns the relationships between entities in the input text + /// + /// text, Uri object or FileStream + public SemanticsVector(object content) : base("semantics/vector", content) + { + } +} \ No newline at end of file diff --git a/rosette_api/SentencesEndpoint.cs b/rosette_api/Endpoints/Sentences.cs old mode 100755 new mode 100644 similarity index 51% rename from rosette_api/SentencesEndpoint.cs rename to rosette_api/Endpoints/Sentences.cs index e0fa5dd..577953f --- a/rosette_api/SentencesEndpoint.cs +++ b/rosette_api/Endpoints/Sentences.cs @@ -1,11 +1,13 @@ -namespace rosette_api -{ - public class SentencesEndpoint : ContentBasedEndpoint { - /// - /// SentencesEndpoint returns each entity extracted from the input - /// - /// text, Uri object or FileStream - public SentencesEndpoint(object content) : base("sentences", content) { - } - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public class Sentences : ContentEndpointBase { + /// + /// SentencesEndpoint returns each entity extracted from the input + /// + /// text, Uri object or FileStream + public Sentences(object content) : base("sentences", content) { + } + } +} diff --git a/rosette_api/SentimentEndpoint.cs b/rosette_api/Endpoints/Sentiment.cs old mode 100755 new mode 100644 similarity index 55% rename from rosette_api/SentimentEndpoint.cs rename to rosette_api/Endpoints/Sentiment.cs index 5058df4..0bbe478 --- a/rosette_api/SentimentEndpoint.cs +++ b/rosette_api/Endpoints/Sentiment.cs @@ -1,13 +1,15 @@ -namespace rosette_api -{ - public class SentimentEndpoint : ContentBasedEndpoint - { - /// - /// SentimentEndpoint analyzes the positive and negative sentiment expressed by the input - /// - /// text, Uri object or FileStream - public SentimentEndpoint(object content) : base("sentiment", content) - { - } - } +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public class Sentiment : ContentEndpointBase + { + /// + /// SentimentEndpoint analyzes the positive and negative sentiment expressed by the input + /// + /// text, Uri object or FileStream + public Sentiment(object content) : base("sentiment", content) + { + } + } } \ No newline at end of file diff --git a/rosette_api/SyntaxDependenciesEndpoint.cs b/rosette_api/Endpoints/SyntaxDependencies.cs old mode 100755 new mode 100644 similarity index 55% rename from rosette_api/SyntaxDependenciesEndpoint.cs rename to rosette_api/Endpoints/SyntaxDependencies.cs index 86c0d8f..271e364 --- a/rosette_api/SyntaxDependenciesEndpoint.cs +++ b/rosette_api/Endpoints/SyntaxDependencies.cs @@ -1,10 +1,12 @@ -namespace rosette_api; - -public class SyntaxDependenciesEndpoint : ContentBasedEndpoint { - /// - /// SyntaxDependenciesEndpoint returns the parse tree of the input text as a list of labeled directed links between tokens, as well as the list of tokens in the input sentence - /// - /// text, Uri object or FileStream - public SyntaxDependenciesEndpoint(object content) : base("syntax/dependencies", content) { - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class SyntaxDependencies : ContentEndpointBase { + /// + /// SyntaxDependenciesEndpoint returns the parse tree of the input text as a list of labeled directed links between tokens, as well as the list of tokens in the input sentence + /// + /// text, Uri object or FileStream + public SyntaxDependencies(object content) : base("syntax/dependencies", content) { + } +} diff --git a/rosette_api/TextEmbeddingEndpoint.cs b/rosette_api/Endpoints/TextEmbedding.cs old mode 100755 new mode 100644 similarity index 78% rename from rosette_api/TextEmbeddingEndpoint.cs rename to rosette_api/Endpoints/TextEmbedding.cs index 10ba4a9..d1233ce --- a/rosette_api/TextEmbeddingEndpoint.cs +++ b/rosette_api/Endpoints/TextEmbedding.cs @@ -1,69 +1,71 @@ -namespace rosette_api -{ - public class TextEmbeddingEndpoint : EndpointCommon { - /// - /// TextEmbeddingEndpoint returns the embedding for the input text - /// - /// text, Uri object or FileStream - public TextEmbeddingEndpoint(object content) : base("text-embedding") { - SetContent(content); - } - /// - /// SetContent sets the content to be reviewed - /// - /// text, Uri object or FileStream - /// update TextEmbedding endpoint - public TextEmbeddingEndpoint SetContent(object content) { - Funcs.Content = content; - - return this; - } - - public object Content => Funcs.Content; - /// - /// SetLanguage sets the optional ISO 639-3 language code - /// - /// ISO 639-3 language code - /// updated TextEmbedding endpoint - public TextEmbeddingEndpoint SetLanguage(string language) { - Funcs.Language = language; - - return this; - } - - public string? Language => Funcs.Language; - /// - /// SetGenre sets the optional document genre, e.g. social-media - /// - /// document genre - /// updated TextEmbedding endpoint - public TextEmbeddingEndpoint SetGenre(string genre) { - Funcs.Genre = genre; - - return this; - } - - public string? Genre => Funcs.Genre; - /// - /// SetFileContentType sets the content type of the file contents. Note that - /// it only applies when the content is a filename - /// - /// Content-Type - /// updated TextEmbedding endpoint - public TextEmbeddingEndpoint SetFileContentType(string contentType) { - Funcs.FileContentType = contentType; - - return this; - } - public string FileContentType => Funcs.FileContentType; - public string Filename => Funcs.Filename; - /// - /// Call passes the data to the server and returns the response - /// - /// RosetteAPI object - /// RosetteResponse - public RosetteResponse Call(RosetteAPI api) { - return Funcs.PostCall(api); - } - } -} +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints +{ + public class TextEmbedding : EndpointBase { + /// + /// TextEmbeddingEndpoint returns the embedding for the input text + /// + /// text, Uri object or FileStream + public TextEmbedding(object content) : base("text-embedding") { + SetContent(content); + } + /// + /// SetContent sets the content to be reviewed + /// + /// text, Uri object or FileStream + /// update TextEmbedding endpoint + public TextEmbedding SetContent(object content) { + Funcs.Content = content; + + return this; + } + + public object Content => Funcs.Content; + /// + /// SetLanguage sets the optional ISO 639-3 language code + /// + /// ISO 639-3 language code + /// updated TextEmbedding endpoint + public TextEmbedding SetLanguage(string language) { + Funcs.Language = language; + + return this; + } + + public string? Language => Funcs.Language; + /// + /// SetGenre sets the optional document genre, e.g. social-media + /// + /// document genre + /// updated TextEmbedding endpoint + public TextEmbedding SetGenre(string genre) { + Funcs.Genre = genre; + + return this; + } + + public string? Genre => Funcs.Genre; + /// + /// SetFileContentType sets the content type of the file contents. Note that + /// it only applies when the content is a filename + /// + /// Content-Type + /// updated TextEmbedding endpoint + public TextEmbedding SetFileContentType(string contentType) { + Funcs.FileContentType = contentType; + + return this; + } + public string FileContentType => Funcs.FileContentType; + public string Filename => Funcs.Filename; + /// + /// Call passes the data to the server and returns the response + /// + /// RosetteAPI object + /// RosetteResponse + public Response Call(ApiClient api) { + return Funcs.PostCall(api); + } + } +} diff --git a/rosette_api/Endpoints/Tokens.cs b/rosette_api/Endpoints/Tokens.cs new file mode 100644 index 0000000..257d33c --- /dev/null +++ b/rosette_api/Endpoints/Tokens.cs @@ -0,0 +1,12 @@ +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class Tokens : ContentEndpointBase { + /// + /// TokensEndpoint returns each entity extracted from the input + /// + /// text, Uri object or FileStream + public Tokens(object content) : base("tokens", content) { + } +} diff --git a/rosette_api/Endpoints/Topics.cs b/rosette_api/Endpoints/Topics.cs new file mode 100644 index 0000000..68bdadb --- /dev/null +++ b/rosette_api/Endpoints/Topics.cs @@ -0,0 +1,12 @@ +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class Topics : ContentEndpointBase { + /// + /// TopicsEndpoint returns the topic extracted from the endpoint + /// + /// text, Uri object or FileStream + public Topics(object content) : base("topics", content) { + } +} diff --git a/rosette_api/Endpoints/Transliteration.cs b/rosette_api/Endpoints/Transliteration.cs new file mode 100644 index 0000000..62b2415 --- /dev/null +++ b/rosette_api/Endpoints/Transliteration.cs @@ -0,0 +1,12 @@ +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class Transliteration : ContentEndpointBase { + /// + /// TransliterationEndpoint returns the transliteration of the input + /// + /// text, Uri object or FileStream + public Transliteration(object content) : base("transliteration", content) { + } +} diff --git a/rosette_api/InfoEndpoint.cs b/rosette_api/InfoEndpoint.cs deleted file mode 100755 index e48dadb..0000000 --- a/rosette_api/InfoEndpoint.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace rosette_api -{ - public class InfoEndpoint : EndpointCommon - { - public InfoEndpoint() : base("info") { } - - public RosetteResponse Call(RosetteAPI api) => Funcs.GetCall(api); - } -} diff --git a/rosette_api/RosetteName.cs b/rosette_api/Name.cs old mode 100755 new mode 100644 similarity index 85% rename from rosette_api/RosetteName.cs rename to rosette_api/Name.cs index 1908a64..9614ccb --- a/rosette_api/RosetteName.cs +++ b/rosette_api/Name.cs @@ -1,81 +1,81 @@ -using System.Text.Json.Serialization; - -namespace rosette_api; - -public class RosetteName -{ - [JsonPropertyName("text")] - public string Text { get; private set; } - - [JsonPropertyName("entityType")] - public string? EntityType { get; private set; } - - [JsonPropertyName("language")] - public string? Language { get; private set; } - - [JsonPropertyName("script")] - public string? Script { get; private set; } - - /// - /// Constructor for a Name object, used by several endpoints - /// - /// required text - /// optional entity type - /// optional language code - /// optional script code - [JsonConstructor] - public RosetteName(string text, string? entityType = null, string? language = null, string? script = null) - { - ArgumentException.ThrowIfNullOrWhiteSpace(text); - Text = text; - EntityType = entityType; - Language = language; - Script = script; - } - - /// - /// SetEntityType sets the optional entity type. PERSON, LOCATION and ORGANIZATION - /// are currently supported. - /// - /// entity type, PERSON, LOCATION or ORGANIZATION - /// updated RosetteName object - public RosetteName SetEntityType(string type) - { - ArgumentException.ThrowIfNullOrWhiteSpace(type); - - string[] validTypes = ["PERSON", "LOCATION", "ORGANIZATION"]; - if (!validTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) - { - throw new ArgumentException( - $"Entity type must be one of: {string.Join(", ", validTypes)}. Provided: {type}", - nameof(type)); - } - - EntityType = type.ToUpperInvariant(); - return this; - } - - /// - /// SetLanguage sets the optional ISO-639-3 language code for the name's language - /// - /// ISO-639-3 language code - /// updated RosetteName object - public RosetteName SetLanguage(string language) - { - ArgumentException.ThrowIfNullOrWhiteSpace(language); - Language = language; - return this; - } - - /// - /// SetScript sets the ISO-15924 code for the name's script - /// - /// ISO-15924 script code - /// updated RosetteName object - public RosetteName SetScript(string script) - { - ArgumentException.ThrowIfNullOrWhiteSpace(script); - Script = script; - return this; - } +using System.Text.Json.Serialization; + +namespace Rosette.Api; + +public class Name +{ + [JsonPropertyName("text")] + public string Text { get; private set; } + + [JsonPropertyName("entityType")] + public string? EntityType { get; private set; } + + [JsonPropertyName("language")] + public string? Language { get; private set; } + + [JsonPropertyName("script")] + public string? Script { get; private set; } + + /// + /// Constructor for a Name object, used by several endpoints + /// + /// required text + /// optional entity type + /// optional language code + /// optional script code + [JsonConstructor] + public Name(string text, string? entityType = null, string? language = null, string? script = null) + { + ArgumentException.ThrowIfNullOrWhiteSpace(text); + Text = text; + EntityType = entityType; + Language = language; + Script = script; + } + + /// + /// SetEntityType sets the optional entity type. PERSON, LOCATION and ORGANIZATION + /// are currently supported. + /// + /// entity type, PERSON, LOCATION or ORGANIZATION + /// updated RosetteName object + public Name SetEntityType(string type) + { + ArgumentException.ThrowIfNullOrWhiteSpace(type); + + string[] validTypes = ["PERSON", "LOCATION", "ORGANIZATION"]; + if (!validTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) + { + throw new ArgumentException( + $"Entity type must be one of: {string.Join(", ", validTypes)}. Provided: {type}", + nameof(type)); + } + + EntityType = type.ToUpperInvariant(); + return this; + } + + /// + /// SetLanguage sets the optional ISO-639-3 language code for the name's language + /// + /// ISO-639-3 language code + /// updated RosetteName object + public Name SetLanguage(string language) + { + ArgumentException.ThrowIfNullOrWhiteSpace(language); + Language = language; + return this; + } + + /// + /// SetScript sets the ISO-15924 code for the name's script + /// + /// ISO-15924 script code + /// updated RosetteName object + public Name SetScript(string script) + { + ArgumentException.ThrowIfNullOrWhiteSpace(script); + Script = script; + return this; + } } \ No newline at end of file diff --git a/rosette_api/PingEndpoint.cs b/rosette_api/PingEndpoint.cs deleted file mode 100755 index a87e855..0000000 --- a/rosette_api/PingEndpoint.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace rosette_api -{ - public class PingEndpoint : EndpointCommon - { - public PingEndpoint() : base("ping") { } - - public RosetteResponse Call(RosetteAPI api) => Funcs.GetCall(api); - } -} diff --git a/rosette_api/RosetteResponse.cs b/rosette_api/Response.cs old mode 100755 new mode 100644 similarity index 96% rename from rosette_api/RosetteResponse.cs rename to rosette_api/Response.cs index f52bc2d..cb6d228 --- a/rosette_api/RosetteResponse.cs +++ b/rosette_api/Response.cs @@ -2,11 +2,11 @@ using System.Text; using System.Text.Json; -namespace rosette_api; +namespace Rosette.Api; -public class RosetteResponse +public class Response { - public RosetteResponse(HttpResponseMessage responseMsg) { + public Response(HttpResponseMessage responseMsg) { Content = new Dictionary(); Headers = new Dictionary(); diff --git a/rosette_api/SemanticsVectorEndpoint.cs b/rosette_api/SemanticsVectorEndpoint.cs deleted file mode 100644 index 423e736..0000000 --- a/rosette_api/SemanticsVectorEndpoint.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace rosette_api; - -public class SemanticsVectorEndpoint : ContentBasedEndpoint -{ - /// - /// SemanticVectorsEndpoint returns the relationships between entities in the input text - /// - /// text, Uri object or FileStream - public SemanticsVectorEndpoint(object content) : base("semantics/vector", content) - { - } -} \ No newline at end of file diff --git a/rosette_api/TokensEndpoint.cs b/rosette_api/TokensEndpoint.cs deleted file mode 100755 index e512de4..0000000 --- a/rosette_api/TokensEndpoint.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace rosette_api; - -public class TokensEndpoint : ContentBasedEndpoint { - /// - /// TokensEndpoint returns each entity extracted from the input - /// - /// text, Uri object or FileStream - public TokensEndpoint(object content) : base("tokens", content) { - } -} diff --git a/rosette_api/TopicsEndpoint.cs b/rosette_api/TopicsEndpoint.cs deleted file mode 100755 index 87096de..0000000 --- a/rosette_api/TopicsEndpoint.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace rosette_api; - -public class TopicsEndpoint : ContentBasedEndpoint { - /// - /// TopicsEndpoint returns the topic extracted from the endpoint - /// - /// text, Uri object or FileStream - public TopicsEndpoint(object content) : base("topics", content) { - } -} diff --git a/rosette_api/TransliterationEndpoint.cs b/rosette_api/TransliterationEndpoint.cs deleted file mode 100755 index 09fc723..0000000 --- a/rosette_api/TransliterationEndpoint.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace rosette_api; - -public class TransliterationEndpoint : ContentBasedEndpoint { - /// - /// TransliterationEndpoint returns the transliteration of the input - /// - /// text, Uri object or FileStream - public TransliterationEndpoint(object content) : base("transliteration", content) { - } -} diff --git a/tests/TestAPI.cs b/tests/ApiClientTests.cs old mode 100755 new mode 100644 similarity index 82% rename from tests/TestAPI.cs rename to tests/ApiClientTests.cs index 3271e28..c1ee05a --- a/tests/TestAPI.cs +++ b/tests/ApiClientTests.cs @@ -1,99 +1,96 @@ -using rosette_api; -using Xunit; - -namespace tests -{ - public class TestAPI - { - private static readonly string _defaultUri = "https://api.rosette.com/rest/v1/"; - private static readonly string _testKey = "testKey"; - - private static RosetteAPI Init() { - return new RosetteAPI(_testKey); - } - - [Fact] - public void TestKey() { - RosetteAPI api = Init(); - Assert.Equal(_testKey, api.APIKey); - } - - [Fact] - public void TestNullKey() { -#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. - string key = null; -#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. -#pragma warning disable CS8604 // Possible null reference argument. - Exception ex = Assert.Throws(() => new RosetteAPI(key)); -#pragma warning restore CS8604 // Possible null reference argument. - - Assert.Equal("Value cannot be null. (Parameter 'apiKey')", ex.Message); - } - - [Fact] - public void TestURI() { - RosetteAPI api = Init(); - Assert.Equal(_defaultUri, api.URI); - - // test alternate url as well as auto append trailing slash - string alternateUrl = "https://stage.rosette.com/rest/v1"; - api.UseAlternateURL(alternateUrl); - Assert.Equal(alternateUrl + "/", api.URI); - } - - [Fact] - public void TestConnections() { - RosetteAPI api = Init(); - Assert.Equal(2, api.ConcurrentConnections); - - api.AssignConcurrentConnections(6); - Assert.Equal(6, api.ConcurrentConnections); - } - - [Fact] - public void TestValidCustomHeader() { - RosetteAPI api = Init(); - Exception ex = Assert.Throws(() => api.AddCustomHeader("BogusHeader", "BogusValue")); - - Assert.Contains(@"Custom header name must begin with 'X-RosetteAPI-'", ex.Message); - } - - [Fact] - public void TestVersion() { - Assert.NotEmpty(RosetteAPI.Version); - } - - [Fact] - public void TestTimeout() { - RosetteAPI api = Init(); - Assert.Equal(300, api.Timeout); - - api.AssignTimeout(15); - Assert.Equal(15, api.Timeout); - } - - [Fact] - public void TestDebug() { - RosetteAPI api = Init(); - Assert.False(api.Debug); - - api.SetDebug(); - Assert.True(api.Debug); - } - - [Fact] - public void TestDefaultClient() { - RosetteAPI api = Init(); - - Assert.Equal(_defaultUri, api.Client.BaseAddress.AbsoluteUri); - var acceptHeader = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"); - Assert.Contains(acceptHeader, api.Client.DefaultRequestHeaders.Accept); - foreach (string encodingType in new List() { "gzip", "deflate" }) { - var encodingHeader = new System.Net.Http.Headers.StringWithQualityHeaderValue(encodingType); - Assert.Contains(encodingHeader, api.Client.DefaultRequestHeaders.AcceptEncoding); - } - Assert.Equal(api.Timeout, api.Client.Timeout.TotalSeconds); - } - - } -} +namespace Rosette.Api.Tests +{ + public class ApiClientTests + { + private static readonly string _defaultUri = "https://api.rosette.com/rest/v1/"; + private static readonly string _testKey = "testKey"; + + private static ApiClient Init() { + return new ApiClient(_testKey); + } + + [Fact] + public void TestKey() { + ApiClient api = Init(); + Assert.Equal(_testKey, api.APIKey); + } + + [Fact] + public void TestNullKey() { +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. + string key = null; +#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8604 // Possible null reference argument. + Exception ex = Assert.Throws(() => new ApiClient(key)); +#pragma warning restore CS8604 // Possible null reference argument. + + Assert.Equal("Value cannot be null. (Parameter 'apiKey')", ex.Message); + } + + [Fact] + public void TestURI() { + ApiClient api = Init(); + Assert.Equal(_defaultUri, api.URI); + + // test alternate url as well as auto append trailing slash + string alternateUrl = "https://stage.rosette.com/rest/v1"; + api.UseAlternateURL(alternateUrl); + Assert.Equal(alternateUrl + "/", api.URI); + } + + [Fact] + public void TestConnections() { + ApiClient api = Init(); + Assert.Equal(2, api.ConcurrentConnections); + + api.AssignConcurrentConnections(6); + Assert.Equal(6, api.ConcurrentConnections); + } + + [Fact] + public void TestValidCustomHeader() { + ApiClient api = Init(); + Exception ex = Assert.Throws(() => api.AddCustomHeader("BogusHeader", "BogusValue")); + + Assert.Contains(@"Custom header name must begin with 'X-RosetteAPI-'", ex.Message); + } + + [Fact] + public void TestVersion() { + Assert.NotEmpty(ApiClient.Version); + } + + [Fact] + public void TestTimeout() { + ApiClient api = Init(); + Assert.Equal(300, api.Timeout); + + api.AssignTimeout(15); + Assert.Equal(15, api.Timeout); + } + + [Fact] + public void TestDebug() { + ApiClient api = Init(); + Assert.False(api.Debug); + + api.SetDebug(); + Assert.True(api.Debug); + } + + [Fact] + public void TestDefaultClient() { + ApiClient api = Init(); + + Assert.Equal(_defaultUri, api.Client.BaseAddress.AbsoluteUri); + var acceptHeader = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"); + Assert.Contains(acceptHeader, api.Client.DefaultRequestHeaders.Accept); + foreach (string encodingType in new List() { "gzip", "deflate" }) { + var encodingHeader = new System.Net.Http.Headers.StringWithQualityHeaderValue(encodingType); + Assert.Contains(encodingHeader, api.Client.DefaultRequestHeaders.AcceptEncoding); + } + Assert.Equal(api.Timeout, api.Client.Timeout.TotalSeconds); + } + + } +} diff --git a/tests/TestEndpointCommon.cs b/tests/EndpointBaseTests.cs old mode 100755 new mode 100644 similarity index 62% rename from tests/TestEndpointCommon.cs rename to tests/EndpointBaseTests.cs index d58fc72..de099cf --- a/tests/TestEndpointCommon.cs +++ b/tests/EndpointBaseTests.cs @@ -1,18 +1,18 @@ -using rosette_api; -using Xunit; +using Rosette.Api.Endpoints; +using Rosette.Api.Endpoints.Core; -namespace tests { - public class TestEndpointCommon +namespace Rosette.Api.Tests { + public class EndpointBaseTests { - private static RosetteAPI Init() { - return new RosetteAPI("testkey"); + private static ApiClient Init() { + return new ApiClient("testkey"); } [Fact] public void CheckEndpoint() { - EndpointCommon ec = new EndpointCommon("foo"); + EndpointBase ec = new EndpointBase("foo"); Assert.Equal("foo", ec.Endpoint); } @@ -20,7 +20,7 @@ public void CheckEndpoint() { [Fact] public void CheckOptions() { - EntitiesEndpoint e = new EntitiesEndpoint("foo").SetOption("test", "value"); + Entities e = new Entities("foo").SetOption("test", "value"); Assert.Equal("value", e.Options["test"]); @@ -39,7 +39,7 @@ public void CheckOptions() { [Fact] public void CheckUrlParameters() { - EntitiesEndpoint e = new EntitiesEndpoint("foo").SetUrlParameter("test", "value"); + Entities e = new Entities("foo").SetUrlParameter("test", "value"); Assert.Equal("value", e.UrlParameters["test"]); diff --git a/tests/TestEndpointFunctions.cs b/tests/EndpointExecutorTests.cs old mode 100755 new mode 100644 similarity index 71% rename from tests/TestEndpointFunctions.cs rename to tests/EndpointExecutorTests.cs index f4fbf71..98a6342 --- a/tests/TestEndpointFunctions.cs +++ b/tests/EndpointExecutorTests.cs @@ -1,140 +1,139 @@ -using Xunit; -using RichardSzalay.MockHttp; -using System.Collections.Specialized; -using System.Net; -using System.Text.Json; -using rosette_api; - -namespace tests -{ - public class TestEndpointFunctions - { - private readonly Dictionary _params; - private readonly Dictionary _options; - private readonly NameValueCollection _urlParameters; - private static readonly string _defaultUri = "https://api.rosette.com/rest/v1/*"; - - public TestEndpointFunctions() { - _params = new Dictionary(); - _options = new Dictionary(); - _urlParameters = new NameValueCollection(); - } - - [Fact] - public void CheckContent() { - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - Assert.Empty(f.Content.ToString()!); - f.Content = "Sample Content"; - Assert.Equal("Sample Content", f.Content); - Assert.True(_params.ContainsKey("content")); - Assert.False(_params.ContainsKey("contenturi")); - } - - [Fact] - public void CheckEndpoint() { - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - Assert.Equal("test", f.Endpoint); - } - - [Fact] - public void CheckContentUri() { - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - Assert.Empty(f.Content.ToString()!); - f.Content = new Uri("http://google.com"); - Assert.Equal("http://google.com/", f.Content); - Assert.True(_params.ContainsKey("contenturi")); - Assert.False(_params.ContainsKey("content")); - } - - [Fact] - public void CheckFilename() { - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - Assert.Empty(f.Content.ToString()!); - var newFile = Path.GetTempFileName(); - using (FileStream fs = File.OpenRead(newFile)) { - f.Content = fs; - Assert.Equal(newFile, f.Filename); - Assert.Empty(f.Content.ToString()!); - Assert.False(_params.ContainsKey("content")); - Assert.False(_params.ContainsKey("contenturi")); - } - } - - [Fact] - public void CheckLanguage() { - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - Assert.Empty(f.Language!); - f.Language = "eng"; - Assert.Equal("eng", f.Language); - } - - [Fact] - public void CheckGenre() { - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - Assert.Empty(f.Genre!); - f.Genre = "social-media"; - Assert.Equal("social-media", f.Genre); - } - - [Fact] - public void CheckFileContentType() { - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - Assert.Equal("text/plain", f.FileContentType); - f.FileContentType = "octet/stream"; - Assert.Equal("octet/stream", f.FileContentType); - } - - [Fact] - public void TestParameterSerialization() { - RosetteAPI api = new RosetteAPI("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - - api.AssignClient(client); - - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - - _options["opt"] = true; - Dictionary paramTest = new Dictionary(); - paramTest["content"] = "Test Content"; - paramTest["options"] = _options; - - f.Content = "Test Content"; - RosetteResponse result = f.PostCall(api); - Assert.Equal(JsonSerializer.Serialize(paramTest), JsonSerializer.Serialize(f.Parameters)); - } - - [Fact] - public void TestPostCall() { - RosetteAPI api = new RosetteAPI("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - - api.AssignClient(client); - - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - f.Content = "Test content"; - RosetteResponse result = f.PostCall(api); - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - } - - [Fact] - public void TestGetCall() { - RosetteAPI api = new RosetteAPI("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - - api.AssignClient(client); - - EndpointFunctions f = new EndpointFunctions(_params, _options, _urlParameters, "test"); - RosetteResponse result = f.GetCall(api); - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - } - } -} +using RichardSzalay.MockHttp; +using System.Collections.Specialized; +using System.Net; +using System.Text.Json; +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Tests +{ + public class EndpointExecutorTests + { + private readonly Dictionary _params; + private readonly Dictionary _options; + private readonly NameValueCollection _urlParameters; + private static readonly string _defaultUri = "https://api.rosette.com/rest/v1/*"; + + public EndpointExecutorTests() { + _params = new Dictionary(); + _options = new Dictionary(); + _urlParameters = new NameValueCollection(); + } + + [Fact] + public void CheckContent() { + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + Assert.Empty(f.Content.ToString()!); + f.Content = "Sample Content"; + Assert.Equal("Sample Content", f.Content); + Assert.True(_params.ContainsKey("content")); + Assert.False(_params.ContainsKey("contenturi")); + } + + [Fact] + public void CheckEndpoint() { + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + Assert.Equal("test", f.Endpoint); + } + + [Fact] + public void CheckContentUri() { + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + Assert.Empty(f.Content.ToString()!); + f.Content = new Uri("http://google.com"); + Assert.Equal("http://google.com/", f.Content); + Assert.True(_params.ContainsKey("contenturi")); + Assert.False(_params.ContainsKey("content")); + } + + [Fact] + public void CheckFilename() { + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + Assert.Empty(f.Content.ToString()!); + var newFile = Path.GetTempFileName(); + using (FileStream fs = File.OpenRead(newFile)) { + f.Content = fs; + Assert.Equal(newFile, f.Filename); + Assert.Empty(f.Content.ToString()!); + Assert.False(_params.ContainsKey("content")); + Assert.False(_params.ContainsKey("contenturi")); + } + } + + [Fact] + public void CheckLanguage() { + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + Assert.Empty(f.Language!); + f.Language = "eng"; + Assert.Equal("eng", f.Language); + } + + [Fact] + public void CheckGenre() { + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + Assert.Empty(f.Genre!); + f.Genre = "social-media"; + Assert.Equal("social-media", f.Genre); + } + + [Fact] + public void CheckFileContentType() { + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + Assert.Equal("text/plain", f.FileContentType); + f.FileContentType = "octet/stream"; + Assert.Equal("octet/stream", f.FileContentType); + } + + [Fact] + public void TestParameterSerialization() { + ApiClient api = new ApiClient("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + + api.AssignClient(client); + + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + + _options["opt"] = true; + Dictionary paramTest = new Dictionary(); + paramTest["content"] = "Test Content"; + paramTest["options"] = _options; + + f.Content = "Test Content"; + Response result = f.PostCall(api); + Assert.Equal(JsonSerializer.Serialize(paramTest), JsonSerializer.Serialize(f.Parameters)); + } + + [Fact] + public void TestPostCall() { + ApiClient api = new ApiClient("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + + api.AssignClient(client); + + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + f.Content = "Test content"; + Response result = f.PostCall(api); + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + } + + [Fact] + public void TestGetCall() { + ApiClient api = new ApiClient("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + + api.AssignClient(client); + + EndpointExecutor f = new EndpointExecutor(_params, _options, _urlParameters, "test"); + Response result = f.GetCall(api); + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + } + } +} diff --git a/tests/TestForValidEndpoint.cs b/tests/TestForValidEndpoint.cs index fd3fca0..56dc394 100755 --- a/tests/TestForValidEndpoint.cs +++ b/tests/TestForValidEndpoint.cs @@ -1,20 +1,19 @@ -using rosette_api; -using Xunit; +using Rosette.Api.Endpoints; -namespace tests +namespace Rosette.Api.Tests { public class TestForValidEndpoint { [Fact] public void CategoriesEndpoint() { - CategoriesEndpoint c = new CategoriesEndpoint("foo"); + Categories c = new Categories("foo"); Assert.Equal("categories", c.Endpoint); Assert.Equal("foo", c.Content); } [Fact] public void EntitiesEndpoint() { - EntitiesEndpoint e = new EntitiesEndpoint("foo"); + Entities e = new Entities("foo"); Assert.Equal("entities", e.Endpoint); Assert.Equal("foo", e.Content); } @@ -22,33 +21,33 @@ public void EntitiesEndpoint() { [Fact] public void EventsEndpoint() { - EventsEndpoint e = new EventsEndpoint("foo"); + Events e = new Events("foo"); Assert.Equal("events", e.Endpoint); Assert.Equal("foo", e.Content); } [Fact] public void InfoEndpoint() { - InfoEndpoint i = new InfoEndpoint(); + Info i = new Info(); Assert.Equal("info", i.Endpoint); } [Fact] public void LanguageEndpoint() { - LanguageEndpoint l = new LanguageEndpoint("foo"); + Language l = new Language("foo"); Assert.Equal("language", l.Endpoint); Assert.Equal("foo", l.Content); } [Theory] - [InlineData(rosette_api.MorphologyFeature.complete)] - [InlineData(rosette_api.MorphologyFeature.compoundComponents)] - [InlineData(rosette_api.MorphologyFeature.hanReadings)] - [InlineData(rosette_api.MorphologyFeature.lemmas)] - [InlineData(rosette_api.MorphologyFeature.partsOfSpeech)] + [InlineData(MorphologyFeature.complete)] + [InlineData(MorphologyFeature.compoundComponents)] + [InlineData(MorphologyFeature.hanReadings)] + [InlineData(MorphologyFeature.lemmas)] + [InlineData(MorphologyFeature.partsOfSpeech)] public void MorphologyEndpoint(MorphologyFeature feature) { - MorphologyEndpoint m = new MorphologyEndpoint("foo", feature); + Morphology m = new Morphology("foo", feature); Assert.Equal("morphology/" + m.FeatureAsString(feature), m.Endpoint); Assert.Equal("foo", m.Content); @@ -56,20 +55,20 @@ public void MorphologyEndpoint(MorphologyFeature feature) { [Fact] public void NameSimilarityEndpoint() { - RosetteName rn = new RosetteName("foo"); - NameSimilarityEndpoint ns = new NameSimilarityEndpoint(rn, rn); + Name rn = new Name("foo"); + NameSimilarity ns = new NameSimilarity(rn, rn); Assert.Equal("name-similarity", ns.Endpoint); } [Fact] public void PingEndpoint() { - PingEndpoint p = new PingEndpoint(); + Ping p = new Ping(); Assert.Equal("ping", p.Endpoint); } [Fact] public void RelationshipsEndpoint() { - RelationshipsEndpoint r = new RelationshipsEndpoint("foo"); + Relationships r = new Relationships("foo"); Assert.Equal("relationships", r.Endpoint); Assert.Equal("foo", r.Content); @@ -78,28 +77,28 @@ public void RelationshipsEndpoint() { [Fact] public void SemanticVectorsEndpoint() { - SemanticsVectorEndpoint s = new SemanticsVectorEndpoint("foo"); + SemanticsVector s = new SemanticsVector("foo"); Assert.Equal("semantics/vector", s.Endpoint); Assert.Equal("foo", s.Content); } [Fact] public void SentencesEndpoint() { - SentencesEndpoint s = new SentencesEndpoint("foo"); + Sentences s = new Sentences("foo"); Assert.Equal("sentences", s.Endpoint); Assert.Equal("foo", s.Content); } [Fact] public void SentimentEndpoint() { - SentimentEndpoint s = new SentimentEndpoint("foo"); + Sentiment s = new Sentiment("foo"); Assert.Equal("sentiment", s.Endpoint); Assert.Equal("foo", s.Content); } [Fact] public void SyntaxDependenciesEndpoint() { - SyntaxDependenciesEndpoint s = new SyntaxDependenciesEndpoint("foo"); + SyntaxDependencies s = new SyntaxDependencies("foo"); Assert.Equal("syntax/dependencies", s.Endpoint); Assert.Equal("foo", s.Content); @@ -107,7 +106,7 @@ public void SyntaxDependenciesEndpoint() { [Fact] public void TextEmbeddingEndpoint() { - TextEmbeddingEndpoint t = new TextEmbeddingEndpoint("foo"); + TextEmbedding t = new TextEmbedding("foo"); Assert.Equal("text-embedding", t.Endpoint); Assert.Equal("foo", t.Content); @@ -115,7 +114,7 @@ public void TextEmbeddingEndpoint() { [Fact] public void TokensEndpoint() { - TokensEndpoint t = new TokensEndpoint("foo"); + Tokens t = new Tokens("foo"); Assert.Equal("tokens", t.Endpoint); Assert.Equal("foo", t.Content); @@ -123,7 +122,7 @@ public void TokensEndpoint() { [Fact] public void TopicsEndpoint() { - TopicsEndpoint t = new TopicsEndpoint("foo"); + Topics t = new Topics("foo"); Assert.Equal("topics", t.Endpoint); Assert.Equal("foo", t.Content); @@ -131,7 +130,7 @@ public void TopicsEndpoint() { [Fact] public void TransliterationEndpoint() { - TransliterationEndpoint t = new TransliterationEndpoint("foo"); + Transliteration t = new Transliteration("foo"); Assert.Equal("transliteration", t.Endpoint); Assert.Equal("foo", t.Content); diff --git a/tests/TestNameDeduplication.cs b/tests/TestNameDeduplication.cs index 68d6bf1..2dfc5f3 100755 --- a/tests/TestNameDeduplication.cs +++ b/tests/TestNameDeduplication.cs @@ -1,39 +1,38 @@ -using rosette_api; -using Xunit; +using Rosette.Api.Endpoints; -namespace tests +namespace Rosette.Api.Tests { public class TestNameDeduplication { [Fact] public void CheckBasicUsage() { - List names = new List { - new RosetteName("foo"), - new RosetteName("bar") + List names = new List { + new Name("foo"), + new Name("bar") }; - NameDeduplicationEndpoint n = new NameDeduplicationEndpoint(names); + NameDeduplication n = new NameDeduplication(names); Assert.Equal(names, n.Names); Assert.Equal(0.75f, n.Threshold); } [Fact] public void CheckProfileID() { - List names = new List { - new RosetteName("foo"), - new RosetteName("bar") + List names = new List { + new Name("foo"), + new Name("bar") }; - NameDeduplicationEndpoint n = new NameDeduplicationEndpoint(names).SetProfileID("profileid"); + NameDeduplication n = new NameDeduplication(names).SetProfileID("profileid"); Assert.Equal(names, n.Names); Assert.Equal("profileid", n.ProfileID); } [Fact] public void CheckThreshold() { - List names = new List { - new RosetteName("foo"), - new RosetteName("bar") + List names = new List { + new Name("foo"), + new Name("bar") }; - NameDeduplicationEndpoint n = new NameDeduplicationEndpoint(names).SetThreshold(0.8f); + NameDeduplication n = new NameDeduplication(names).SetThreshold(0.8f); Assert.Equal(names, n.Names); Assert.Equal(0.8f, n.Threshold); } diff --git a/tests/TestNameSimilarity.cs b/tests/TestNameSimilarity.cs index b9834e4..2f04b67 100755 --- a/tests/TestNameSimilarity.cs +++ b/tests/TestNameSimilarity.cs @@ -1,18 +1,17 @@ -using rosette_api; -using Xunit; +using Rosette.Api.Endpoints; -namespace tests +namespace Rosette.Api.Tests { public class TestNameSimilarity { [Fact] public void CheckForNull() { - var exception = Record.Exception(() => new NameSimilarityEndpoint(null, null)); + var exception = Record.Exception(() => new NameSimilarity(null, null)); Assert.IsType(exception); Assert.Equal("Value cannot be null. (Parameter 'name1')", exception.Message); - RosetteName rn = new RosetteName("foo"); - exception = Record.Exception(() => new NameSimilarityEndpoint(rn, null)); + Name rn = new Name("foo"); + exception = Record.Exception(() => new NameSimilarity(rn, null)); Assert.IsType(exception); Assert.Equal("Value cannot be null. (Parameter 'name2')", exception.Message); } diff --git a/tests/TestNameTranslation.cs b/tests/TestNameTranslation.cs index 5d32c3d..a1c2c60 100755 --- a/tests/TestNameTranslation.cs +++ b/tests/TestNameTranslation.cs @@ -1,13 +1,12 @@ -using rosette_api; -using Xunit; +using Rosette.Api.Endpoints; -namespace tests +namespace Rosette.Api.Tests { public class TestNameTranslation { [Fact] public void CheckBasicUsage() { - NameTranslationEndpoint n = new NameTranslationEndpoint("foo"); + NameTranslation n = new NameTranslation("foo"); Assert.Equal("foo", n.Name); Assert.Equal("eng", n.TargetLanguage); Assert.Empty(n.EntityType); @@ -20,7 +19,7 @@ public void CheckBasicUsage() { [Fact] public void CheckAllUsage() { - NameTranslationEndpoint n = new NameTranslationEndpoint("foo") + NameTranslation n = new NameTranslation("foo") .SetEntityType("PERSON") .SetSourceLanguageOfOrigin("eng") .SetSourceLanguageOfUse("eng") diff --git a/tests/TestRosetteName.cs b/tests/TestRosetteName.cs index 2648aba..cca371d 100755 --- a/tests/TestRosetteName.cs +++ b/tests/TestRosetteName.cs @@ -1,13 +1,10 @@ -using rosette_api; -using Xunit; - -namespace tests +namespace Rosette.Api.Tests { public class TestRosetteName { [Fact] public void CheckName() { - RosetteName rn = new RosetteName("foo"); + Name rn = new Name("foo"); Assert.Equal("foo", rn.Text); Assert.Null(rn.EntityType); Assert.Null(rn.Language); @@ -16,28 +13,28 @@ public void CheckName() { [Fact] public void CheckWithEntityType() { - RosetteName rn = new RosetteName("foo").SetEntityType("PERSON"); + Name rn = new Name("foo").SetEntityType("PERSON"); Assert.Equal("foo", rn.Text); Assert.Equal("PERSON", rn.EntityType); } [Fact] public void CheckWithLanguage() { - RosetteName rn = new RosetteName("foo").SetLanguage("eng"); + Name rn = new Name("foo").SetLanguage("eng"); Assert.Equal("foo", rn.Text); Assert.Equal("eng", rn.Language); } [Fact] public void CheckWithScript() { - RosetteName rn = new RosetteName("foo").SetScript("zho"); + Name rn = new Name("foo").SetScript("zho"); Assert.Equal("foo", rn.Text); Assert.Equal("zho", rn.Script); } [Fact] public void CheckAll() { - RosetteName rn = new RosetteName("foo") + Name rn = new Name("foo") .SetEntityType("PERSON") .SetLanguage("eng") .SetScript("zho"); diff --git a/tests/TestRosetteResponse.cs b/tests/TestRosetteResponse.cs index 6b592c2..69e1b77 100755 --- a/tests/TestRosetteResponse.cs +++ b/tests/TestRosetteResponse.cs @@ -1,9 +1,7 @@ -using Xunit; -using System.Net; +using System.Net; using System.Text.Json; -using rosette_api; -namespace tests +namespace Rosette.Api.Tests { public class TestRosetteResponse { @@ -19,7 +17,7 @@ public void CheckStatusOK() { msg.Content = new StringContent(json); msg.Headers.Add("Test-Header", "Test Header Content"); - RosetteResponse response = new RosetteResponse(msg); + Response response = new Response(msg); Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); Assert.Equal(json, response.ContentAsJson()); From 886b85bf42b10e4862c08e2c2c0aa0e77bf4fdbf Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Wed, 11 Feb 2026 20:13:12 -0500 Subject: [PATCH 03/10] BX-68163: Added new example classes and reorganized namespaces --- examples/AddressSimilarity.cs | 65 ++ examples/Categories.cs | 5 + examples/Entities.cs | 1 + examples/Events.cs | 1 + examples/Info.cs | 1 + examples/Language.cs | 1 + examples/LanguageMultilingual.cs | 1 + examples/MorphologyComplete.cs | 1 + examples/MorphologyCompoundComponents.cs | 1 + examples/MorphologyHanReadings.cs | 1 + examples/MorphologyLemmas.cs | 2 + examples/MorphologyPartsOfSpeech.cs | 1 + examples/NameDeduplication.cs | 1 + examples/{NameMatch.cs => NameSimilarity.cs} | 13 +- examples/NameTranslation.cs | 1 + examples/Ping.cs | 1 + examples/RecordSimilarity.cs | 62 ++ examples/Relationships.cs | 1 + examples/SemanticsVector.cs | 1 + examples/Sentences.cs | 1 + examples/Sentiment.cs | 1 + examples/SimilarTerms.cs | 64 ++ examples/SyntaxDependencies.cs | 1 + examples/TextEmbedding.cs | 1 + examples/Tokens.cs | 1 + examples/Topics.cs | 1 + examples/Transliteration.cs | 1 + rosette_api/Endpoints/AddressSimilarity.cs | 26 + .../Endpoints/Core/ContentEndpointBase.cs | 2 + .../Endpoints/Core/EndpointExecutor.cs | 3 +- rosette_api/Endpoints/Info.cs | 1 + rosette_api/Endpoints/Morphology.cs | 1 + rosette_api/Endpoints/NameDeduplication.cs | 1 + rosette_api/Endpoints/NameSimilarity.cs | 1 + rosette_api/Endpoints/NameTranslation.cs | 1 + rosette_api/Endpoints/Ping.cs | 1 + rosette_api/Endpoints/RecordSimilarity.cs | 14 + rosette_api/Endpoints/SimilarTerms.cs | 14 + rosette_api/Endpoints/TextEmbedding.cs | 1 + rosette_api/Models/Address.cs | 99 ++ rosette_api/Models/EntityType.cs | 16 + rosette_api/Models/IAddress.cs | 10 + rosette_api/{ => Models}/Name.cs | 19 +- .../Models/RecordSimilarityConverter.cs | 60 ++ rosette_api/Models/RecordSimilarityField.cs | 915 ++++++++++++++++++ .../Models/RecordSimilarityFieldInfo.cs | 107 ++ rosette_api/{ => Models}/Response.cs | 2 +- rosette_api/Models/UnfieldedAddress.cs | 39 + tests/EndpointExecutorTests.cs | 1 + tests/TestForValidEndpoint.cs | 85 +- tests/TestNameDeduplication.cs | 1 + tests/TestNameSimilarity.cs | 1 + tests/TestRosetteName.cs | 4 +- tests/TestRosetteResponse.cs | 3 +- 54 files changed, 1631 insertions(+), 29 deletions(-) create mode 100644 examples/AddressSimilarity.cs rename examples/{NameMatch.cs => NameSimilarity.cs} (77%) create mode 100644 examples/RecordSimilarity.cs create mode 100644 examples/SimilarTerms.cs create mode 100644 rosette_api/Endpoints/AddressSimilarity.cs create mode 100644 rosette_api/Endpoints/RecordSimilarity.cs create mode 100644 rosette_api/Endpoints/SimilarTerms.cs create mode 100644 rosette_api/Models/Address.cs create mode 100644 rosette_api/Models/EntityType.cs create mode 100644 rosette_api/Models/IAddress.cs rename rosette_api/{ => Models}/Name.cs (82%) create mode 100644 rosette_api/Models/RecordSimilarityConverter.cs create mode 100644 rosette_api/Models/RecordSimilarityField.cs create mode 100644 rosette_api/Models/RecordSimilarityFieldInfo.cs rename rosette_api/{ => Models}/Response.cs (99%) create mode 100644 rosette_api/Models/UnfieldedAddress.cs diff --git a/examples/AddressSimilarity.cs b/examples/AddressSimilarity.cs new file mode 100644 index 0000000..1b7be29 --- /dev/null +++ b/examples/AddressSimilarity.cs @@ -0,0 +1,65 @@ +using Rosette.Api; +using Rosette.Api.Models; + +namespace examples +{ + class AddressSimilarity + { + /// + /// RunEndpoint runs the example. By default the endpoint will be run against the Rosette Cloud Service. + /// An optional alternate URL may be provided, i.e. for an on-premise solution. + /// + /// Required api key (obtained from Basis Technology) + /// Optional alternate URL + private void RunEndpoint(string apiKey, string? altUrl = null) + { + try + { + ApiClient api = new ApiClient(apiKey); + if (!string.IsNullOrEmpty(altUrl)) + { + api.UseAlternateURL(altUrl); + } + + var add1 = new UnfieldedAddress("160 Pennsilvana Avenue, Washington, D.C., 20500"); + var add2 = new Address() + .SetHouseNumber("1600") + .SetRoad("Pennsylvania Ave. NW") + .SetCity("Washington") + .SetState("D.C.") + .SetPostCode("20500"); + + + Rosette.Api.Endpoints.AddressSimilarity endpoint = new Rosette.Api.Endpoints.AddressSimilarity(add1, add2); + + Response response = endpoint.Call(api); + + //The results of the API call will come back in the form of a Dictionary + foreach (KeyValuePair h in response.Headers) + { + Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); + } + Console.WriteLine(response.ContentAsJson(pretty: true)); + } + catch (Exception e) + { + Console.WriteLine(e.Message); + } + } + /// + /// Main is a simple entrypoint for command line calling of the endpoint examples + /// + /// Command line args, expects API Key, (optional) alt URL + static void Main(string[] args) + { + if (args.Length != 0) + { + new AddressSimilarity().RunEndpoint(args[0], args.Length > 1 ? args[1] : null); + } + else + { + Console.WriteLine("An API Key is required"); + } + } + } +} diff --git a/examples/Categories.cs b/examples/Categories.cs index 463e912..560cad3 100644 --- a/examples/Categories.cs +++ b/examples/Categories.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Categories @@ -21,6 +22,10 @@ private void RunEndpoint(string apiKey, string? altUrl =null) { Response response = endpoint.Call(api); //The results of the API call will come back in the form of a Dictionary + foreach (KeyValuePair h in response.Headers) + { + Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); + } Console.WriteLine(response.ContentAsJson(pretty: true)); } catch (Exception e) { diff --git a/examples/Entities.cs b/examples/Entities.cs index 6314ea2..7237266 100755 --- a/examples/Entities.cs +++ b/examples/Entities.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Entities diff --git a/examples/Events.cs b/examples/Events.cs index 9aaa774..1698578 100644 --- a/examples/Events.cs +++ b/examples/Events.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Events diff --git a/examples/Info.cs b/examples/Info.cs index 3702f6f..3f80fd0 100644 --- a/examples/Info.cs +++ b/examples/Info.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Info diff --git a/examples/Language.cs b/examples/Language.cs index 9c1f0fb..dc6a0bf 100644 --- a/examples/Language.cs +++ b/examples/Language.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Language diff --git a/examples/LanguageMultilingual.cs b/examples/LanguageMultilingual.cs index bbf840b..aa0500c 100644 --- a/examples/LanguageMultilingual.cs +++ b/examples/LanguageMultilingual.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class LanguageMultilingual diff --git a/examples/MorphologyComplete.cs b/examples/MorphologyComplete.cs index 03bba34..ff3b870 100644 --- a/examples/MorphologyComplete.cs +++ b/examples/MorphologyComplete.cs @@ -1,5 +1,6 @@ using Rosette.Api; using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace examples { class MorphologyComplete diff --git a/examples/MorphologyCompoundComponents.cs b/examples/MorphologyCompoundComponents.cs index 9f903a4..8693799 100644 --- a/examples/MorphologyCompoundComponents.cs +++ b/examples/MorphologyCompoundComponents.cs @@ -1,5 +1,6 @@ using Rosette.Api; using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace Rosette.ApiExamples { class MorphologyCompoundComponents diff --git a/examples/MorphologyHanReadings.cs b/examples/MorphologyHanReadings.cs index 4b6403d..d4da2c3 100644 --- a/examples/MorphologyHanReadings.cs +++ b/examples/MorphologyHanReadings.cs @@ -1,5 +1,6 @@ using Rosette.Api; using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace examples { class MorphologyHanReadings diff --git a/examples/MorphologyLemmas.cs b/examples/MorphologyLemmas.cs index 2882b66..fb1c55f 100644 --- a/examples/MorphologyLemmas.cs +++ b/examples/MorphologyLemmas.cs @@ -1,5 +1,6 @@ using Rosette.Api; using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace examples { class MorphologyLemmas @@ -20,6 +21,7 @@ private void RunEndpoint(string apiKey, string? altUrl =null) { //The results of the API call will come back in the form of a Dictionary Morphology endpoint = new Morphology(morphology_lemmas_data, MorphologyFeature.lemmas); Response response = endpoint.Call(api); + foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); } diff --git a/examples/MorphologyPartsOfSpeech.cs b/examples/MorphologyPartsOfSpeech.cs index dec48dc..107b22a 100644 --- a/examples/MorphologyPartsOfSpeech.cs +++ b/examples/MorphologyPartsOfSpeech.cs @@ -1,5 +1,6 @@ using Rosette.Api; using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace examples { class MorphologyPartsOfSpeech diff --git a/examples/NameDeduplication.cs b/examples/NameDeduplication.cs index 6bc256f..c950c0e 100644 --- a/examples/NameDeduplication.cs +++ b/examples/NameDeduplication.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class NameDeduplication diff --git a/examples/NameMatch.cs b/examples/NameSimilarity.cs similarity index 77% rename from examples/NameMatch.cs rename to examples/NameSimilarity.cs index f6ae460..38f6eb1 100644 --- a/examples/NameMatch.cs +++ b/examples/NameSimilarity.cs @@ -1,8 +1,9 @@ using Rosette.Api; using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace examples { - class NameMatch + class NameSimilarity { /// /// RunEndpoint runs the example. By default the endpoint will be run against the Rosette Cloud Service. @@ -16,9 +17,11 @@ private void RunEndpoint(string apiKey, string? altUrl =null) { if (!string.IsNullOrEmpty(altUrl)) { api.UseAlternateURL(altUrl); } - string matched_name_data1 = @"Michael Jackson"; - string matched_name_data2 = @"迈克尔·杰克逊"; - NameSimilarity endpoint = new NameSimilarity(new Name(matched_name_data1), new Name(matched_name_data2)); + + var name1 = new Name("Michael Jackson").SetLanguage("eng").SetEntityType(EntityType.Person); + var name2 = new Name("迈克尔·杰克逊").SetEntityType(EntityType.Person); + + Rosette.Api.Endpoints.NameSimilarity endpoint = new Rosette.Api.Endpoints.NameSimilarity(name1, name2); Response response = endpoint.Call(api); foreach (KeyValuePair h in response.Headers) { Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); @@ -35,7 +38,7 @@ private void RunEndpoint(string apiKey, string? altUrl =null) { /// Command line args, expects API Key, (optional) alt URL static void Main(string[] args) { if (args.Length != 0) { - new NameMatch().RunEndpoint(args[0], args.Length > 1 ? args[1] : null); + new NameSimilarity().RunEndpoint(args[0], args.Length > 1 ? args[1] : null); } else { Console.WriteLine("An API Key is required"); diff --git a/examples/NameTranslation.cs b/examples/NameTranslation.cs index 77fc243..76d7b60 100644 --- a/examples/NameTranslation.cs +++ b/examples/NameTranslation.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class NameTranslation diff --git a/examples/Ping.cs b/examples/Ping.cs index 45fd238..87c528c 100644 --- a/examples/Ping.cs +++ b/examples/Ping.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Ping diff --git a/examples/RecordSimilarity.cs b/examples/RecordSimilarity.cs new file mode 100644 index 0000000..f7ea760 --- /dev/null +++ b/examples/RecordSimilarity.cs @@ -0,0 +1,62 @@ +using Rosette.Api; +using Rosette.Api.Models; + +namespace examples +{ + class RecordSimilarity + { + /// + /// RunEndpoint runs the example. By default the endpoint will be run against the Rosette Cloud Service. + /// An optional alternate URL may be provided, i.e. for an on-premise solution. + /// + /// Required api key (obtained from Basis Technology) + /// Optional alternate URL + private void RunEndpoint(string apiKey, string? altUrl = null) + { + try + { + ApiClient api = new ApiClient(apiKey); + if (!string.IsNullOrEmpty(altUrl)) + { + api.UseAlternateURL(altUrl); + } + + string events_text_data = @"I am looking for flights to Super Bowl 2022 in Inglewood, LA."; + + Rosette.Api.Endpoints.RecordSimilarity endpoint = new Rosette.Api.Endpoints.RecordSimilarity(events_text_data); + Response response = endpoint.Call(api); + + // Print out the response headers + foreach (KeyValuePair h in response.Headers) + { + Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); + } + // Print out the content in JSON format. The Content property returns an IDictionary. + Console.WriteLine(response.ContentAsJson(pretty: true)); + + // Retrieve the Events with full ADM + response = endpoint.SetUrlParameter("output", "rosette").Call(api); + Console.WriteLine(response.ContentAsJson(pretty: true)); + } + catch (Exception e) + { + Console.WriteLine("Exception: " + e.Message); + } + } + /// + /// Main is a simple entrypoint for command line calling of the endpoint examples + /// + /// Command line args, expects API Key, (optional) alt URL + static void Main(string[] args) + { + if (args.Length != 0) + { + new RecordSimilarity().RunEndpoint(args[0], args.Length > 1 ? args[1] : null); + } + else + { + Console.WriteLine("An API Key is required"); + } + } + } +} \ No newline at end of file diff --git a/examples/Relationships.cs b/examples/Relationships.cs index 7d73973..9717b43 100644 --- a/examples/Relationships.cs +++ b/examples/Relationships.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Relationships diff --git a/examples/SemanticsVector.cs b/examples/SemanticsVector.cs index f56377a..e331885 100644 --- a/examples/SemanticsVector.cs +++ b/examples/SemanticsVector.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { diff --git a/examples/Sentences.cs b/examples/Sentences.cs index dae27b4..9fa41d0 100644 --- a/examples/Sentences.cs +++ b/examples/Sentences.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Sentences diff --git a/examples/Sentiment.cs b/examples/Sentiment.cs index f416379..67a4293 100644 --- a/examples/Sentiment.cs +++ b/examples/Sentiment.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Sentiment diff --git a/examples/SimilarTerms.cs b/examples/SimilarTerms.cs new file mode 100644 index 0000000..3a3ae6d --- /dev/null +++ b/examples/SimilarTerms.cs @@ -0,0 +1,64 @@ +using Rosette.Api; +using Rosette.Api.Models; + +namespace examples +{ + class SimilarTerms + { + /// + /// RunEndpoint runs the example. By default the endpoint will be run against the Rosette Cloud Service. + /// An optional alternate URL may be provided, i.e. for an on-premise solution. + /// + /// Required api key (obtained from Basis Technology) + /// Optional alternate URL + private void RunEndpoint(string apiKey, string? altUrl = null) + { + try + { + ApiClient api = new ApiClient(apiKey); + if (!string.IsNullOrEmpty(altUrl)) + { + api.UseAlternateURL(altUrl); + } + + var similar_terms_data = "spy"; + var resultLanguages = new List() { "spa", "deu", "jpn" }; + + Rosette.Api.Endpoints.SimilarTerms endpoint = new Rosette.Api.Endpoints.SimilarTerms(similar_terms_data); + endpoint.SetOption("resultLanguages", resultLanguages); + Response response = endpoint.Call(api); + + // Print out the response headers + foreach (KeyValuePair h in response.Headers) + { + Console.WriteLine(string.Format("{0}:{1}", h.Key, h.Value)); + } + // Print out the content in JSON format. The Content property returns an IDictionary. + Console.WriteLine(response.ContentAsJson(pretty: true)); + + // Retrieve the Events with full ADM + response = endpoint.SetUrlParameter("output", "rosette").Call(api); + Console.WriteLine(response.ContentAsJson(pretty: true)); + } + catch (Exception e) + { + Console.WriteLine("Exception: " + e.Message); + } + } + /// + /// Main is a simple entrypoint for command line calling of the endpoint examples + /// + /// Command line args, expects API Key, (optional) alt URL + static void Main(string[] args) + { + if (args.Length != 0) + { + new SimilarTerms().RunEndpoint(args[0], args.Length > 1 ? args[1] : null); + } + else + { + Console.WriteLine("An API Key is required"); + } + } + } +} \ No newline at end of file diff --git a/examples/SyntaxDependencies.cs b/examples/SyntaxDependencies.cs index dcecf61..2c1c024 100644 --- a/examples/SyntaxDependencies.cs +++ b/examples/SyntaxDependencies.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class SyntaxDependencies diff --git a/examples/TextEmbedding.cs b/examples/TextEmbedding.cs index 38151cf..a44b2b7 100644 --- a/examples/TextEmbedding.cs +++ b/examples/TextEmbedding.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class TextEmbedding diff --git a/examples/Tokens.cs b/examples/Tokens.cs index 63dcc94..d4c9336 100644 --- a/examples/Tokens.cs +++ b/examples/Tokens.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Tokens diff --git a/examples/Topics.cs b/examples/Topics.cs index cf0a01f..bc0e003 100644 --- a/examples/Topics.cs +++ b/examples/Topics.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Topics diff --git a/examples/Transliteration.cs b/examples/Transliteration.cs index 055eccc..dc484c1 100644 --- a/examples/Transliteration.cs +++ b/examples/Transliteration.cs @@ -1,4 +1,5 @@ using Rosette.Api; +using Rosette.Api.Models; namespace examples { class Transliteration diff --git a/rosette_api/Endpoints/AddressSimilarity.cs b/rosette_api/Endpoints/AddressSimilarity.cs new file mode 100644 index 0000000..bfefc77 --- /dev/null +++ b/rosette_api/Endpoints/AddressSimilarity.cs @@ -0,0 +1,26 @@ +using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; + +namespace Rosette.Api.Endpoints +{ + public class AddressSimilarity : EndpointBase + { + /// + /// AddressSimilarityEndpoint checks the similarity between two RosetteAddress objects + /// + /// RosetteAddress object + /// RosetteAddress object + public AddressSimilarity(IAddress? address1, IAddress? address2) : base("address-similarity") + { + ArgumentNullException.ThrowIfNull(address1); + Params["address1"] = address1; + ArgumentNullException.ThrowIfNull(address2); + Params["address2"] = address2; + } + + public Response Call(ApiClient api) + { + return Funcs.PostCall(api); + } + } +} \ No newline at end of file diff --git a/rosette_api/Endpoints/Core/ContentEndpointBase.cs b/rosette_api/Endpoints/Core/ContentEndpointBase.cs index 7fd6d43..16c53ab 100644 --- a/rosette_api/Endpoints/Core/ContentEndpointBase.cs +++ b/rosette_api/Endpoints/Core/ContentEndpointBase.cs @@ -1,3 +1,5 @@ +using Rosette.Api.Models; + namespace Rosette.Api.Endpoints.Core; /// diff --git a/rosette_api/Endpoints/Core/EndpointExecutor.cs b/rosette_api/Endpoints/Core/EndpointExecutor.cs index 550be2a..ba8f6b5 100644 --- a/rosette_api/Endpoints/Core/EndpointExecutor.cs +++ b/rosette_api/Endpoints/Core/EndpointExecutor.cs @@ -1,4 +1,5 @@ -using System.Collections.Specialized; +using Rosette.Api.Models; +using System.Collections.Specialized; using System.Text; using System.Text.Json; diff --git a/rosette_api/Endpoints/Info.cs b/rosette_api/Endpoints/Info.cs index a6e3a9c..eec493e 100644 --- a/rosette_api/Endpoints/Info.cs +++ b/rosette_api/Endpoints/Info.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Endpoints { diff --git a/rosette_api/Endpoints/Morphology.cs b/rosette_api/Endpoints/Morphology.cs index 3bf1251..ba98f80 100644 --- a/rosette_api/Endpoints/Morphology.cs +++ b/rosette_api/Endpoints/Morphology.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Endpoints { diff --git a/rosette_api/Endpoints/NameDeduplication.cs b/rosette_api/Endpoints/NameDeduplication.cs index 13a84fa..5f96a45 100644 --- a/rosette_api/Endpoints/NameDeduplication.cs +++ b/rosette_api/Endpoints/NameDeduplication.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Endpoints { diff --git a/rosette_api/Endpoints/NameSimilarity.cs b/rosette_api/Endpoints/NameSimilarity.cs index 588217a..062c677 100644 --- a/rosette_api/Endpoints/NameSimilarity.cs +++ b/rosette_api/Endpoints/NameSimilarity.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Endpoints { diff --git a/rosette_api/Endpoints/NameTranslation.cs b/rosette_api/Endpoints/NameTranslation.cs index e0d3855..6586193 100644 --- a/rosette_api/Endpoints/NameTranslation.cs +++ b/rosette_api/Endpoints/NameTranslation.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Endpoints { diff --git a/rosette_api/Endpoints/Ping.cs b/rosette_api/Endpoints/Ping.cs index d3209ce..52858af 100644 --- a/rosette_api/Endpoints/Ping.cs +++ b/rosette_api/Endpoints/Ping.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Endpoints { diff --git a/rosette_api/Endpoints/RecordSimilarity.cs b/rosette_api/Endpoints/RecordSimilarity.cs new file mode 100644 index 0000000..5434eef --- /dev/null +++ b/rosette_api/Endpoints/RecordSimilarity.cs @@ -0,0 +1,14 @@ +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class RecordSimilarity : ContentEndpointBase +{ + /// + /// RecordSimilarity returns the similarity score between records + /// + /// text, Uri object or FileStream + public RecordSimilarity(object content) : base("record-similarity", content) + { + } +} \ No newline at end of file diff --git a/rosette_api/Endpoints/SimilarTerms.cs b/rosette_api/Endpoints/SimilarTerms.cs new file mode 100644 index 0000000..a320e8e --- /dev/null +++ b/rosette_api/Endpoints/SimilarTerms.cs @@ -0,0 +1,14 @@ +using Rosette.Api.Endpoints.Core; + +namespace Rosette.Api.Endpoints; + +public class SimilarTerms : ContentEndpointBase +{ + /// + /// SimilarTerms returns terms that are similar to the input + /// + /// text, Uri object or FileStream + public SimilarTerms(object content) : base("semantics/similar", content) + { + } +} \ No newline at end of file diff --git a/rosette_api/Endpoints/TextEmbedding.cs b/rosette_api/Endpoints/TextEmbedding.cs index d1233ce..3e785e6 100644 --- a/rosette_api/Endpoints/TextEmbedding.cs +++ b/rosette_api/Endpoints/TextEmbedding.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Endpoints { diff --git a/rosette_api/Models/Address.cs b/rosette_api/Models/Address.cs new file mode 100644 index 0000000..963b454 --- /dev/null +++ b/rosette_api/Models/Address.cs @@ -0,0 +1,99 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +public class Address : IAddress +{ + [JsonPropertyName("houseNumber")] + public string? HouseNumber { get; private set; } + + [JsonPropertyName("road")] + public string? Road { get; private set; } + + [JsonPropertyName("city")] + public string? City { get; private set; } + + [JsonPropertyName("state")] + public string? State { get; private set; } + + [JsonPropertyName("postCode")] + public string? PostCode { get; private set; } + + /// + /// Constructor for a Address object, used by several endpoints + /// + /// optional house number + /// optional road + /// optional city + /// optional state + /// optional post code + [JsonConstructor] + public Address(string? houseNumber = null, string? road = null, string? city = null, string? state = null, string? postCode = null) + { + HouseNumber = houseNumber; + Road = road; + City = city; + State = state; + PostCode = postCode; + } + + /// + /// SetHouseNumber sets the house number + /// + /// house number + /// Updated Address instance + public Address SetHouseNumber(string? houseNumber) + { + HouseNumber = houseNumber; + return this; + } + + /// + /// SetRoad sets the road + /// + /// road + /// Updated Address instance + public Address SetRoad(string? road) + { + Road = road; + return this; + } + + /// + /// SetCity sets the city + /// + /// city + /// Updated Address instance + public Address SetCity(string? city) + { + City = city; + return this; + } + + /// + /// SetState sets the state + /// + /// state + /// Updated Address instance + public Address SetState(string? state) + { + State = state; + return this; + } + + /// + /// SetPostCode sets the post code + /// + /// post code + /// Updated Address instance + public Address SetPostCode(string? postCode) + { + PostCode = postCode; + return this; + } + + public bool Fielded() + { + return true; + } +} \ No newline at end of file diff --git a/rosette_api/Models/EntityType.cs b/rosette_api/Models/EntityType.cs new file mode 100644 index 0000000..6c95126 --- /dev/null +++ b/rosette_api/Models/EntityType.cs @@ -0,0 +1,16 @@ +namespace Rosette.Api.Models; + +/// +/// Represents the type of entity for name processing +/// +public enum EntityType +{ + /// Person entity type + Person, + + /// Location entity type + Location, + + /// Organization entity type + Organization +} \ No newline at end of file diff --git a/rosette_api/Models/IAddress.cs b/rosette_api/Models/IAddress.cs new file mode 100644 index 0000000..c3b96ed --- /dev/null +++ b/rosette_api/Models/IAddress.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Rosette.Api.Models; + +public interface IAddress +{ + bool Fielded(); +} diff --git a/rosette_api/Name.cs b/rosette_api/Models/Name.cs similarity index 82% rename from rosette_api/Name.cs rename to rosette_api/Models/Name.cs index 9614ccb..6882901 100644 --- a/rosette_api/Name.cs +++ b/rosette_api/Models/Name.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Rosette.Api; +namespace Rosette.Api.Models; public class Name { @@ -33,12 +33,23 @@ public Name(string text, string? entityType = null, string? language = null, str Script = script; } + /// + /// SetEntityType sets the entity type using the EntityType enum + /// + /// entity type + /// updated Name object + public Name SetEntityType(EntityType type) + { + EntityType = type.ToString().ToUpperInvariant(); + return this; + } + /// /// SetEntityType sets the optional entity type. PERSON, LOCATION and ORGANIZATION /// are currently supported. /// /// entity type, PERSON, LOCATION or ORGANIZATION - /// updated RosetteName object + /// updated Name object public Name SetEntityType(string type) { ArgumentException.ThrowIfNullOrWhiteSpace(type); @@ -59,7 +70,7 @@ public Name SetEntityType(string type) /// SetLanguage sets the optional ISO-639-3 language code for the name's language /// /// ISO-639-3 language code - /// updated RosetteName object + /// updated Name object public Name SetLanguage(string language) { ArgumentException.ThrowIfNullOrWhiteSpace(language); @@ -71,7 +82,7 @@ public Name SetLanguage(string language) /// SetScript sets the ISO-15924 code for the name's script /// /// ISO-15924 script code - /// updated RosetteName object + /// updated Name object public Name SetScript(string script) { ArgumentException.ThrowIfNullOrWhiteSpace(script); diff --git a/rosette_api/Models/RecordSimilarityConverter.cs b/rosette_api/Models/RecordSimilarityConverter.cs new file mode 100644 index 0000000..f972a40 --- /dev/null +++ b/rosette_api/Models/RecordSimilarityConverter.cs @@ -0,0 +1,60 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// JsonConverter for Unfielded Record Similarity objects +/// +public class UnfieldedRecordSimilarityConverter : JsonConverter +{ + /// + /// Initializes a new instance of the UnfieldedRecordSimilarityConverter class + /// + public UnfieldedRecordSimilarityConverter() + { + // Default constructor for JSON serialization + } + + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(UnknownFieldRecord) || + typeToConvert == typeof(NumberRecord) || + typeToConvert == typeof(BooleanRecord) || + typeToConvert == typeof(object); + } + + public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.Number => new NumberRecord { Number = reader.GetDouble() }, + JsonTokenType.True or JsonTokenType.False => new BooleanRecord { Boolean = reader.GetBoolean() }, + JsonTokenType.String => reader.GetString(), + JsonTokenType.StartObject => JsonSerializer.Deserialize(ref reader, options), + _ => null + }; + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + switch (value) + { + case UnknownFieldRecord unknownField: + JsonSerializer.Serialize(writer, unknownField.Data, options); + break; + case NumberRecord numberRecord: + writer.WriteNumberValue(numberRecord.Number); + break; + case BooleanRecord booleanRecord: + writer.WriteBooleanValue(booleanRecord.Boolean); + break; + case string stringValue: + writer.WriteStringValue(stringValue); + break; + default: + writer.WriteStringValue(value?.ToString() ?? string.Empty); + break; + } + } +} \ No newline at end of file diff --git a/rosette_api/Models/RecordSimilarityField.cs b/rosette_api/Models/RecordSimilarityField.cs new file mode 100644 index 0000000..e80fe19 --- /dev/null +++ b/rosette_api/Models/RecordSimilarityField.cs @@ -0,0 +1,915 @@ +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models +{ + /// + /// Parent Interface for RecordSimilarityField objects + /// + public interface RecordSimilarityField + { + + } + + /// + /// Abstract parent class for UnfieldedName and FieldedName + /// + public abstract class NameField : RecordSimilarityField + { + public const string TEXT = "text"; + + /// + /// Gets or sets the the name field's text + /// + [JsonPropertyName("text")] + public string Text { get; set; } + } + + /// + /// Class for representing an unfielded name + /// + [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] + public class UnfieldedNameRecord : NameField + { + + /// + /// No-args constructor + /// + public UnfieldedNameRecord() { } + + /// + /// Full constructor + /// + /// The name as a string + public UnfieldedNameRecord(string text) + { + this.Text = text; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is UnfieldedNameRecord) + { + UnfieldedNameRecord other = obj as UnfieldedNameRecord; + return this.Text == other.Text; + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + return this.Text != null ? this.Text.GetHashCode() : 1; + } + + /// + /// ToString override. Also used for JSON serialization + /// + public override string ToString() + { + return this.Text; + } + } + + /// + /// Class for representing a fielded name + /// + public class FieldedNameRecord : NameField + { + + /// language + /// + /// Getter, Setter for the language + /// language: Language: ISO 639-3 code + /// + /// + [JsonPropertyName("language")] + public string Language { get; set; } + + /// language + /// + /// Getter, Setter for the languageOfOrigin + /// languageOfOrigin: Language: ISO 639-3 code + /// + /// + [JsonPropertyName("languageOfOrigin")] + public string LanguageOfOrigin { get; set; } + + /// script + /// + /// Getter, Setter for the script + /// script: ISO 15924 code for the name's script + /// + /// + [JsonPropertyName("script")] + public string Script { get; set; } + + /// entityType + /// + /// Getter, Setter for the entityType + /// entityType: Entity type of the name: PERSON, LOCATION, or ORGANIZATION + /// + /// + [JsonPropertyName("entityType")] + public string EntityType { get; set; } + + /// + /// No-args constructor + /// + public FieldedNameRecord() { } + + /// + /// Full constructor + /// + /// (string): Text describing the name + /// (string, optional): Language: ISO 639-3 code (ignored for the /language endpoint) + /// (string, optional): Language the name originates from: ISO 639-3 code (ignored for the /language endpoint) + /// (string, optional): ISO 15924 code for the name's script + /// (string, optional): Entity type of the name: PERSON, LOCATION, or ORGANIZATION + public FieldedNameRecord(string text, string language = null, string languageOfOrigin = null, string script = null, string entityType = null) + { + this.Text = text; + this.Language = language; + this.LanguageOfOrigin = languageOfOrigin; + this.Script = script; + this.EntityType = entityType; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is FieldedNameRecord) + { + FieldedNameRecord other = obj as FieldedNameRecord; + List conditions = new List() { + this.Text == other.Text, + this.Language == other.Language, + this.LanguageOfOrigin == other.LanguageOfOrigin, + this.Script == other.Script, + this.EntityType == other.EntityType + }; + return conditions.All(condition => condition); + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + int h0 = this.Text != null ? this.Text.GetHashCode() : 1; + int h1 = this.Language != null ? this.Language.GetHashCode() : 1; + int h2 = this.LanguageOfOrigin != null ? this.LanguageOfOrigin.GetHashCode() : 1; + int h3 = this.Script != null ? this.Script.GetHashCode() : 1; + int h4 = this.EntityType != null ? this.EntityType.GetHashCode() : 1; + return h0 ^ h1 ^ h2 ^ h3 ^ h4; + } + + /// + /// ToString override. + /// + /// This fielded name in JSON form + public override string ToString() + { + return JsonSerializer.Serialize(this); + } + } + + /// + /// Abstract parent class for UnfieldedDate and FieldedDate + /// + public abstract class DateField : RecordSimilarityField + { + /// + /// Gets or sets the the date field's date + /// + [JsonPropertyName("date")] + public string Date { get; set; } + } + + /// + /// Class for representing an unfielded date + /// + [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] + public class UnfieldedDateRecord : DateField + { + + /// + /// No-args constructor + /// + public UnfieldedDateRecord() { } + /// + /// Full constructor + /// + /// The date in string form + public UnfieldedDateRecord(string date) + { + this.Date = date; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is UnfieldedDateRecord) + { + UnfieldedDateRecord other = obj as UnfieldedDateRecord; + return this.Date == other.Date; + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + return this.Date != null ? this.Date.GetHashCode() : 1; + } + + /// + /// ToString override. Also used for JSON serialization + /// + public override string ToString() + { + return this.Date; + } + } + + /// + /// Class for representing a fielded date + /// + public class FieldedDateRecord : DateField + { + + /// + /// Gets or sets the the date field's format + /// + [JsonPropertyName("format")] + public string Format { get; set; } + + /// + /// No-args constructor + /// + public FieldedDateRecord() { } + + /// + /// Full constructor + /// + /// The date in string format + /// The date's format. Rules are defined at https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html + public FieldedDateRecord(string date, string format) + { + this.Date = date; + this.Format = format; + } + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is FieldedDateRecord) + { + FieldedDateRecord other = obj as FieldedDateRecord; + List conditions = new List() { + this.Date == other.Date, + this.Format == other.Format + }; + return conditions.All(condition => condition); + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + int h0 = this.Date != null ? this.Date.GetHashCode() : 1; + int h1 = this.Format != null ? this.Format.GetHashCode() : 1; + return h0 ^ h1; + } + + /// + /// ToString override. + /// + /// This fielded date in JSON form + public override string ToString() + { + return JsonSerializer.Serialize(this); + } + } + + /// + /// Abstract parent class for UnfieldedAddress and FieldedAddress + /// + public abstract class AddressField : RecordSimilarityField + { + + } + + /// + /// Class for representing an unfielded address + /// + [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] + public class UnfieldedAddressRecord : AddressField + { + + /// + /// Gets or sets the the address field's address + /// + [JsonPropertyName("address")] + public string Address { get; set; } + + /// + /// No-args constructor + /// + public UnfieldedAddressRecord() { } + /// + /// Full constructor + /// + /// The adress in string form + public UnfieldedAddressRecord(string address) + { + this.Address = address; + } + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is UnfieldedAddressRecord) + { + UnfieldedAddressRecord other = obj as UnfieldedAddressRecord; + return this.Address == other.Address; + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + return this.Address != null ? this.Address.GetHashCode() : 1; + } + + /// + /// ToString override. Also used for JSON serialization + /// + public override string ToString() + { + return this.Address; + } + } + + /// + /// Class for representing a fielded address + /// + public class FieldedAddressRecord : AddressField + { + + /// + /// Gets or sets the the address field's house + /// + [JsonPropertyName("house")] + public string House { get; set; } + + /// + /// Gets or sets the the address field's house number + /// + [JsonPropertyName("house_number")] + public string HouseNumber { get; set; } + + /// + /// Gets or sets the the address field's road + /// + [JsonPropertyName("road")] + public string Road { get; set; } + + /// + /// Gets or sets the the address field's unit + /// + [JsonPropertyName("unit")] + public string Unit { get; set; } + + /// + /// Gets or sets the the address field's level + /// + [JsonPropertyName("level")] + public string Level { get; set; } + + /// + /// Gets or sets the the address field's staircase + /// + [JsonPropertyName("staircase")] + public string Staircase { get; set; } + + /// + /// Gets or sets the the address field's entrance + /// + [JsonPropertyName("entrance")] + public string Entrance { get; set; } + + /// + /// Gets or sets the the address field's suburb + /// + [JsonPropertyName("suburb")] + public string Suburb { get; set; } + + /// + /// Gets or sets the the address field's city district + /// + [JsonPropertyName("city_district")] + public string CityDistrict { get; set; } + + /// + /// Gets or sets the the address field's city + /// + [JsonPropertyName("city")] + public string City { get; set; } + + /// + /// Gets or sets the the address field's island + /// + [JsonPropertyName("island")] + public string Island { get; set; } + + /// + /// Gets or sets the the address field's state district + /// + [JsonPropertyName("state_district")] + public string StateDistrict { get; set; } + + /// + /// Gets or sets the the address field's state + /// + [JsonPropertyName("state")] + public string State { get; set; } + + /// + /// Gets or sets the the address field's country region + /// + [JsonPropertyName("country_region")] + public string CountryRegion { get; set; } + + /// + /// Gets or sets the the address field's country + /// + [JsonPropertyName("country")] + public string Country { get; set; } + + /// + /// Gets or sets the the address field's world region + /// + [JsonPropertyName("world_region")] + public string WorldRegion { get; set; } + + /// + /// Gets or sets the the address field's postcode + /// + [JsonPropertyName("postcode")] + public string Postcode { get; set; } + + /// + /// Gets or sets the the address field's po box + /// + [JsonPropertyName("po_box")] + public string PoBox { get; set; } + + + + /// + /// No-args constructor + /// + public FieldedAddressRecord() { } + /// + /// Full constructor + /// + /// The house + /// The house number + /// The road + /// The unit + /// The level + /// The staircase + /// The entrance + /// The suburb + /// The city district + /// The city + /// The island + /// The state district + /// The state + /// The country region + /// The country + /// The world region + /// The postcode + /// The po box + public FieldedAddressRecord( + string house, + string houseNumber, + string road, + string unit, + string level, + string staircase, + string entrance, + string suburb, + string cityDistrict, + string city, + string island, + string stateDistrict, + string state, + string countryRegion, + string country, + string worldRegion, + string postcode, + string poBox + ) + { + this.House = house; + this.HouseNumber = houseNumber; + this.Road = road; + this.Unit = unit; + this.Level = level; + this.Staircase = staircase; + this.Entrance = entrance; + this.Suburb = suburb; + this.CityDistrict = cityDistrict; + this.City = city; + this.Island = island; + this.StateDistrict = stateDistrict; + this.State = state; + this.CountryRegion = countryRegion; + this.Country = country; + this.WorldRegion = worldRegion; + this.Postcode = postcode; + this.PoBox = poBox; + } + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is FieldedAddressRecord) + { + FieldedAddressRecord other = obj as FieldedAddressRecord; + List conditions = new List() { + this.House == other.House, + this.HouseNumber == other.HouseNumber, + this.Road == other.Road, + this.Unit == other.Unit, + this.Level == other.Level, + this.Staircase == other.Staircase, + this.Entrance == other.Entrance, + this.Suburb == other.Suburb, + this.CityDistrict == other.CityDistrict, + this.City == other.City, + this.Island == other.Island, + this.StateDistrict == other.StateDistrict, + this.State == other.State, + this.CountryRegion == other.CountryRegion, + this.Country == other.Country, + this.WorldRegion == other.WorldRegion, + this.Postcode == other.Postcode, + this.PoBox == other.PoBox + }; + return conditions.All(condition => condition); + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + int h0 = this.House != null ? this.House.GetHashCode() : 1; + int h1 = this.HouseNumber != null ? this.HouseNumber.GetHashCode() : 1; + int h2 = this.Road != null ? this.Road.GetHashCode() : 1; + int h3 = this.Unit != null ? this.Unit.GetHashCode() : 1; + int h4 = this.Level != null ? this.Level.GetHashCode() : 1; + int h5 = this.Staircase != null ? this.Staircase.GetHashCode() : 1; + int h6 = this.Entrance != null ? this.Entrance.GetHashCode() : 1; + int h7 = this.Suburb != null ? this.Suburb.GetHashCode() : 1; + int h8 = this.CityDistrict != null ? this.CityDistrict.GetHashCode() : 1; + int h9 = this.City != null ? this.City.GetHashCode() : 1; + int h10 = this.Island != null ? this.Island.GetHashCode() : 1; + int h11 = this.StateDistrict != null ? this.StateDistrict.GetHashCode() : 1; + int h12 = this.State != null ? this.State.GetHashCode() : 1; + int h13 = this.CountryRegion != null ? this.CountryRegion.GetHashCode() : 1; + int h14 = this.Country != null ? this.Country.GetHashCode() : 1; + int h15 = this.WorldRegion != null ? this.WorldRegion.GetHashCode() : 1; + int h16 = this.Postcode != null ? this.Postcode.GetHashCode() : 1; + int h17 = this.PoBox != null ? this.PoBox.GetHashCode() : 1; + return h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ h7 ^ h8 ^ h9 ^ h10 ^ h11 ^ h12 ^ h13 ^ h14 ^ h15 ^ h16 ^ h17; + } + + /// + /// ToString override. + /// + /// This fielded address in JSON form + public override string ToString() + { + return JsonSerializer.Serialize(this); + } + } + + /// + /// Class for representing a string record + /// + [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] + public class StringRecord : RecordSimilarityField + { + + /// + /// Gets and sets the string record + /// + [JsonPropertyName("data")] + public string Text { get; set;} + + /// + /// No-args constructor + /// + public StringRecord() { } + + /// + /// Constructor + /// + /// The string record + public StringRecord(string data) + { + this.Text = data; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is StringRecord) + { + StringRecord other = obj as StringRecord; + return this.Text == other.Text; + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + return this.Text != null ? this.Text.GetHashCode() : 1; + } + + /// + /// ToString override. + /// + /// This string record as a string + public override string ToString() + { + return this.Text; + } + } + + /// + /// Class for representing a number record + /// + [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] + public class NumberRecord : RecordSimilarityField + { + public const string DATA = "data"; + + /// + /// Gets and sets the number record + /// + [JsonPropertyName("data")] + public double Number { get; set; } + + /// + /// No-args constructor + /// + public NumberRecord() { } + + /// + /// Constructor + /// + /// The number record + public NumberRecord(double data) + { + this.Number = data; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is NumberRecord) + { + NumberRecord other = obj as NumberRecord; + return this.Number == other.Number; + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + return this.Number.GetHashCode(); + } + + /// + /// ToString override. + /// + /// This number record as a string + public override string ToString() + { + return this.Number.ToString(); + } + } + + /// + /// Class for representing a boolean record + /// + [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] + public class BooleanRecord : RecordSimilarityField + { + /// + /// Gets and sets the boolean record + /// + [JsonPropertyName("data")] + public bool Boolean { get; set; } + + /// + /// No-args constructor + /// + public BooleanRecord() { } + + /// + /// Constructor + /// + /// The boolean record + public BooleanRecord(bool data) + { + this.Boolean = data; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is BooleanRecord) + { + BooleanRecord other = obj as BooleanRecord; + return this.Boolean == other.Boolean; + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + return this.Boolean.GetHashCode(); + } + + /// + /// ToString override. + /// + /// This boolean record as a string + public override string ToString() + { + return this.Boolean.ToString().ToLower(); + } + } + + /// + /// Class for representing an unknown field + /// + + [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] + public class UnknownFieldRecord : RecordSimilarityField + { + /// + /// Gets or the unknown field's data + /// + [JsonPropertyName("data")] + public JsonNode Data { get; } + + /// + /// Constructor + /// + /// The data as a JsonNode + public UnknownFieldRecord(JsonNode data) + { + this.Data = data; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is UnknownFieldRecord) + { + UnknownFieldRecord other = obj as UnknownFieldRecord; + return JsonNode.DeepEquals(this.Data, other.Data); + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + return this.Data != null ? this.Data.GetHashCode() : 1; + } + + /// + /// ToString override. + /// + /// This unknown field in JSON form + public override string ToString() + { + return this.Data.ToJsonString(); + } + + } + +} \ No newline at end of file diff --git a/rosette_api/Models/RecordSimilarityFieldInfo.cs b/rosette_api/Models/RecordSimilarityFieldInfo.cs new file mode 100644 index 0000000..d92d0c7 --- /dev/null +++ b/rosette_api/Models/RecordSimilarityFieldInfo.cs @@ -0,0 +1,107 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models { + /// RecordFieldType + /// + /// The possible record types that can be used in the Record Similarity endpoint + /// + /// + public static class RecordFieldType + { + public const string RniName = "rni_name"; + public const string RniDate = "rni_date"; + public const string RniAddress = "rni_address"; + public const string RniString = "rni_string"; + public const string RniNumber = "rni_number"; + public const string RniBoolean = "rni_boolean"; + } + + /// + /// Class for representing record similarity field information + /// + public class RecordSimilarityFieldInfo + { + /// + /// Gets or sets the record's field type + /// + [JsonPropertyName("type")] + public string Type { get; set; } + + /// + /// Gets or sets the record's field weight + /// + [JsonPropertyName("weight")] + public double? Weight { get; set; } + + /// + /// Gets or sets the record's field scoreIfNull + /// + [JsonPropertyName("scoreIfNull")] + public double? ScoreIfNull { get; set; } + + /// + /// No-args constructor + /// + public RecordSimilarityFieldInfo() { } + + /// + /// Full constructor + /// + /// The record's field type + /// The record's field weight + /// The record's field scoreIfNull + public RecordSimilarityFieldInfo(string type, double? weight, double? scoreIfNull) + { + this.Type = type; + this.Weight = weight; + this.ScoreIfNull = scoreIfNull; + } + + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is RecordSimilarityFieldInfo) + { + RecordSimilarityFieldInfo other = obj as RecordSimilarityFieldInfo; + List conditions = new List() { + this.Type == other.Type, + this.Weight == other.Weight, + this.ScoreIfNull == other.ScoreIfNull + }; + return conditions.All(condition => condition); + } + else + { + return false; + } + } + + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + int h0 = this.Type.GetHashCode(); + int h1 = this.Weight != null ? this.Weight.GetHashCode() : 1; + int h2 = this.ScoreIfNull != null ? this.ScoreIfNull.GetHashCode() : 1; + return h0 ^ h1 ^ h2; + } + + /// + /// ToString override. + /// + /// This record similarity field info in JSON form + public override string ToString() + { + return JsonSerializer.Serialize(this); + } + } +} \ No newline at end of file diff --git a/rosette_api/Response.cs b/rosette_api/Models/Response.cs similarity index 99% rename from rosette_api/Response.cs rename to rosette_api/Models/Response.cs index cb6d228..5a6a854 100644 --- a/rosette_api/Response.cs +++ b/rosette_api/Models/Response.cs @@ -2,7 +2,7 @@ using System.Text; using System.Text.Json; -namespace Rosette.Api; +namespace Rosette.Api.Models; public class Response { diff --git a/rosette_api/Models/UnfieldedAddress.cs b/rosette_api/Models/UnfieldedAddress.cs new file mode 100644 index 0000000..d2bb2d9 --- /dev/null +++ b/rosette_api/Models/UnfieldedAddress.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +public class UnfieldedAddress : IAddress +{ + [JsonPropertyName("address")] + public string? Address { get; private set; } + + /// + /// Constructor for a Address object, used by several endpoints + /// + /// optional house number + /// optional road + /// optional city + /// optional state + /// optional post code + [JsonConstructor] + public UnfieldedAddress(string? address = null) + { + Address = address; + } + + /// + /// SetAddress sets the address + /// + /// address + /// Updated Address instance + public UnfieldedAddress SetAddress(string? address) + { + Address = address; + return this; + } + + public bool Fielded() + { + return false; + } +} \ No newline at end of file diff --git a/tests/EndpointExecutorTests.cs b/tests/EndpointExecutorTests.cs index 98a6342..466c506 100644 --- a/tests/EndpointExecutorTests.cs +++ b/tests/EndpointExecutorTests.cs @@ -3,6 +3,7 @@ using System.Net; using System.Text.Json; using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Tests { diff --git a/tests/TestForValidEndpoint.cs b/tests/TestForValidEndpoint.cs index 56dc394..4438d06 100755 --- a/tests/TestForValidEndpoint.cs +++ b/tests/TestForValidEndpoint.cs @@ -1,18 +1,32 @@ using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace Rosette.Api.Tests { public class TestForValidEndpoint { + + [Fact] + public void AddressSimilarityEndpoint() + { + Address a = new Address("foo"); + + AddressSimilarity asim = new AddressSimilarity(a,a); + Assert.Equal("address-similarity", asim.Endpoint); + } + [Fact] - public void CategoriesEndpoint() { + public void CategoriesEndpoint() + { Categories c = new Categories("foo"); Assert.Equal("categories", c.Endpoint); Assert.Equal("foo", c.Content); } + [Fact] - public void EntitiesEndpoint() { + public void EntitiesEndpoint() + { Entities e = new Entities("foo"); Assert.Equal("entities", e.Endpoint); Assert.Equal("foo", e.Content); @@ -27,13 +41,15 @@ public void EventsEndpoint() } [Fact] - public void InfoEndpoint() { + public void InfoEndpoint() + { Info i = new Info(); Assert.Equal("info", i.Endpoint); } [Fact] - public void LanguageEndpoint() { + public void LanguageEndpoint() + { Language l = new Language("foo"); Assert.Equal("language", l.Endpoint); @@ -46,7 +62,8 @@ public void LanguageEndpoint() { [InlineData(MorphologyFeature.hanReadings)] [InlineData(MorphologyFeature.lemmas)] [InlineData(MorphologyFeature.partsOfSpeech)] - public void MorphologyEndpoint(MorphologyFeature feature) { + public void MorphologyEndpoint(MorphologyFeature feature) + { Morphology m = new Morphology("foo", feature); Assert.Equal("morphology/" + m.FeatureAsString(feature), m.Endpoint); @@ -54,20 +71,40 @@ public void MorphologyEndpoint(MorphologyFeature feature) { } [Fact] - public void NameSimilarityEndpoint() { + public void NameSimilarityEndpoint() + { Name rn = new Name("foo"); NameSimilarity ns = new NameSimilarity(rn, rn); Assert.Equal("name-similarity", ns.Endpoint); } [Fact] - public void PingEndpoint() { + public void NameTranslationEndpoint() + { + NameTranslation nt = new NameTranslation("foo"); + + Assert.Equal("name-translation", nt.Endpoint); + } + + [Fact] + public void PingEndpoint() + { Ping p = new Ping(); Assert.Equal("ping", p.Endpoint); } [Fact] - public void RelationshipsEndpoint() { + public void RecordSimilarityEndpoint() + { + RecordSimilarity rs = new RecordSimilarity("foo"); + + Assert.Equal("record-similarity", rs.Endpoint); + Assert.Equal("foo", rs.Content); + } + + [Fact] + public void RelationshipsEndpoint() + { Relationships r = new Relationships("foo"); Assert.Equal("relationships", r.Endpoint); @@ -83,21 +120,33 @@ public void SemanticVectorsEndpoint() } [Fact] - public void SentencesEndpoint() { + public void SentencesEndpoint() + { Sentences s = new Sentences("foo"); Assert.Equal("sentences", s.Endpoint); Assert.Equal("foo", s.Content); } [Fact] - public void SentimentEndpoint() { + public void SentimentEndpoint() + { Sentiment s = new Sentiment("foo"); Assert.Equal("sentiment", s.Endpoint); Assert.Equal("foo", s.Content); } [Fact] - public void SyntaxDependenciesEndpoint() { + public void SimilarTermsEndpoint() + { + SimilarTerms st = new SimilarTerms("foo"); + + Assert.Equal("semantics/similar", st.Endpoint); + Assert.Equal("foo", st.Content); + } + + [Fact] + public void SyntaxDependenciesEndpoint() + { SyntaxDependencies s = new SyntaxDependencies("foo"); Assert.Equal("syntax/dependencies", s.Endpoint); @@ -105,7 +154,8 @@ public void SyntaxDependenciesEndpoint() { } [Fact] - public void TextEmbeddingEndpoint() { + public void TextEmbeddingEndpoint() + { TextEmbedding t = new TextEmbedding("foo"); Assert.Equal("text-embedding", t.Endpoint); @@ -113,7 +163,8 @@ public void TextEmbeddingEndpoint() { } [Fact] - public void TokensEndpoint() { + public void TokensEndpoint() + { Tokens t = new Tokens("foo"); Assert.Equal("tokens", t.Endpoint); @@ -121,7 +172,8 @@ public void TokensEndpoint() { } [Fact] - public void TopicsEndpoint() { + public void TopicsEndpoint() + { Topics t = new Topics("foo"); Assert.Equal("topics", t.Endpoint); @@ -129,11 +181,12 @@ public void TopicsEndpoint() { } [Fact] - public void TransliterationEndpoint() { + public void TransliterationEndpoint() + { Transliteration t = new Transliteration("foo"); Assert.Equal("transliteration", t.Endpoint); Assert.Equal("foo", t.Content); } } -} +} \ No newline at end of file diff --git a/tests/TestNameDeduplication.cs b/tests/TestNameDeduplication.cs index 2dfc5f3..ff84eff 100755 --- a/tests/TestNameDeduplication.cs +++ b/tests/TestNameDeduplication.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace Rosette.Api.Tests { diff --git a/tests/TestNameSimilarity.cs b/tests/TestNameSimilarity.cs index 2f04b67..d2eb7ff 100755 --- a/tests/TestNameSimilarity.cs +++ b/tests/TestNameSimilarity.cs @@ -1,4 +1,5 @@ using Rosette.Api.Endpoints; +using Rosette.Api.Models; namespace Rosette.Api.Tests { diff --git a/tests/TestRosetteName.cs b/tests/TestRosetteName.cs index cca371d..b0c072d 100755 --- a/tests/TestRosetteName.cs +++ b/tests/TestRosetteName.cs @@ -1,4 +1,6 @@ -namespace Rosette.Api.Tests +using Rosette.Api.Models; + +namespace Rosette.Api.Tests { public class TestRosetteName { diff --git a/tests/TestRosetteResponse.cs b/tests/TestRosetteResponse.cs index 69e1b77..af8928e 100755 --- a/tests/TestRosetteResponse.cs +++ b/tests/TestRosetteResponse.cs @@ -1,4 +1,5 @@ -using System.Net; +using Rosette.Api.Models; +using System.Net; using System.Text.Json; namespace Rosette.Api.Tests From 3bc995d2da0e45c2b78bc2241a9500813b65d786 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Thu, 12 Feb 2026 08:31:49 -0500 Subject: [PATCH 04/10] BX-68163: Breaks out classes from RecordSimilarityField file --- examples/RecordSimilarity.cs | 67 +- .../Endpoints/Core/EndpointExecutor.cs | 3 +- rosette_api/Endpoints/RecordSimilarity.cs | 18 +- rosette_api/Models/AddressField.cs | 8 + rosette_api/Models/BooleanRecord.cs | 46 + rosette_api/Models/DateField.cs | 15 + rosette_api/Models/FieldedAddressRecord.cs | 168 ++++ rosette_api/Models/FieldedDateRecord.cs | 58 ++ rosette_api/Models/FieldedNameRecord.cs | 91 ++ rosette_api/Models/IAddress.cs | 6 +- rosette_api/Models/NameField.cs | 15 + rosette_api/Models/NumberRecord.cs | 48 + rosette_api/Models/RecordFieldType.cs | 16 + rosette_api/Models/RecordSimilarityField.cs | 917 +----------------- .../Models/RecordSimilarityFieldInfo.cs | 14 - .../Models/RecordSimilarityProperties.cs | 113 +++ rosette_api/Models/RecordSimilarityRecords.cs | 77 ++ rosette_api/Models/StringRecord.cs | 50 + rosette_api/Models/UnfieldedAddressRecord.cs | 53 + rosette_api/Models/UnfieldedDateRecord.cs | 47 + rosette_api/Models/UnfieldedNameRecord.cs | 47 + rosette_api/Models/UnknownFieldRecord.cs | 42 + rosette_api/Utilities.cs | 35 + tests/TestForValidEndpoint.cs | 7 +- 24 files changed, 1023 insertions(+), 938 deletions(-) create mode 100644 rosette_api/Models/AddressField.cs create mode 100644 rosette_api/Models/BooleanRecord.cs create mode 100644 rosette_api/Models/DateField.cs create mode 100644 rosette_api/Models/FieldedAddressRecord.cs create mode 100644 rosette_api/Models/FieldedDateRecord.cs create mode 100644 rosette_api/Models/FieldedNameRecord.cs create mode 100644 rosette_api/Models/NameField.cs create mode 100644 rosette_api/Models/NumberRecord.cs create mode 100644 rosette_api/Models/RecordFieldType.cs create mode 100644 rosette_api/Models/RecordSimilarityProperties.cs create mode 100644 rosette_api/Models/RecordSimilarityRecords.cs create mode 100644 rosette_api/Models/StringRecord.cs create mode 100644 rosette_api/Models/UnfieldedAddressRecord.cs create mode 100644 rosette_api/Models/UnfieldedDateRecord.cs create mode 100644 rosette_api/Models/UnfieldedNameRecord.cs create mode 100644 rosette_api/Models/UnknownFieldRecord.cs create mode 100644 rosette_api/Utilities.cs diff --git a/examples/RecordSimilarity.cs b/examples/RecordSimilarity.cs index f7ea760..a17a3bd 100644 --- a/examples/RecordSimilarity.cs +++ b/examples/RecordSimilarity.cs @@ -21,9 +21,72 @@ private void RunEndpoint(string apiKey, string? altUrl = null) api.UseAlternateURL(altUrl); } - string events_text_data = @"I am looking for flights to Super Bowl 2022 in Inglewood, LA."; + // record field names + string primaryNameField = "primaryName"; + string dobField = "dob"; + string dob2Field = "dob2"; + string addrField = "addr"; + string jobField = "jobTitle"; + string ageField = "age"; + string retiredField = "isRetired"; + string dobHyphen = "1993-04-16"; - Rosette.Api.Endpoints.RecordSimilarity endpoint = new Rosette.Api.Endpoints.RecordSimilarity(events_text_data); + // Creating the request object + Dictionary fields = new Dictionary + { + { primaryNameField, new RecordSimilarityFieldInfo { Type = RecordFieldType.RniName, Weight = 0.5 } }, + { dobField, new RecordSimilarityFieldInfo { Type = RecordFieldType.RniDate, Weight = 0.2 } }, + { dob2Field, new RecordSimilarityFieldInfo { Type = RecordFieldType.RniDate, Weight = 0.1 } }, + { addrField, new RecordSimilarityFieldInfo { Type = RecordFieldType.RniAddress, Weight = 0.5 } }, + { jobField, new RecordSimilarityFieldInfo { Type = RecordFieldType.RniString, Weight = 0.2 } }, + { ageField, new RecordSimilarityFieldInfo { Type = RecordFieldType.RniNumber, Weight = 0.4 } }, + { retiredField, new RecordSimilarityFieldInfo { Type = RecordFieldType.RniBoolean, Weight = 0.05 } } + }; + + RecordSimilarityProperties properties = new RecordSimilarityProperties { Threshold = 0.7, IncludeExplainInfo = true }; + + RecordSimilarityRecords records = new RecordSimilarityRecords + { + Left = new List> + { + new Dictionary + { + { primaryNameField, new FieldedNameRecord { Text = "Ethan R", Language = "eng", LanguageOfOrigin = "eng", Script = "Latn", EntityType = "PERSON"} }, + { dobField, new UnfieldedDateRecord { Date = dobHyphen} }, + { dob2Field, new FieldedDateRecord { Date = "04161993", Format = "MMddyyyy"} }, + { addrField, new UnfieldedAddressRecord { Address = "123 Roadlane Ave"} }, + { jobField, new StringRecord { Text = "software engineer"} } + }, + new Dictionary + { + { primaryNameField, new FieldedNameRecord { Text = "Evan R"} }, + { dobField, new FieldedDateRecord { Date = dobHyphen} }, + { ageField, new NumberRecord { Number = 47.344 } }, + { retiredField, new BooleanRecord { Boolean = false } } + } + }, + Right = new List> + { + new Dictionary + { + { primaryNameField, new FieldedNameRecord { Text = "Seth R", Language = "eng"} }, + { dobField, new FieldedDateRecord { Date = dobHyphen} }, + { jobField, new StringRecord { Text = "manager"} }, + { retiredField, new BooleanRecord { Boolean = true } } + }, + new Dictionary + { + { primaryNameField, new UnfieldedNameRecord { Text = "Ivan R"} }, + { dobField, new FieldedDateRecord { Date = dobHyphen} }, + { dob2Field, new FieldedDateRecord { Date = "1993/04/16"} }, + { addrField, new FieldedAddressRecord { HouseNumber = "123", Road = "Roadlane Ave"} }, + { ageField, new NumberRecord { Number = 72 } }, + { retiredField, new BooleanRecord { Boolean = true } } + } + } + }; + + Rosette.Api.Endpoints.RecordSimilarity endpoint = new Rosette.Api.Endpoints.RecordSimilarity(fields, properties, records); Response response = endpoint.Call(api); // Print out the response headers diff --git a/rosette_api/Endpoints/Core/EndpointExecutor.cs b/rosette_api/Endpoints/Core/EndpointExecutor.cs index ba8f6b5..aa18f67 100644 --- a/rosette_api/Endpoints/Core/EndpointExecutor.cs +++ b/rosette_api/Endpoints/Core/EndpointExecutor.cs @@ -151,7 +151,8 @@ public Response GetCall(ApiClient api) { public virtual Response PostCall(ApiClient api) { string url = api.URI + Endpoint + ToQueryString(); if (Filestream == null) { - HttpContent content = new StringContent(JsonSerializer.Serialize(AppendOptions(_params))); + var requestBody = JsonSerializer.Serialize(AppendOptions(_params)); + HttpContent content = new StringContent(requestBody); content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); Task task = Task.Run(async () => await api.Client.PostAsync(url, content)); var response = task.Result; diff --git a/rosette_api/Endpoints/RecordSimilarity.cs b/rosette_api/Endpoints/RecordSimilarity.cs index 5434eef..29ebb81 100644 --- a/rosette_api/Endpoints/RecordSimilarity.cs +++ b/rosette_api/Endpoints/RecordSimilarity.cs @@ -1,14 +1,28 @@ using Rosette.Api.Endpoints.Core; +using Rosette.Api.Models; namespace Rosette.Api.Endpoints; -public class RecordSimilarity : ContentEndpointBase +public class RecordSimilarity : EndpointBase { /// /// RecordSimilarity returns the similarity score between records /// /// text, Uri object or FileStream - public RecordSimilarity(object content) : base("record-similarity", content) + public RecordSimilarity(Dictionary? fields, + RecordSimilarityProperties? properties, + RecordSimilarityRecords? records) : base("record-similarity") { + ArgumentNullException.ThrowIfNull(fields); + Params["fields"] = fields; + ArgumentNullException.ThrowIfNull(properties); + Params["properties"] = properties; + ArgumentNullException.ThrowIfNull(records); + Params["records"] = records; + } + + public Response Call(ApiClient api) + { + return Funcs.PostCall(api); } } \ No newline at end of file diff --git a/rosette_api/Models/AddressField.cs b/rosette_api/Models/AddressField.cs new file mode 100644 index 0000000..0230af9 --- /dev/null +++ b/rosette_api/Models/AddressField.cs @@ -0,0 +1,8 @@ +namespace Rosette.Api.Models; + +/// +/// Abstract parent class for UnfieldedAddress and FieldedAddress +/// +public abstract class AddressField : RecordSimilarityField +{ +} \ No newline at end of file diff --git a/rosette_api/Models/BooleanRecord.cs b/rosette_api/Models/BooleanRecord.cs new file mode 100644 index 0000000..f9e6cc1 --- /dev/null +++ b/rosette_api/Models/BooleanRecord.cs @@ -0,0 +1,46 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing a boolean record +/// +[JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] +public class BooleanRecord : RecordSimilarityField +{ + /// + /// Gets and sets the boolean record + /// + [JsonPropertyName("data")] + public bool Boolean { get; set; } + + /// + /// No-args constructor + /// + public BooleanRecord() { } + + /// + /// Constructor + /// + /// The boolean record + public BooleanRecord(bool data) + { + Boolean = data; + } + + /// + /// Equals override + /// + public override bool Equals(object? obj) => + obj is BooleanRecord other && Boolean == other.Boolean; + + /// + /// Hashcode override + /// + public override int GetHashCode() => Boolean.GetHashCode(); + + /// + /// ToString override + /// + public override string ToString() => Boolean.ToString().ToLowerInvariant(); +} \ No newline at end of file diff --git a/rosette_api/Models/DateField.cs b/rosette_api/Models/DateField.cs new file mode 100644 index 0000000..7fd78ee --- /dev/null +++ b/rosette_api/Models/DateField.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Abstract parent class for UnfieldedDate and FieldedDate +/// +public abstract class DateField : RecordSimilarityField +{ + /// + /// Gets or sets the date field's date + /// + [JsonPropertyName("date")] + public required string Date { get; set; } +} \ No newline at end of file diff --git a/rosette_api/Models/FieldedAddressRecord.cs b/rosette_api/Models/FieldedAddressRecord.cs new file mode 100644 index 0000000..d9702ce --- /dev/null +++ b/rosette_api/Models/FieldedAddressRecord.cs @@ -0,0 +1,168 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing a fielded address +/// +public class FieldedAddressRecord : AddressField +{ + [JsonPropertyName("house")] + public string? House { get; set; } + + [JsonPropertyName("house_number")] + public string? HouseNumber { get; set; } + + [JsonPropertyName("road")] + public string? Road { get; set; } + + [JsonPropertyName("unit")] + public string? Unit { get; set; } + + [JsonPropertyName("level")] + public string? Level { get; set; } + + [JsonPropertyName("staircase")] + public string? Staircase { get; set; } + + [JsonPropertyName("entrance")] + public string? Entrance { get; set; } + + [JsonPropertyName("suburb")] + public string? Suburb { get; set; } + + [JsonPropertyName("city_district")] + public string? CityDistrict { get; set; } + + [JsonPropertyName("city")] + public string? City { get; set; } + + [JsonPropertyName("island")] + public string? Island { get; set; } + + [JsonPropertyName("state_district")] + public string? StateDistrict { get; set; } + + [JsonPropertyName("state")] + public string? State { get; set; } + + [JsonPropertyName("country_region")] + public string? CountryRegion { get; set; } + + [JsonPropertyName("country")] + public string? Country { get; set; } + + [JsonPropertyName("world_region")] + public string? WorldRegion { get; set; } + + [JsonPropertyName("postcode")] + public string? Postcode { get; set; } + + [JsonPropertyName("po_box")] + public string? PoBox { get; set; } + + /// + /// No-args constructor + /// + public FieldedAddressRecord() { } + + /// + /// Full constructor + /// + public FieldedAddressRecord( + string? house = null, + string? houseNumber = null, + string? road = null, + string? unit = null, + string? level = null, + string? staircase = null, + string? entrance = null, + string? suburb = null, + string? cityDistrict = null, + string? city = null, + string? island = null, + string? stateDistrict = null, + string? state = null, + string? countryRegion = null, + string? country = null, + string? worldRegion = null, + string? postcode = null, + string? poBox = null) + { + House = house; + HouseNumber = houseNumber; + Road = road; + Unit = unit; + Level = level; + Staircase = staircase; + Entrance = entrance; + Suburb = suburb; + CityDistrict = cityDistrict; + City = city; + Island = island; + StateDistrict = stateDistrict; + State = state; + CountryRegion = countryRegion; + Country = country; + WorldRegion = worldRegion; + Postcode = postcode; + PoBox = poBox; + } + + /// + /// Equals override + /// + public override bool Equals(object? obj) => + obj is FieldedAddressRecord other && + House == other.House && + HouseNumber == other.HouseNumber && + Road == other.Road && + Unit == other.Unit && + Level == other.Level && + Staircase == other.Staircase && + Entrance == other.Entrance && + Suburb == other.Suburb && + CityDistrict == other.CityDistrict && + City == other.City && + Island == other.Island && + StateDistrict == other.StateDistrict && + State == other.State && + CountryRegion == other.CountryRegion && + Country == other.Country && + WorldRegion == other.WorldRegion && + Postcode == other.Postcode && + PoBox == other.PoBox; + + /// + /// Hashcode override + /// + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(House); + hash.Add(HouseNumber); + hash.Add(Road); + hash.Add(Unit); + hash.Add(Level); + hash.Add(Staircase); + hash.Add(Entrance); + hash.Add(Suburb); + hash.Add(CityDistrict); + hash.Add(City); + hash.Add(Island); + hash.Add(StateDistrict); + hash.Add(State); + hash.Add(CountryRegion); + hash.Add(Country); + hash.Add(WorldRegion); + hash.Add(Postcode); + hash.Add(PoBox); + return hash.ToHashCode(); + } + + /// + /// ToString override + /// + public override string ToString() => JsonSerializer.Serialize(this); +} \ No newline at end of file diff --git a/rosette_api/Models/FieldedDateRecord.cs b/rosette_api/Models/FieldedDateRecord.cs new file mode 100644 index 0000000..57d5b1a --- /dev/null +++ b/rosette_api/Models/FieldedDateRecord.cs @@ -0,0 +1,58 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing a fielded date +/// +public class FieldedDateRecord : DateField +{ + /// + /// Gets or sets the date field's format + /// + [JsonPropertyName("format")] + public string? Format { get; set; } + + /// + /// No-args constructor + /// + public FieldedDateRecord() + { + Date = string.Empty; + } + + /// + /// Full constructor + /// + /// The date in string format + /// The date's format. Rules are defined at https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html + public FieldedDateRecord(string date, string? format = null) + { + ArgumentException.ThrowIfNullOrWhiteSpace(date); + Date = date; + Format = format; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object? obj) => + obj is FieldedDateRecord other && + Date == other.Date && + Format == other.Format; + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() => HashCode.Combine(Date, Format); + + /// + /// ToString override + /// + /// This fielded date in JSON form + public override string ToString() => JsonSerializer.Serialize(this); +} \ No newline at end of file diff --git a/rosette_api/Models/FieldedNameRecord.cs b/rosette_api/Models/FieldedNameRecord.cs new file mode 100644 index 0000000..f1f6778 --- /dev/null +++ b/rosette_api/Models/FieldedNameRecord.cs @@ -0,0 +1,91 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing a fielded name +/// +public class FieldedNameRecord : NameField +{ + /// + /// Gets or sets the language (ISO 639-3 code) + /// + [JsonPropertyName("language")] + public string? Language { get; set; } + + /// + /// Gets or sets the language of origin (ISO 639-3 code) + /// + [JsonPropertyName("languageOfOrigin")] + public string? LanguageOfOrigin { get; set; } + + /// + /// Gets or sets the script (ISO 15924 code for the name's script) + /// + [JsonPropertyName("script")] + public string? Script { get; set; } + + /// + /// Gets or sets the entity type (PERSON, LOCATION, or ORGANIZATION) + /// + [JsonPropertyName("entityType")] + public string? EntityType { get; set; } + + /// + /// No-args constructor + /// + public FieldedNameRecord() + { + Text = string.Empty; + } + + /// + /// Full constructor + /// + /// Text describing the name + /// Language: ISO 639-3 code (optional) + /// Language the name originates from: ISO 639-3 code (optional) + /// ISO 15924 code for the name's script (optional) + /// Entity type of the name: PERSON, LOCATION, or ORGANIZATION (optional) + public FieldedNameRecord( + string text, + string? language = null, + string? languageOfOrigin = null, + string? script = null, + string? entityType = null) + { + ArgumentException.ThrowIfNullOrWhiteSpace(text); + Text = text; + Language = language; + LanguageOfOrigin = languageOfOrigin; + Script = script; + EntityType = entityType; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object? obj) => + obj is FieldedNameRecord other && + Text == other.Text && + Language == other.Language && + LanguageOfOrigin == other.LanguageOfOrigin && + Script == other.Script && + EntityType == other.EntityType; + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() => + HashCode.Combine(Text, Language, LanguageOfOrigin, Script, EntityType); + + /// + /// ToString override + /// + /// This fielded name in JSON form + public override string ToString() => JsonSerializer.Serialize(this); +} \ No newline at end of file diff --git a/rosette_api/Models/IAddress.cs b/rosette_api/Models/IAddress.cs index c3b96ed..08c41d5 100644 --- a/rosette_api/Models/IAddress.cs +++ b/rosette_api/Models/IAddress.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Rosette.Api.Models; +namespace Rosette.Api.Models; public interface IAddress { diff --git a/rosette_api/Models/NameField.cs b/rosette_api/Models/NameField.cs new file mode 100644 index 0000000..a740b7d --- /dev/null +++ b/rosette_api/Models/NameField.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Abstract parent class for UnfieldedName and FieldedName +/// +public abstract class NameField : RecordSimilarityField +{ + /// + /// Gets or sets the name field's text + /// + [JsonPropertyName("text")] + public required string Text { get; set; } +} \ No newline at end of file diff --git a/rosette_api/Models/NumberRecord.cs b/rosette_api/Models/NumberRecord.cs new file mode 100644 index 0000000..bab26f0 --- /dev/null +++ b/rosette_api/Models/NumberRecord.cs @@ -0,0 +1,48 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing a number record +/// +[JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] +public class NumberRecord : RecordSimilarityField +{ + public const string DATA = "data"; + + /// + /// Gets and sets the number record + /// + [JsonPropertyName("data")] + public double Number { get; set; } + + /// + /// No-args constructor + /// + public NumberRecord() { } + + /// + /// Constructor + /// + /// The number record + public NumberRecord(double data) + { + Number = data; + } + + /// + /// Equals override + /// + public override bool Equals(object? obj) => + obj is NumberRecord other && Number == other.Number; + + /// + /// Hashcode override + /// + public override int GetHashCode() => Number.GetHashCode(); + + /// + /// ToString override + /// + public override string ToString() => Number.ToString(); +} \ No newline at end of file diff --git a/rosette_api/Models/RecordFieldType.cs b/rosette_api/Models/RecordFieldType.cs new file mode 100644 index 0000000..5732b6c --- /dev/null +++ b/rosette_api/Models/RecordFieldType.cs @@ -0,0 +1,16 @@ +namespace Rosette.Api.Models; + +/// RecordFieldType +/// +/// The possible record types that can be used in the Record Similarity endpoint +/// +/// +public static class RecordFieldType +{ + public const string RniName = "rni_name"; + public const string RniDate = "rni_date"; + public const string RniAddress = "rni_address"; + public const string RniString = "rni_string"; + public const string RniNumber = "rni_number"; + public const string RniBoolean = "rni_boolean"; +} diff --git a/rosette_api/Models/RecordSimilarityField.cs b/rosette_api/Models/RecordSimilarityField.cs index e80fe19..25aa547 100644 --- a/rosette_api/Models/RecordSimilarityField.cs +++ b/rosette_api/Models/RecordSimilarityField.cs @@ -1,915 +1,8 @@ -using System.Net.Http.Json; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; +namespace Rosette.Api.Models; -namespace Rosette.Api.Models +/// +/// Parent Interface for RecordSimilarityField objects +/// +public interface RecordSimilarityField { - /// - /// Parent Interface for RecordSimilarityField objects - /// - public interface RecordSimilarityField - { - - } - - /// - /// Abstract parent class for UnfieldedName and FieldedName - /// - public abstract class NameField : RecordSimilarityField - { - public const string TEXT = "text"; - - /// - /// Gets or sets the the name field's text - /// - [JsonPropertyName("text")] - public string Text { get; set; } - } - - /// - /// Class for representing an unfielded name - /// - [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] - public class UnfieldedNameRecord : NameField - { - - /// - /// No-args constructor - /// - public UnfieldedNameRecord() { } - - /// - /// Full constructor - /// - /// The name as a string - public UnfieldedNameRecord(string text) - { - this.Text = text; - } - - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is UnfieldedNameRecord) - { - UnfieldedNameRecord other = obj as UnfieldedNameRecord; - return this.Text == other.Text; - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - return this.Text != null ? this.Text.GetHashCode() : 1; - } - - /// - /// ToString override. Also used for JSON serialization - /// - public override string ToString() - { - return this.Text; - } - } - - /// - /// Class for representing a fielded name - /// - public class FieldedNameRecord : NameField - { - - /// language - /// - /// Getter, Setter for the language - /// language: Language: ISO 639-3 code - /// - /// - [JsonPropertyName("language")] - public string Language { get; set; } - - /// language - /// - /// Getter, Setter for the languageOfOrigin - /// languageOfOrigin: Language: ISO 639-3 code - /// - /// - [JsonPropertyName("languageOfOrigin")] - public string LanguageOfOrigin { get; set; } - - /// script - /// - /// Getter, Setter for the script - /// script: ISO 15924 code for the name's script - /// - /// - [JsonPropertyName("script")] - public string Script { get; set; } - - /// entityType - /// - /// Getter, Setter for the entityType - /// entityType: Entity type of the name: PERSON, LOCATION, or ORGANIZATION - /// - /// - [JsonPropertyName("entityType")] - public string EntityType { get; set; } - - /// - /// No-args constructor - /// - public FieldedNameRecord() { } - - /// - /// Full constructor - /// - /// (string): Text describing the name - /// (string, optional): Language: ISO 639-3 code (ignored for the /language endpoint) - /// (string, optional): Language the name originates from: ISO 639-3 code (ignored for the /language endpoint) - /// (string, optional): ISO 15924 code for the name's script - /// (string, optional): Entity type of the name: PERSON, LOCATION, or ORGANIZATION - public FieldedNameRecord(string text, string language = null, string languageOfOrigin = null, string script = null, string entityType = null) - { - this.Text = text; - this.Language = language; - this.LanguageOfOrigin = languageOfOrigin; - this.Script = script; - this.EntityType = entityType; - } - - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is FieldedNameRecord) - { - FieldedNameRecord other = obj as FieldedNameRecord; - List conditions = new List() { - this.Text == other.Text, - this.Language == other.Language, - this.LanguageOfOrigin == other.LanguageOfOrigin, - this.Script == other.Script, - this.EntityType == other.EntityType - }; - return conditions.All(condition => condition); - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - int h0 = this.Text != null ? this.Text.GetHashCode() : 1; - int h1 = this.Language != null ? this.Language.GetHashCode() : 1; - int h2 = this.LanguageOfOrigin != null ? this.LanguageOfOrigin.GetHashCode() : 1; - int h3 = this.Script != null ? this.Script.GetHashCode() : 1; - int h4 = this.EntityType != null ? this.EntityType.GetHashCode() : 1; - return h0 ^ h1 ^ h2 ^ h3 ^ h4; - } - - /// - /// ToString override. - /// - /// This fielded name in JSON form - public override string ToString() - { - return JsonSerializer.Serialize(this); - } - } - - /// - /// Abstract parent class for UnfieldedDate and FieldedDate - /// - public abstract class DateField : RecordSimilarityField - { - /// - /// Gets or sets the the date field's date - /// - [JsonPropertyName("date")] - public string Date { get; set; } - } - - /// - /// Class for representing an unfielded date - /// - [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] - public class UnfieldedDateRecord : DateField - { - - /// - /// No-args constructor - /// - public UnfieldedDateRecord() { } - /// - /// Full constructor - /// - /// The date in string form - public UnfieldedDateRecord(string date) - { - this.Date = date; - } - - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is UnfieldedDateRecord) - { - UnfieldedDateRecord other = obj as UnfieldedDateRecord; - return this.Date == other.Date; - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - return this.Date != null ? this.Date.GetHashCode() : 1; - } - - /// - /// ToString override. Also used for JSON serialization - /// - public override string ToString() - { - return this.Date; - } - } - - /// - /// Class for representing a fielded date - /// - public class FieldedDateRecord : DateField - { - - /// - /// Gets or sets the the date field's format - /// - [JsonPropertyName("format")] - public string Format { get; set; } - - /// - /// No-args constructor - /// - public FieldedDateRecord() { } - - /// - /// Full constructor - /// - /// The date in string format - /// The date's format. Rules are defined at https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html - public FieldedDateRecord(string date, string format) - { - this.Date = date; - this.Format = format; - } - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is FieldedDateRecord) - { - FieldedDateRecord other = obj as FieldedDateRecord; - List conditions = new List() { - this.Date == other.Date, - this.Format == other.Format - }; - return conditions.All(condition => condition); - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - int h0 = this.Date != null ? this.Date.GetHashCode() : 1; - int h1 = this.Format != null ? this.Format.GetHashCode() : 1; - return h0 ^ h1; - } - - /// - /// ToString override. - /// - /// This fielded date in JSON form - public override string ToString() - { - return JsonSerializer.Serialize(this); - } - } - - /// - /// Abstract parent class for UnfieldedAddress and FieldedAddress - /// - public abstract class AddressField : RecordSimilarityField - { - - } - - /// - /// Class for representing an unfielded address - /// - [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] - public class UnfieldedAddressRecord : AddressField - { - - /// - /// Gets or sets the the address field's address - /// - [JsonPropertyName("address")] - public string Address { get; set; } - - /// - /// No-args constructor - /// - public UnfieldedAddressRecord() { } - /// - /// Full constructor - /// - /// The adress in string form - public UnfieldedAddressRecord(string address) - { - this.Address = address; - } - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is UnfieldedAddressRecord) - { - UnfieldedAddressRecord other = obj as UnfieldedAddressRecord; - return this.Address == other.Address; - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - return this.Address != null ? this.Address.GetHashCode() : 1; - } - - /// - /// ToString override. Also used for JSON serialization - /// - public override string ToString() - { - return this.Address; - } - } - - /// - /// Class for representing a fielded address - /// - public class FieldedAddressRecord : AddressField - { - - /// - /// Gets or sets the the address field's house - /// - [JsonPropertyName("house")] - public string House { get; set; } - - /// - /// Gets or sets the the address field's house number - /// - [JsonPropertyName("house_number")] - public string HouseNumber { get; set; } - - /// - /// Gets or sets the the address field's road - /// - [JsonPropertyName("road")] - public string Road { get; set; } - - /// - /// Gets or sets the the address field's unit - /// - [JsonPropertyName("unit")] - public string Unit { get; set; } - - /// - /// Gets or sets the the address field's level - /// - [JsonPropertyName("level")] - public string Level { get; set; } - - /// - /// Gets or sets the the address field's staircase - /// - [JsonPropertyName("staircase")] - public string Staircase { get; set; } - - /// - /// Gets or sets the the address field's entrance - /// - [JsonPropertyName("entrance")] - public string Entrance { get; set; } - - /// - /// Gets or sets the the address field's suburb - /// - [JsonPropertyName("suburb")] - public string Suburb { get; set; } - - /// - /// Gets or sets the the address field's city district - /// - [JsonPropertyName("city_district")] - public string CityDistrict { get; set; } - - /// - /// Gets or sets the the address field's city - /// - [JsonPropertyName("city")] - public string City { get; set; } - - /// - /// Gets or sets the the address field's island - /// - [JsonPropertyName("island")] - public string Island { get; set; } - - /// - /// Gets or sets the the address field's state district - /// - [JsonPropertyName("state_district")] - public string StateDistrict { get; set; } - - /// - /// Gets or sets the the address field's state - /// - [JsonPropertyName("state")] - public string State { get; set; } - - /// - /// Gets or sets the the address field's country region - /// - [JsonPropertyName("country_region")] - public string CountryRegion { get; set; } - - /// - /// Gets or sets the the address field's country - /// - [JsonPropertyName("country")] - public string Country { get; set; } - - /// - /// Gets or sets the the address field's world region - /// - [JsonPropertyName("world_region")] - public string WorldRegion { get; set; } - - /// - /// Gets or sets the the address field's postcode - /// - [JsonPropertyName("postcode")] - public string Postcode { get; set; } - - /// - /// Gets or sets the the address field's po box - /// - [JsonPropertyName("po_box")] - public string PoBox { get; set; } - - - - /// - /// No-args constructor - /// - public FieldedAddressRecord() { } - /// - /// Full constructor - /// - /// The house - /// The house number - /// The road - /// The unit - /// The level - /// The staircase - /// The entrance - /// The suburb - /// The city district - /// The city - /// The island - /// The state district - /// The state - /// The country region - /// The country - /// The world region - /// The postcode - /// The po box - public FieldedAddressRecord( - string house, - string houseNumber, - string road, - string unit, - string level, - string staircase, - string entrance, - string suburb, - string cityDistrict, - string city, - string island, - string stateDistrict, - string state, - string countryRegion, - string country, - string worldRegion, - string postcode, - string poBox - ) - { - this.House = house; - this.HouseNumber = houseNumber; - this.Road = road; - this.Unit = unit; - this.Level = level; - this.Staircase = staircase; - this.Entrance = entrance; - this.Suburb = suburb; - this.CityDistrict = cityDistrict; - this.City = city; - this.Island = island; - this.StateDistrict = stateDistrict; - this.State = state; - this.CountryRegion = countryRegion; - this.Country = country; - this.WorldRegion = worldRegion; - this.Postcode = postcode; - this.PoBox = poBox; - } - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is FieldedAddressRecord) - { - FieldedAddressRecord other = obj as FieldedAddressRecord; - List conditions = new List() { - this.House == other.House, - this.HouseNumber == other.HouseNumber, - this.Road == other.Road, - this.Unit == other.Unit, - this.Level == other.Level, - this.Staircase == other.Staircase, - this.Entrance == other.Entrance, - this.Suburb == other.Suburb, - this.CityDistrict == other.CityDistrict, - this.City == other.City, - this.Island == other.Island, - this.StateDistrict == other.StateDistrict, - this.State == other.State, - this.CountryRegion == other.CountryRegion, - this.Country == other.Country, - this.WorldRegion == other.WorldRegion, - this.Postcode == other.Postcode, - this.PoBox == other.PoBox - }; - return conditions.All(condition => condition); - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - int h0 = this.House != null ? this.House.GetHashCode() : 1; - int h1 = this.HouseNumber != null ? this.HouseNumber.GetHashCode() : 1; - int h2 = this.Road != null ? this.Road.GetHashCode() : 1; - int h3 = this.Unit != null ? this.Unit.GetHashCode() : 1; - int h4 = this.Level != null ? this.Level.GetHashCode() : 1; - int h5 = this.Staircase != null ? this.Staircase.GetHashCode() : 1; - int h6 = this.Entrance != null ? this.Entrance.GetHashCode() : 1; - int h7 = this.Suburb != null ? this.Suburb.GetHashCode() : 1; - int h8 = this.CityDistrict != null ? this.CityDistrict.GetHashCode() : 1; - int h9 = this.City != null ? this.City.GetHashCode() : 1; - int h10 = this.Island != null ? this.Island.GetHashCode() : 1; - int h11 = this.StateDistrict != null ? this.StateDistrict.GetHashCode() : 1; - int h12 = this.State != null ? this.State.GetHashCode() : 1; - int h13 = this.CountryRegion != null ? this.CountryRegion.GetHashCode() : 1; - int h14 = this.Country != null ? this.Country.GetHashCode() : 1; - int h15 = this.WorldRegion != null ? this.WorldRegion.GetHashCode() : 1; - int h16 = this.Postcode != null ? this.Postcode.GetHashCode() : 1; - int h17 = this.PoBox != null ? this.PoBox.GetHashCode() : 1; - return h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ h7 ^ h8 ^ h9 ^ h10 ^ h11 ^ h12 ^ h13 ^ h14 ^ h15 ^ h16 ^ h17; - } - - /// - /// ToString override. - /// - /// This fielded address in JSON form - public override string ToString() - { - return JsonSerializer.Serialize(this); - } - } - - /// - /// Class for representing a string record - /// - [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] - public class StringRecord : RecordSimilarityField - { - - /// - /// Gets and sets the string record - /// - [JsonPropertyName("data")] - public string Text { get; set;} - - /// - /// No-args constructor - /// - public StringRecord() { } - - /// - /// Constructor - /// - /// The string record - public StringRecord(string data) - { - this.Text = data; - } - - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is StringRecord) - { - StringRecord other = obj as StringRecord; - return this.Text == other.Text; - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - return this.Text != null ? this.Text.GetHashCode() : 1; - } - - /// - /// ToString override. - /// - /// This string record as a string - public override string ToString() - { - return this.Text; - } - } - - /// - /// Class for representing a number record - /// - [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] - public class NumberRecord : RecordSimilarityField - { - public const string DATA = "data"; - - /// - /// Gets and sets the number record - /// - [JsonPropertyName("data")] - public double Number { get; set; } - - /// - /// No-args constructor - /// - public NumberRecord() { } - - /// - /// Constructor - /// - /// The number record - public NumberRecord(double data) - { - this.Number = data; - } - - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is NumberRecord) - { - NumberRecord other = obj as NumberRecord; - return this.Number == other.Number; - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - return this.Number.GetHashCode(); - } - - /// - /// ToString override. - /// - /// This number record as a string - public override string ToString() - { - return this.Number.ToString(); - } - } - - /// - /// Class for representing a boolean record - /// - [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] - public class BooleanRecord : RecordSimilarityField - { - /// - /// Gets and sets the boolean record - /// - [JsonPropertyName("data")] - public bool Boolean { get; set; } - - /// - /// No-args constructor - /// - public BooleanRecord() { } - - /// - /// Constructor - /// - /// The boolean record - public BooleanRecord(bool data) - { - this.Boolean = data; - } - - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is BooleanRecord) - { - BooleanRecord other = obj as BooleanRecord; - return this.Boolean == other.Boolean; - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - return this.Boolean.GetHashCode(); - } - - /// - /// ToString override. - /// - /// This boolean record as a string - public override string ToString() - { - return this.Boolean.ToString().ToLower(); - } - } - - /// - /// Class for representing an unknown field - /// - - [JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] - public class UnknownFieldRecord : RecordSimilarityField - { - /// - /// Gets or the unknown field's data - /// - [JsonPropertyName("data")] - public JsonNode Data { get; } - - /// - /// Constructor - /// - /// The data as a JsonNode - public UnknownFieldRecord(JsonNode data) - { - this.Data = data; - } - - /// - /// Equals override - /// - /// The object to compare - /// True if equal - public override bool Equals(object obj) - { - if (obj is UnknownFieldRecord) - { - UnknownFieldRecord other = obj as UnknownFieldRecord; - return JsonNode.DeepEquals(this.Data, other.Data); - } - else - { - return false; - } - } - - /// - /// Hashcode override - /// - /// The hashcode - public override int GetHashCode() - { - return this.Data != null ? this.Data.GetHashCode() : 1; - } - - /// - /// ToString override. - /// - /// This unknown field in JSON form - public override string ToString() - { - return this.Data.ToJsonString(); - } - - } - } \ No newline at end of file diff --git a/rosette_api/Models/RecordSimilarityFieldInfo.cs b/rosette_api/Models/RecordSimilarityFieldInfo.cs index d92d0c7..05a8f72 100644 --- a/rosette_api/Models/RecordSimilarityFieldInfo.cs +++ b/rosette_api/Models/RecordSimilarityFieldInfo.cs @@ -2,20 +2,6 @@ using System.Text.Json.Serialization; namespace Rosette.Api.Models { - /// RecordFieldType - /// - /// The possible record types that can be used in the Record Similarity endpoint - /// - /// - public static class RecordFieldType - { - public const string RniName = "rni_name"; - public const string RniDate = "rni_date"; - public const string RniAddress = "rni_address"; - public const string RniString = "rni_string"; - public const string RniNumber = "rni_number"; - public const string RniBoolean = "rni_boolean"; - } /// /// Class for representing record similarity field information diff --git a/rosette_api/Models/RecordSimilarityProperties.cs b/rosette_api/Models/RecordSimilarityProperties.cs new file mode 100644 index 0000000..c14bc82 --- /dev/null +++ b/rosette_api/Models/RecordSimilarityProperties.cs @@ -0,0 +1,113 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models +{ + public class RecordSimilarityProperties + { + /// + /// Gets or sets the record similarity request's score threshold + /// + [JsonPropertyName("threshold")] + public double? Threshold { get; set; } = 0.0; + + + /// + /// Gets or sets the record similarity request's include explain info parameter + /// + [JsonPropertyName("includeExplainInfo")] + public bool? IncludeExplainInfo { get; set; } + + /// + /// Gets or sets the record similarity request's parameters + /// + [JsonPropertyName("parameters")] + public Dictionary Parameters { get; set; } + + /// + /// Gets or sets the record similarity request's parameter universe + /// + [JsonPropertyName("parameterUniverse")] + public string ParameterUniverse { get; set; } + + /// + /// No-args constructor + /// + public RecordSimilarityProperties() { } + + /// + /// Includes explain info constructor. Sets the threshold to 0.0. + /// + /// The include explain info parameter + public RecordSimilarityProperties(bool includeExplainInfo) + { + this.IncludeExplainInfo = includeExplainInfo; + this.Threshold = 0.0; + this.Parameters = new Dictionary(); + this.ParameterUniverse = ""; + } + + /// + /// Full constructor + /// + /// The score threshold + /// The include explain info parameter + /// A map of string parameter names to string parameter values + /// The parameter universe to use + public RecordSimilarityProperties(double threshold, bool includeExplainInfo, Dictionary parameters, string parameterUniverse) + { + this.Threshold = threshold; + this.IncludeExplainInfo = includeExplainInfo; + this.Parameters = parameters; + this.ParameterUniverse = parameterUniverse; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is RecordSimilarityProperties) + { + RecordSimilarityProperties other = obj as RecordSimilarityProperties; + List conditions = new List() { + this.Threshold == other.Threshold, + this.IncludeExplainInfo == other.IncludeExplainInfo, + this.Parameters != null && other.Parameters != null ? + Utilities.DictionaryEquals(this.Parameters, other.Parameters) : this.Parameters == other.Parameters, + this.ParameterUniverse == other.ParameterUniverse + }; + return conditions.All(condition => condition); + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + int h0 = this.Threshold.GetHashCode(); + int h1 = this.IncludeExplainInfo.GetHashCode(); + int h2 = this.Parameters != null ? this.Parameters.GetHashCode() : 1; + int h3 = this.ParameterUniverse != null ? this.ParameterUniverse.GetHashCode() : 1; + return h0 ^ h1 ^ h2 ^ h3; + } + + /// + /// ToString override. + /// + /// This record similarity properties in JSON form + public override string ToString() + { + return JsonSerializer.Serialize(this); + } + + } +} diff --git a/rosette_api/Models/RecordSimilarityRecords.cs b/rosette_api/Models/RecordSimilarityRecords.cs new file mode 100644 index 0000000..4004155 --- /dev/null +++ b/rosette_api/Models/RecordSimilarityRecords.cs @@ -0,0 +1,77 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +public class RecordSimilarityRecords +{ + /// + /// Gets or sets the the record similarity request's left records + /// + [JsonPropertyName("left")] + public List> Left { get; set; } + + /// + /// Gets or sets the the record similarity request's right records + /// + [JsonPropertyName("right")] + public List> Right { get; set; } + + /// + /// No-args constructor + /// + public RecordSimilarityRecords() { } + + /// + /// Full constructor + /// + /// The left records + /// The right records + public RecordSimilarityRecords(List> left, List> right) + { + this.Left = left; + this.Right = right; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object obj) + { + if (obj is RecordSimilarityRecords) + { + RecordSimilarityRecords other = obj as RecordSimilarityRecords; + List conditions = new List() { + this.Left != null && other.Left != null ? this.Left.SequenceEqual(other.Left) : this.Left == other.Left, + this.Right != null && other.Right != null ? this.Right.SequenceEqual(other.Right) : this.Right == other.Right + }; + return conditions.All(condition => condition); + } + else + { + return false; + } + } + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() + { + int h0 = this.Left != null ? this.Left.GetHashCode() : 1; + int h1 = this.Right != null ? this.Right.GetHashCode() : 1; + return h0 ^ h1; + } + + /// + /// ToString override. + /// + /// This record similarity records in JSON form + public override string ToString() + { + return JsonSerializer.Serialize(this); + } +} diff --git a/rosette_api/Models/StringRecord.cs b/rosette_api/Models/StringRecord.cs new file mode 100644 index 0000000..7ca910b --- /dev/null +++ b/rosette_api/Models/StringRecord.cs @@ -0,0 +1,50 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing a string record +/// +[JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] +public class StringRecord : RecordSimilarityField +{ + /// + /// Gets and sets the string record + /// + [JsonPropertyName("data")] + public required string Text { get; set; } + + /// + /// No-args constructor + /// + public StringRecord() + { + Text = string.Empty; + } + + /// + /// Constructor + /// + /// The string record + public StringRecord(string data) + { + ArgumentException.ThrowIfNullOrWhiteSpace(data); + Text = data; + } + + /// + /// Equals override + /// + public override bool Equals(object? obj) => + obj is StringRecord other && Text == other.Text; + + /// + /// Hashcode override + /// + public override int GetHashCode() => Text?.GetHashCode() ?? 0; + + /// + /// ToString override + /// + public override string ToString() => Text; +} \ No newline at end of file diff --git a/rosette_api/Models/UnfieldedAddressRecord.cs b/rosette_api/Models/UnfieldedAddressRecord.cs new file mode 100644 index 0000000..043daf1 --- /dev/null +++ b/rosette_api/Models/UnfieldedAddressRecord.cs @@ -0,0 +1,53 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing an unfielded address +/// +[JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] +public class UnfieldedAddressRecord : AddressField +{ + /// + /// Gets or sets the address field's address + /// + [JsonPropertyName("address")] + public required string Address { get; set; } + + /// + /// No-args constructor + /// + public UnfieldedAddressRecord() + { + Address = string.Empty; + } + + /// + /// Full constructor + /// + /// The address in string form + public UnfieldedAddressRecord(string address) + { + ArgumentException.ThrowIfNullOrWhiteSpace(address); + Address = address; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object? obj) => + obj is UnfieldedAddressRecord other && Address == other.Address; + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() => Address?.GetHashCode() ?? 0; + + /// + /// ToString override. Also used for JSON serialization + /// + public override string ToString() => Address; +} \ No newline at end of file diff --git a/rosette_api/Models/UnfieldedDateRecord.cs b/rosette_api/Models/UnfieldedDateRecord.cs new file mode 100644 index 0000000..d301fed --- /dev/null +++ b/rosette_api/Models/UnfieldedDateRecord.cs @@ -0,0 +1,47 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing an unfielded date +/// +[JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] +public class UnfieldedDateRecord : DateField +{ + /// + /// No-args constructor + /// + public UnfieldedDateRecord() + { + Date = string.Empty; + } + + /// + /// Full constructor + /// + /// The date in string form + public UnfieldedDateRecord(string date) + { + ArgumentException.ThrowIfNullOrWhiteSpace(date); + Date = date; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object? obj) => + obj is UnfieldedDateRecord other && Date == other.Date; + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() => Date?.GetHashCode() ?? 0; + + /// + /// ToString override. Also used for JSON serialization + /// + public override string ToString() => Date; +} \ No newline at end of file diff --git a/rosette_api/Models/UnfieldedNameRecord.cs b/rosette_api/Models/UnfieldedNameRecord.cs new file mode 100644 index 0000000..8e942b4 --- /dev/null +++ b/rosette_api/Models/UnfieldedNameRecord.cs @@ -0,0 +1,47 @@ +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing an unfielded name +/// +[JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] +public class UnfieldedNameRecord : NameField +{ + /// + /// No-args constructor + /// + public UnfieldedNameRecord() + { + Text = string.Empty; + } + + /// + /// Full constructor + /// + /// The name as a string + public UnfieldedNameRecord(string text) + { + ArgumentException.ThrowIfNullOrWhiteSpace(text); + Text = text; + } + + /// + /// Equals override + /// + /// The object to compare + /// True if equal + public override bool Equals(object? obj) => + obj is UnfieldedNameRecord other && Text == other.Text; + + /// + /// Hashcode override + /// + /// The hashcode + public override int GetHashCode() => Text?.GetHashCode() ?? 0; + + /// + /// ToString override. Also used for JSON serialization + /// + public override string ToString() => Text; +} \ No newline at end of file diff --git a/rosette_api/Models/UnknownFieldRecord.cs b/rosette_api/Models/UnknownFieldRecord.cs new file mode 100644 index 0000000..ac30e37 --- /dev/null +++ b/rosette_api/Models/UnknownFieldRecord.cs @@ -0,0 +1,42 @@ +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace Rosette.Api.Models; + +/// +/// Class for representing an unknown field +/// +[JsonConverter(typeof(UnfieldedRecordSimilarityConverter))] +public class UnknownFieldRecord : RecordSimilarityField +{ + /// + /// Gets the unknown field's data + /// + [JsonPropertyName("data")] + public JsonNode? Data { get; } + + /// + /// Constructor + /// + /// The data as a JsonNode + public UnknownFieldRecord(JsonNode? data) + { + Data = data; + } + + /// + /// Equals override + /// + public override bool Equals(object? obj) => + obj is UnknownFieldRecord other && JsonNode.DeepEquals(Data, other.Data); + + /// + /// Hashcode override + /// + public override int GetHashCode() => Data?.GetHashCode() ?? 0; + + /// + /// ToString override + /// + public override string ToString() => Data?.ToJsonString() ?? "null"; +} \ No newline at end of file diff --git a/rosette_api/Utilities.cs b/rosette_api/Utilities.cs new file mode 100644 index 0000000..b4100f9 --- /dev/null +++ b/rosette_api/Utilities.cs @@ -0,0 +1,35 @@ +namespace Rosette.Api; + +public static class Utilities +{ + /// + /// Compares two dictionaries for equality + /// + public static bool DictionaryEquals(this IDictionary first, IDictionary second) + { + if (first == null && second == null) + { + return true; + } + if (first == null || second == null) + { + return false; + } + if (first.Count != second.Count) + { + return false; + } + foreach (var kvp in first) + { + if (!second.TryGetValue(kvp.Key, out TValue secondValue)) + { + return false; + } + if (!kvp.Value.Equals(secondValue)) + { + return false; + } + } + return true; + } +} diff --git a/tests/TestForValidEndpoint.cs b/tests/TestForValidEndpoint.cs index 4438d06..7c79bda 100755 --- a/tests/TestForValidEndpoint.cs +++ b/tests/TestForValidEndpoint.cs @@ -96,10 +96,13 @@ public void PingEndpoint() [Fact] public void RecordSimilarityEndpoint() { - RecordSimilarity rs = new RecordSimilarity("foo"); + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new RecordSimilarity(fields, properties, records); Assert.Equal("record-similarity", rs.Endpoint); - Assert.Equal("foo", rs.Content); } [Fact] From f908d062df1174bdde1d1766d1d3db66f3411f55 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Thu, 12 Feb 2026 12:48:29 -0500 Subject: [PATCH 05/10] BX-68163: Adds a new Json converter for RecordSimilarity --- rosette_api/Models/BooleanRecord.cs | 1 + .../RecordSimilarityFieldConverter.cs | 111 ++++++++++++++++++ .../RecordSimilarityRecordsConverter.cs | 95 +++++++++++++++ .../UnfieldedRecordSimilarityConverter.cs} | 3 +- rosette_api/Models/NumberRecord.cs | 1 + rosette_api/Models/RecordSimilarityRecords.cs | 4 +- rosette_api/Models/StringRecord.cs | 1 + rosette_api/Models/UnfieldedAddressRecord.cs | 1 + rosette_api/Models/UnfieldedDateRecord.cs | 1 + rosette_api/Models/UnfieldedNameRecord.cs | 1 + rosette_api/Models/UnknownFieldRecord.cs | 1 + 11 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs create mode 100644 rosette_api/Models/JsonConverter/RecordSimilarityRecordsConverter.cs rename rosette_api/Models/{RecordSimilarityConverter.cs => JsonConverter/UnfieldedRecordSimilarityConverter.cs} (96%) diff --git a/rosette_api/Models/BooleanRecord.cs b/rosette_api/Models/BooleanRecord.cs index f9e6cc1..f1e206d 100644 --- a/rosette_api/Models/BooleanRecord.cs +++ b/rosette_api/Models/BooleanRecord.cs @@ -1,3 +1,4 @@ +using rosette_api.Models.JsonConverter; using System.Text.Json.Serialization; namespace Rosette.Api.Models; diff --git a/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs b/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs new file mode 100644 index 0000000..0c3b8b0 --- /dev/null +++ b/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs @@ -0,0 +1,111 @@ +using Rosette.Api.Models; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; + +namespace rosette_api.Models.JsonConverter; + +/// +/// JsonConverter for RecordSimilarityField interface polymorphic serialization +/// +public class RecordSimilarityFieldConverter : JsonConverter +{ + public override RecordSimilarityField? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + // Attempt to deserialize based on JSON structure + return reader.TokenType switch + { + JsonTokenType.String => new StringRecord { Text = reader.GetString() ?? string.Empty }, + JsonTokenType.Number => new NumberRecord(reader.GetDouble()), + JsonTokenType.True or JsonTokenType.False => new BooleanRecord(reader.GetBoolean()), + JsonTokenType.StartObject => DeserializeObject(ref reader, options), + _ => null + }; + } + + private static RecordSimilarityField? DeserializeObject(ref Utf8JsonReader reader, JsonSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); + var root = doc.RootElement; + + // Check for known property patterns to determine type + if (root.TryGetProperty("text", out _)) + { + if (root.TryGetProperty("language", out _) || + root.TryGetProperty("entityType", out _)) + { + return JsonSerializer.Deserialize(root.GetRawText(), options); + } + return JsonSerializer.Deserialize(root.GetRawText(), options); + } + + if (root.TryGetProperty("date", out _)) + { + if (root.TryGetProperty("format", out _)) + { + return JsonSerializer.Deserialize(root.GetRawText(), options); + } + return JsonSerializer.Deserialize(root.GetRawText(), options); + } + + if (root.TryGetProperty("address", out _)) + { + return JsonSerializer.Deserialize(root.GetRawText(), options); + } + + if (root.TryGetProperty("house", out _) || root.TryGetProperty("city", out _)) + { + return JsonSerializer.Deserialize(root.GetRawText(), options); + } + + // Default to unknown field + return new UnknownFieldRecord(JsonNode.Parse(root.GetRawText())); + } + + public override void Write(Utf8JsonWriter writer, RecordSimilarityField value, JsonSerializerOptions options) + { + switch (value) + { + case UnfieldedNameRecord unfieldedName: + writer.WriteStringValue(unfieldedName.Text); + break; + case UnfieldedDateRecord unfieldedDate: + writer.WriteStringValue(unfieldedDate.Date); + break; + case UnfieldedAddressRecord unfieldedAddress: + writer.WriteStringValue(unfieldedAddress.Address); + break; + case FieldedNameRecord fieldedName: + JsonSerializer.Serialize(writer, fieldedName, options); + break; + case FieldedDateRecord fieldedDate: + JsonSerializer.Serialize(writer, fieldedDate, options); + break; + case FieldedAddressRecord fieldedAddress: + JsonSerializer.Serialize(writer, fieldedAddress, options); + break; + case NumberRecord numberRecord: + writer.WriteNumberValue(numberRecord.Number); + break; + case BooleanRecord booleanRecord: + writer.WriteBooleanValue(booleanRecord.Boolean); + break; + case StringRecord stringRecord: + writer.WriteStringValue(stringRecord.Text); + break; + case UnknownFieldRecord unknownField: + if (unknownField.Data != null) + { + unknownField.Data.WriteTo(writer, options); + } + else + { + writer.WriteNullValue(); + } + break; + default: + JsonSerializer.Serialize(writer, value, value.GetType(), options); + break; + } + } +} \ No newline at end of file diff --git a/rosette_api/Models/JsonConverter/RecordSimilarityRecordsConverter.cs b/rosette_api/Models/JsonConverter/RecordSimilarityRecordsConverter.cs new file mode 100644 index 0000000..2dd4e5c --- /dev/null +++ b/rosette_api/Models/JsonConverter/RecordSimilarityRecordsConverter.cs @@ -0,0 +1,95 @@ +using Rosette.Api.Models; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace rosette_api.Models.JsonConverter; + +/// +/// JsonConverter for RecordSimilarityRecords to properly serialize RecordSimilarityField values +/// +public class RecordSimilarityRecordsConverter : JsonConverter +{ + private readonly RecordSimilarityFieldConverter _fieldConverter = new(); + + public override RecordSimilarityRecords? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + using var doc = JsonDocument.ParseValue(ref reader); + var root = doc.RootElement; + + var left = ReadRecordList(root.GetProperty("left"), options); + var right = ReadRecordList(root.GetProperty("right"), options); + + return new RecordSimilarityRecords(left, right); + } + + private List> ReadRecordList( + JsonElement arrayElement, + JsonSerializerOptions options) + { + var list = new List>(); + + foreach (var objElement in arrayElement.EnumerateArray()) + { + var dict = new Dictionary(); + + foreach (var prop in objElement.EnumerateObject()) + { + var reader = new Utf8JsonReader( + System.Text.Encoding.UTF8.GetBytes(prop.Value.GetRawText())); + reader.Read(); + + var field = _fieldConverter.Read(ref reader, typeof(RecordSimilarityField), options); + if (field != null) + { + dict[prop.Name] = field; + } + } + + list.Add(dict); + } + + return list; + } + + public override void Write( + Utf8JsonWriter writer, + RecordSimilarityRecords value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WritePropertyName("left"); + WriteDictionaryList(writer, value.Left, options); + + writer.WritePropertyName("right"); + WriteDictionaryList(writer, value.Right, options); + + writer.WriteEndObject(); + } + + private void WriteDictionaryList( + Utf8JsonWriter writer, + List> list, + JsonSerializerOptions options) + { + writer.WriteStartArray(); + + foreach (var dict in list) + { + writer.WriteStartObject(); + + foreach (var kvp in dict) + { + writer.WritePropertyName(kvp.Key); + _fieldConverter.Write(writer, kvp.Value, options); + } + + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + } +} \ No newline at end of file diff --git a/rosette_api/Models/RecordSimilarityConverter.cs b/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs similarity index 96% rename from rosette_api/Models/RecordSimilarityConverter.cs rename to rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs index f972a40..40c9f98 100644 --- a/rosette_api/Models/RecordSimilarityConverter.cs +++ b/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs @@ -1,7 +1,8 @@ +using Rosette.Api.Models; using System.Text.Json; using System.Text.Json.Serialization; -namespace Rosette.Api.Models; +namespace rosette_api.Models.JsonConverter; /// /// JsonConverter for Unfielded Record Similarity objects diff --git a/rosette_api/Models/NumberRecord.cs b/rosette_api/Models/NumberRecord.cs index bab26f0..adfbb0f 100644 --- a/rosette_api/Models/NumberRecord.cs +++ b/rosette_api/Models/NumberRecord.cs @@ -1,3 +1,4 @@ +using rosette_api.Models.JsonConverter; using System.Text.Json.Serialization; namespace Rosette.Api.Models; diff --git a/rosette_api/Models/RecordSimilarityRecords.cs b/rosette_api/Models/RecordSimilarityRecords.cs index 4004155..1b8f0e7 100644 --- a/rosette_api/Models/RecordSimilarityRecords.cs +++ b/rosette_api/Models/RecordSimilarityRecords.cs @@ -1,8 +1,10 @@ -using System.Text.Json; +using rosette_api.Models.JsonConverter; +using System.Text.Json; using System.Text.Json.Serialization; namespace Rosette.Api.Models; +[JsonConverter(typeof(RecordSimilarityRecordsConverter))] public class RecordSimilarityRecords { /// diff --git a/rosette_api/Models/StringRecord.cs b/rosette_api/Models/StringRecord.cs index 7ca910b..a2a0c3e 100644 --- a/rosette_api/Models/StringRecord.cs +++ b/rosette_api/Models/StringRecord.cs @@ -1,3 +1,4 @@ +using rosette_api.Models.JsonConverter; using System.Text.Json.Serialization; namespace Rosette.Api.Models; diff --git a/rosette_api/Models/UnfieldedAddressRecord.cs b/rosette_api/Models/UnfieldedAddressRecord.cs index 043daf1..2f61ecd 100644 --- a/rosette_api/Models/UnfieldedAddressRecord.cs +++ b/rosette_api/Models/UnfieldedAddressRecord.cs @@ -1,3 +1,4 @@ +using rosette_api.Models.JsonConverter; using System.Text.Json.Serialization; namespace Rosette.Api.Models; diff --git a/rosette_api/Models/UnfieldedDateRecord.cs b/rosette_api/Models/UnfieldedDateRecord.cs index d301fed..03aeda0 100644 --- a/rosette_api/Models/UnfieldedDateRecord.cs +++ b/rosette_api/Models/UnfieldedDateRecord.cs @@ -1,3 +1,4 @@ +using rosette_api.Models.JsonConverter; using System.Text.Json.Serialization; namespace Rosette.Api.Models; diff --git a/rosette_api/Models/UnfieldedNameRecord.cs b/rosette_api/Models/UnfieldedNameRecord.cs index 8e942b4..f8dfe1d 100644 --- a/rosette_api/Models/UnfieldedNameRecord.cs +++ b/rosette_api/Models/UnfieldedNameRecord.cs @@ -1,3 +1,4 @@ +using rosette_api.Models.JsonConverter; using System.Text.Json.Serialization; namespace Rosette.Api.Models; diff --git a/rosette_api/Models/UnknownFieldRecord.cs b/rosette_api/Models/UnknownFieldRecord.cs index ac30e37..e359beb 100644 --- a/rosette_api/Models/UnknownFieldRecord.cs +++ b/rosette_api/Models/UnknownFieldRecord.cs @@ -1,3 +1,4 @@ +using rosette_api.Models.JsonConverter; using System.Text.Json.Nodes; using System.Text.Json.Serialization; From 2db41c8443290ecd0690b8088b45cc0dfab761bf Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Thu, 12 Feb 2026 13:15:47 -0500 Subject: [PATCH 06/10] BX-68163: Updated the address objects --- examples/AddressSimilarity.cs | 9 +- rosette_api/Endpoints/AddressSimilarity.cs | 2 +- rosette_api/Models/Address.cs | 99 ------------------- rosette_api/Models/IAddress.cs | 6 -- .../UnfieldedRecordSimilarityConverter.cs | 8 +- rosette_api/Models/UnfieldedAddress.cs | 39 -------- tests/TestForValidEndpoint.cs | 2 +- 7 files changed, 11 insertions(+), 154 deletions(-) delete mode 100644 rosette_api/Models/Address.cs delete mode 100644 rosette_api/Models/IAddress.cs delete mode 100644 rosette_api/Models/UnfieldedAddress.cs diff --git a/examples/AddressSimilarity.cs b/examples/AddressSimilarity.cs index 1b7be29..6c2683e 100644 --- a/examples/AddressSimilarity.cs +++ b/examples/AddressSimilarity.cs @@ -21,13 +21,8 @@ private void RunEndpoint(string apiKey, string? altUrl = null) api.UseAlternateURL(altUrl); } - var add1 = new UnfieldedAddress("160 Pennsilvana Avenue, Washington, D.C., 20500"); - var add2 = new Address() - .SetHouseNumber("1600") - .SetRoad("Pennsylvania Ave. NW") - .SetCity("Washington") - .SetState("D.C.") - .SetPostCode("20500"); + var add1 = new UnfieldedAddressRecord { Address = "160 Pennsylvana Avenue, Washington, D.C., 20500" }; + var add2 = new FieldedAddressRecord(houseNumber: "1600", road: "Pennsylvania Ave N.W.", city: "Washington", state: "DC", postcode: "20500"); Rosette.Api.Endpoints.AddressSimilarity endpoint = new Rosette.Api.Endpoints.AddressSimilarity(add1, add2); diff --git a/rosette_api/Endpoints/AddressSimilarity.cs b/rosette_api/Endpoints/AddressSimilarity.cs index bfefc77..69a29e6 100644 --- a/rosette_api/Endpoints/AddressSimilarity.cs +++ b/rosette_api/Endpoints/AddressSimilarity.cs @@ -10,7 +10,7 @@ public class AddressSimilarity : EndpointBase /// /// RosetteAddress object /// RosetteAddress object - public AddressSimilarity(IAddress? address1, IAddress? address2) : base("address-similarity") + public AddressSimilarity(AddressField? address1, AddressField? address2) : base("address-similarity") { ArgumentNullException.ThrowIfNull(address1); Params["address1"] = address1; diff --git a/rosette_api/Models/Address.cs b/rosette_api/Models/Address.cs deleted file mode 100644 index 963b454..0000000 --- a/rosette_api/Models/Address.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Rosette.Api.Models; - -public class Address : IAddress -{ - [JsonPropertyName("houseNumber")] - public string? HouseNumber { get; private set; } - - [JsonPropertyName("road")] - public string? Road { get; private set; } - - [JsonPropertyName("city")] - public string? City { get; private set; } - - [JsonPropertyName("state")] - public string? State { get; private set; } - - [JsonPropertyName("postCode")] - public string? PostCode { get; private set; } - - /// - /// Constructor for a Address object, used by several endpoints - /// - /// optional house number - /// optional road - /// optional city - /// optional state - /// optional post code - [JsonConstructor] - public Address(string? houseNumber = null, string? road = null, string? city = null, string? state = null, string? postCode = null) - { - HouseNumber = houseNumber; - Road = road; - City = city; - State = state; - PostCode = postCode; - } - - /// - /// SetHouseNumber sets the house number - /// - /// house number - /// Updated Address instance - public Address SetHouseNumber(string? houseNumber) - { - HouseNumber = houseNumber; - return this; - } - - /// - /// SetRoad sets the road - /// - /// road - /// Updated Address instance - public Address SetRoad(string? road) - { - Road = road; - return this; - } - - /// - /// SetCity sets the city - /// - /// city - /// Updated Address instance - public Address SetCity(string? city) - { - City = city; - return this; - } - - /// - /// SetState sets the state - /// - /// state - /// Updated Address instance - public Address SetState(string? state) - { - State = state; - return this; - } - - /// - /// SetPostCode sets the post code - /// - /// post code - /// Updated Address instance - public Address SetPostCode(string? postCode) - { - PostCode = postCode; - return this; - } - - public bool Fielded() - { - return true; - } -} \ No newline at end of file diff --git a/rosette_api/Models/IAddress.cs b/rosette_api/Models/IAddress.cs deleted file mode 100644 index 08c41d5..0000000 --- a/rosette_api/Models/IAddress.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Rosette.Api.Models; - -public interface IAddress -{ - bool Fielded(); -} diff --git a/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs b/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs index 40c9f98..394cf8b 100644 --- a/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs +++ b/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs @@ -22,6 +22,7 @@ public override bool CanConvert(Type typeToConvert) return typeToConvert == typeof(UnknownFieldRecord) || typeToConvert == typeof(NumberRecord) || typeToConvert == typeof(BooleanRecord) || + typeToConvert == typeof(UnfieldedAddressRecord) || typeToConvert == typeof(object); } @@ -31,7 +32,9 @@ public override bool CanConvert(Type typeToConvert) { JsonTokenType.Number => new NumberRecord { Number = reader.GetDouble() }, JsonTokenType.True or JsonTokenType.False => new BooleanRecord { Boolean = reader.GetBoolean() }, - JsonTokenType.String => reader.GetString(), + JsonTokenType.String => typeToConvert == typeof(UnfieldedAddressRecord) + ? new UnfieldedAddressRecord { Address = reader.GetString() ?? string.Empty } + : reader.GetString(), JsonTokenType.StartObject => JsonSerializer.Deserialize(ref reader, options), _ => null }; @@ -41,6 +44,9 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp { switch (value) { + case UnfieldedAddressRecord unfieldedAddress: + writer.WriteStringValue(unfieldedAddress.Address); + break; case UnknownFieldRecord unknownField: JsonSerializer.Serialize(writer, unknownField.Data, options); break; diff --git a/rosette_api/Models/UnfieldedAddress.cs b/rosette_api/Models/UnfieldedAddress.cs deleted file mode 100644 index d2bb2d9..0000000 --- a/rosette_api/Models/UnfieldedAddress.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Rosette.Api.Models; - -public class UnfieldedAddress : IAddress -{ - [JsonPropertyName("address")] - public string? Address { get; private set; } - - /// - /// Constructor for a Address object, used by several endpoints - /// - /// optional house number - /// optional road - /// optional city - /// optional state - /// optional post code - [JsonConstructor] - public UnfieldedAddress(string? address = null) - { - Address = address; - } - - /// - /// SetAddress sets the address - /// - /// address - /// Updated Address instance - public UnfieldedAddress SetAddress(string? address) - { - Address = address; - return this; - } - - public bool Fielded() - { - return false; - } -} \ No newline at end of file diff --git a/tests/TestForValidEndpoint.cs b/tests/TestForValidEndpoint.cs index 7c79bda..a985c28 100755 --- a/tests/TestForValidEndpoint.cs +++ b/tests/TestForValidEndpoint.cs @@ -9,7 +9,7 @@ public class TestForValidEndpoint [Fact] public void AddressSimilarityEndpoint() { - Address a = new Address("foo"); + var a = new UnfieldedAddressRecord { Address = "foo" }; AddressSimilarity asim = new AddressSimilarity(a,a); Assert.Equal("address-similarity", asim.Endpoint); From 3ac302fb131c7165f82def3824c7d06afa0c4f86 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Thu, 12 Feb 2026 16:08:08 -0500 Subject: [PATCH 07/10] BX-68163: Updates to the unit tests --- tests/CategoriesTests.cs | 48 +++ tests/EntitiesTests.cs | 52 +++ tests/LanguageTests.cs | 43 ++ ...plication.cs => NameDeduplicationTests.cs} | 82 ++-- ...meSimilarity.cs => NameSimilarityTests.cs} | 40 +- tests/{TestRosetteName.cs => NameTests.cs} | 98 ++--- ...Translation.cs => NameTranslationTests.cs} | 82 ++-- tests/RelationshipsTests.cs | 39 ++ ...estRosetteResponse.cs => ResponseTests.cs} | 56 +-- tests/SentencesTests.cs | 37 ++ tests/SentimentTests.cs | 48 +++ tests/SimilarTermsTests.cs | 37 ++ tests/TokensTests.cs | 36 ++ ...ValidEndpoint.cs => ValidEndpointTests.cs} | 400 +++++++++--------- 14 files changed, 725 insertions(+), 373 deletions(-) create mode 100644 tests/CategoriesTests.cs create mode 100644 tests/EntitiesTests.cs create mode 100644 tests/LanguageTests.cs rename tests/{TestNameDeduplication.cs => NameDeduplicationTests.cs} (93%) mode change 100755 => 100644 rename tests/{TestNameSimilarity.cs => NameSimilarityTests.cs} (92%) mode change 100755 => 100644 rename tests/{TestRosetteName.cs => NameTests.cs} (94%) mode change 100755 => 100644 rename tests/{TestNameTranslation.cs => NameTranslationTests.cs} (94%) mode change 100755 => 100644 create mode 100644 tests/RelationshipsTests.cs rename tests/{TestRosetteResponse.cs => ResponseTests.cs} (92%) mode change 100755 => 100644 create mode 100644 tests/SentencesTests.cs create mode 100644 tests/SentimentTests.cs create mode 100644 tests/SimilarTermsTests.cs create mode 100644 tests/TokensTests.cs rename tests/{TestForValidEndpoint.cs => ValidEndpointTests.cs} (93%) mode change 100755 => 100644 diff --git a/tests/CategoriesTests.cs b/tests/CategoriesTests.cs new file mode 100644 index 0000000..509ae84 --- /dev/null +++ b/tests/CategoriesTests.cs @@ -0,0 +1,48 @@ +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class CategoriesTests + { + [Fact] + public void CheckBasicUsage() + { + Categories c = new Categories("Sample text for categorization"); + + Assert.Equal("categories", c.Endpoint); + Assert.Equal("Sample text for categorization", c.Content); + } + + [Fact] + public void CheckWithLanguage() + { + Categories c = new Categories("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", c.Language); + Assert.Equal("Sample text", c.Content); + } + + [Fact] + public void CheckWithGenre() + { + Categories c = new Categories("Sample text") + .SetGenre("social-media"); + + Assert.Equal("social-media", c.Genre); + } + + [Fact] + public void CheckFluentAPI() + { + Categories c = new Categories("Sample text") + .SetLanguage("eng") + .SetGenre("social-media") + .SetOption("customOption", "value"); + + Assert.Equal("eng", c.Language); + Assert.Equal("social-media", c.Genre); + Assert.Equal("value", c.Options["customOption"]); + } + } +} \ No newline at end of file diff --git a/tests/EntitiesTests.cs b/tests/EntitiesTests.cs new file mode 100644 index 0000000..7358616 --- /dev/null +++ b/tests/EntitiesTests.cs @@ -0,0 +1,52 @@ +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class EntitiesTests + { + [Fact] + public void CheckBasicUsage() + { + string text = "Bill Murray will appear in new Ghostbusters film."; + Entities e = new Entities(text); + + Assert.Equal("entities", e.Endpoint); + Assert.Equal(text, e.Content); + } + + [Fact] + public void CheckWithLanguageAndGenre() + { + Entities e = new Entities("Sample text") + .SetLanguage("eng") + .SetGenre("social-media"); + + Assert.Equal("eng", e.Language); + Assert.Equal("social-media", e.Genre); + } + + [Fact] + public void CheckWithUrlParameter() + { + Entities e = new Entities("Sample text") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", e.UrlParameters["output"]); + } + + [Fact] + public void CheckFluentAPI() + { + Entities e = new Entities("Apple announced a new iPhone.") + .SetLanguage("eng") + .SetGenre("news") + .SetOption("linkEntities", true) + .SetUrlParameter("output", "rosette"); + + Assert.Equal("eng", e.Language); + Assert.Equal("news", e.Genre); + Assert.True((bool)e.Options["linkEntities"]); + Assert.Equal("rosette", e.UrlParameters["output"]); + } + } +} \ No newline at end of file diff --git a/tests/LanguageTests.cs b/tests/LanguageTests.cs new file mode 100644 index 0000000..d020baa --- /dev/null +++ b/tests/LanguageTests.cs @@ -0,0 +1,43 @@ +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class LanguageTests + { + [Fact] + public void CheckBasicUsage() + { + Language l = new Language("Por favor Señorita, says the man."); + + Assert.Equal("language", l.Endpoint); + Assert.Equal("Por favor Señorita, says the man.", l.Content); + } + + [Fact] + public void CheckWithUri() + { + Uri uri = new Uri("http://example.com"); + Language l = new Language(uri); + + Assert.Equal("http://example.com/", l.Content.ToString()); + } + + [Fact] + public void CheckContentUpdate() + { + Language l = new Language("Original text") + .SetContent("Updated text"); + + Assert.Equal("Updated text", l.Content); + } + + [Fact] + public void CheckWithGenre() + { + Language l = new Language("Sample text") + .SetGenre("social-media"); + + Assert.Equal("social-media", l.Genre); + } + } +} \ No newline at end of file diff --git a/tests/TestNameDeduplication.cs b/tests/NameDeduplicationTests.cs old mode 100755 new mode 100644 similarity index 93% rename from tests/TestNameDeduplication.cs rename to tests/NameDeduplicationTests.cs index ff84eff..9b382a7 --- a/tests/TestNameDeduplication.cs +++ b/tests/NameDeduplicationTests.cs @@ -1,41 +1,41 @@ -using Rosette.Api.Endpoints; -using Rosette.Api.Models; - -namespace Rosette.Api.Tests -{ - public class TestNameDeduplication - { - [Fact] - public void CheckBasicUsage() { - List names = new List { - new Name("foo"), - new Name("bar") - }; - NameDeduplication n = new NameDeduplication(names); - Assert.Equal(names, n.Names); - Assert.Equal(0.75f, n.Threshold); - } - - [Fact] - public void CheckProfileID() { - List names = new List { - new Name("foo"), - new Name("bar") - }; - NameDeduplication n = new NameDeduplication(names).SetProfileID("profileid"); - Assert.Equal(names, n.Names); - Assert.Equal("profileid", n.ProfileID); - } - - [Fact] - public void CheckThreshold() { - List names = new List { - new Name("foo"), - new Name("bar") - }; - NameDeduplication n = new NameDeduplication(names).SetThreshold(0.8f); - Assert.Equal(names, n.Names); - Assert.Equal(0.8f, n.Threshold); - } - } -} +using Rosette.Api.Endpoints; +using Rosette.Api.Models; + +namespace Rosette.Api.Tests +{ + public class NameDeduplicationTests + { + [Fact] + public void CheckBasicUsage() { + List names = new List { + new Name("foo"), + new Name("bar") + }; + NameDeduplication n = new NameDeduplication(names); + Assert.Equal(names, n.Names); + Assert.Equal(0.75f, n.Threshold); + } + + [Fact] + public void CheckProfileID() { + List names = new List { + new Name("foo"), + new Name("bar") + }; + NameDeduplication n = new NameDeduplication(names).SetProfileID("profileid"); + Assert.Equal(names, n.Names); + Assert.Equal("profileid", n.ProfileID); + } + + [Fact] + public void CheckThreshold() { + List names = new List { + new Name("foo"), + new Name("bar") + }; + NameDeduplication n = new NameDeduplication(names).SetThreshold(0.8f); + Assert.Equal(names, n.Names); + Assert.Equal(0.8f, n.Threshold); + } + } +} diff --git a/tests/TestNameSimilarity.cs b/tests/NameSimilarityTests.cs old mode 100755 new mode 100644 similarity index 92% rename from tests/TestNameSimilarity.cs rename to tests/NameSimilarityTests.cs index d2eb7ff..ce17caa --- a/tests/TestNameSimilarity.cs +++ b/tests/NameSimilarityTests.cs @@ -1,20 +1,20 @@ -using Rosette.Api.Endpoints; -using Rosette.Api.Models; - -namespace Rosette.Api.Tests -{ - public class TestNameSimilarity - { - [Fact] - public void CheckForNull() { - var exception = Record.Exception(() => new NameSimilarity(null, null)); - Assert.IsType(exception); - Assert.Equal("Value cannot be null. (Parameter 'name1')", exception.Message); - - Name rn = new Name("foo"); - exception = Record.Exception(() => new NameSimilarity(rn, null)); - Assert.IsType(exception); - Assert.Equal("Value cannot be null. (Parameter 'name2')", exception.Message); - } - } -} +using Rosette.Api.Endpoints; +using Rosette.Api.Models; + +namespace Rosette.Api.Tests +{ + public class NameSimilarityTests + { + [Fact] + public void CheckForNull() { + var exception = Record.Exception(() => new NameSimilarity(null, null)); + Assert.IsType(exception); + Assert.Equal("Value cannot be null. (Parameter 'name1')", exception.Message); + + Name rn = new Name("foo"); + exception = Record.Exception(() => new NameSimilarity(rn, null)); + Assert.IsType(exception); + Assert.Equal("Value cannot be null. (Parameter 'name2')", exception.Message); + } + } +} diff --git a/tests/TestRosetteName.cs b/tests/NameTests.cs old mode 100755 new mode 100644 similarity index 94% rename from tests/TestRosetteName.cs rename to tests/NameTests.cs index b0c072d..df7d178 --- a/tests/TestRosetteName.cs +++ b/tests/NameTests.cs @@ -1,49 +1,49 @@ -using Rosette.Api.Models; - -namespace Rosette.Api.Tests -{ - public class TestRosetteName - { - [Fact] - public void CheckName() { - Name rn = new Name("foo"); - Assert.Equal("foo", rn.Text); - Assert.Null(rn.EntityType); - Assert.Null(rn.Language); - Assert.Null(rn.Script); - } - - [Fact] - public void CheckWithEntityType() { - Name rn = new Name("foo").SetEntityType("PERSON"); - Assert.Equal("foo", rn.Text); - Assert.Equal("PERSON", rn.EntityType); - } - - [Fact] - public void CheckWithLanguage() { - Name rn = new Name("foo").SetLanguage("eng"); - Assert.Equal("foo", rn.Text); - Assert.Equal("eng", rn.Language); - } - - [Fact] - public void CheckWithScript() { - Name rn = new Name("foo").SetScript("zho"); - Assert.Equal("foo", rn.Text); - Assert.Equal("zho", rn.Script); - } - - [Fact] - public void CheckAll() { - Name rn = new Name("foo") - .SetEntityType("PERSON") - .SetLanguage("eng") - .SetScript("zho"); - Assert.Equal("foo", rn.Text); - Assert.Equal("PERSON", rn.EntityType); - Assert.Equal("eng", rn.Language); - Assert.Equal("zho", rn.Script); - } - } -} +using Rosette.Api.Models; + +namespace Rosette.Api.Tests +{ + public class NameTests + { + [Fact] + public void CheckName() { + Name rn = new Name("foo"); + Assert.Equal("foo", rn.Text); + Assert.Null(rn.EntityType); + Assert.Null(rn.Language); + Assert.Null(rn.Script); + } + + [Fact] + public void CheckWithEntityType() { + Name rn = new Name("foo").SetEntityType("PERSON"); + Assert.Equal("foo", rn.Text); + Assert.Equal("PERSON", rn.EntityType); + } + + [Fact] + public void CheckWithLanguage() { + Name rn = new Name("foo").SetLanguage("eng"); + Assert.Equal("foo", rn.Text); + Assert.Equal("eng", rn.Language); + } + + [Fact] + public void CheckWithScript() { + Name rn = new Name("foo").SetScript("zho"); + Assert.Equal("foo", rn.Text); + Assert.Equal("zho", rn.Script); + } + + [Fact] + public void CheckAll() { + Name rn = new Name("foo") + .SetEntityType("PERSON") + .SetLanguage("eng") + .SetScript("zho"); + Assert.Equal("foo", rn.Text); + Assert.Equal("PERSON", rn.EntityType); + Assert.Equal("eng", rn.Language); + Assert.Equal("zho", rn.Script); + } + } +} diff --git a/tests/TestNameTranslation.cs b/tests/NameTranslationTests.cs old mode 100755 new mode 100644 similarity index 94% rename from tests/TestNameTranslation.cs rename to tests/NameTranslationTests.cs index a1c2c60..164e277 --- a/tests/TestNameTranslation.cs +++ b/tests/NameTranslationTests.cs @@ -1,41 +1,41 @@ -using Rosette.Api.Endpoints; - -namespace Rosette.Api.Tests -{ - public class TestNameTranslation - { - [Fact] - public void CheckBasicUsage() { - NameTranslation n = new NameTranslation("foo"); - Assert.Equal("foo", n.Name); - Assert.Equal("eng", n.TargetLanguage); - Assert.Empty(n.EntityType); - Assert.Empty(n.SourceLanguageOfOrigin); - Assert.Empty(n.SourceLanguageOfUse); - Assert.Empty(n.SourceScript); - Assert.Empty(n.TargetScheme); - Assert.Empty(n.TargetScript); - } - - [Fact] - public void CheckAllUsage() { - NameTranslation n = new NameTranslation("foo") - .SetEntityType("PERSON") - .SetSourceLanguageOfOrigin("eng") - .SetSourceLanguageOfUse("eng") - .SetSourceScript("zho") - .SetTargetLanguage("spa") - .SetTargetScheme("BGN") - .SetTargetScript("eng"); - Assert.Equal("foo", n.Name); - Assert.Equal("spa", n.TargetLanguage); - Assert.Equal("PERSON", n.EntityType); - Assert.Equal("eng", n.SourceLanguageOfOrigin); - Assert.Equal("eng", n.SourceLanguageOfUse); - Assert.Equal("zho", n.SourceScript); - Assert.Equal("BGN", n.TargetScheme); - Assert.Equal("eng", n.TargetScript); - } - - } -} +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class NameTranslationTests + { + [Fact] + public void CheckBasicUsage() { + NameTranslation n = new NameTranslation("foo"); + Assert.Equal("foo", n.Name); + Assert.Equal("eng", n.TargetLanguage); + Assert.Empty(n.EntityType); + Assert.Empty(n.SourceLanguageOfOrigin); + Assert.Empty(n.SourceLanguageOfUse); + Assert.Empty(n.SourceScript); + Assert.Empty(n.TargetScheme); + Assert.Empty(n.TargetScript); + } + + [Fact] + public void CheckAllUsage() { + NameTranslation n = new NameTranslation("foo") + .SetEntityType("PERSON") + .SetSourceLanguageOfOrigin("eng") + .SetSourceLanguageOfUse("eng") + .SetSourceScript("zho") + .SetTargetLanguage("spa") + .SetTargetScheme("BGN") + .SetTargetScript("eng"); + Assert.Equal("foo", n.Name); + Assert.Equal("spa", n.TargetLanguage); + Assert.Equal("PERSON", n.EntityType); + Assert.Equal("eng", n.SourceLanguageOfOrigin); + Assert.Equal("eng", n.SourceLanguageOfUse); + Assert.Equal("zho", n.SourceScript); + Assert.Equal("BGN", n.TargetScheme); + Assert.Equal("eng", n.TargetScript); + } + + } +} diff --git a/tests/RelationshipsTests.cs b/tests/RelationshipsTests.cs new file mode 100644 index 0000000..9c73dc2 --- /dev/null +++ b/tests/RelationshipsTests.cs @@ -0,0 +1,39 @@ +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class RelationshipsTests + { + [Fact] + public void CheckBasicUsage() + { + string text = "John works at Microsoft."; + Relationships r = new Relationships(text); + + Assert.Equal("relationships", r.Endpoint); + Assert.Equal(text, r.Content); + } + + [Fact] + public void CheckWithLanguage() + { + Relationships r = new Relationships("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", r.Language); + } + + [Fact] + public void CheckFluentAPI() + { + Relationships r = new Relationships("Sample relationship text.") + .SetLanguage("eng") + .SetGenre("news") + .SetOption("accuracy", "high"); + + Assert.Equal("eng", r.Language); + Assert.Equal("news", r.Genre); + Assert.Equal("high", r.Options["accuracy"]); + } + } +} \ No newline at end of file diff --git a/tests/TestRosetteResponse.cs b/tests/ResponseTests.cs old mode 100755 new mode 100644 similarity index 92% rename from tests/TestRosetteResponse.cs rename to tests/ResponseTests.cs index af8928e..1348bcb --- a/tests/TestRosetteResponse.cs +++ b/tests/ResponseTests.cs @@ -1,28 +1,28 @@ -using Rosette.Api.Models; -using System.Net; -using System.Text.Json; - -namespace Rosette.Api.Tests -{ - public class TestRosetteResponse - { - [Fact] - public void CheckStatusOK() { - Dictionary data = new Dictionary { - { "content", "Some sample content" }, - { "language", "eng" } - }; - string json = JsonSerializer.Serialize(data); - - HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.OK); - msg.Content = new StringContent(json); - msg.Headers.Add("Test-Header", "Test Header Content"); - - Response response = new Response(msg); - - Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); - Assert.Equal(json, response.ContentAsJson()); - - } - } -} +using Rosette.Api.Models; +using System.Net; +using System.Text.Json; + +namespace Rosette.Api.Tests +{ + public class ResponseTests + { + [Fact] + public void CheckStatusOK() { + Dictionary data = new Dictionary { + { "content", "Some sample content" }, + { "language", "eng" } + }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new HttpResponseMessage(HttpStatusCode.OK); + msg.Content = new StringContent(json); + msg.Headers.Add("Test-Header", "Test Header Content"); + + Response response = new Response(msg); + + Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + Assert.Equal(json, response.ContentAsJson()); + + } + } +} diff --git a/tests/SentencesTests.cs b/tests/SentencesTests.cs new file mode 100644 index 0000000..892e669 --- /dev/null +++ b/tests/SentencesTests.cs @@ -0,0 +1,37 @@ +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class SentencesTests + { + [Fact] + public void CheckBasicUsage() + { + string text = "This is the first sentence. This is the second sentence."; + Sentences s = new Sentences(text); + + Assert.Equal("sentences", s.Endpoint); + Assert.Equal(text, s.Content); + } + + [Fact] + public void CheckWithLanguage() + { + Sentences s = new Sentences("Sample text.") + .SetLanguage("eng"); + + Assert.Equal("eng", s.Language); + } + + [Fact] + public void CheckFluentAPI() + { + Sentences s = new Sentences("Text content.") + .SetLanguage("eng") + .SetGenre("news"); + + Assert.Equal("eng", s.Language); + Assert.Equal("news", s.Genre); + } + } +} \ No newline at end of file diff --git a/tests/SentimentTests.cs b/tests/SentimentTests.cs new file mode 100644 index 0000000..614829f --- /dev/null +++ b/tests/SentimentTests.cs @@ -0,0 +1,48 @@ +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class SentimentTests + { + [Fact] + public void CheckBasicUsage() + { + Sentiment s = new Sentiment("This is a great product!"); + + Assert.Equal("sentiment", s.Endpoint); + Assert.Equal("This is a great product!", s.Content); + } + + [Fact] + public void CheckWithLanguage() + { + Sentiment s = new Sentiment("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", s.Language); + Assert.Equal("Sample text", s.Content); + } + + [Fact] + public void CheckWithGenre() + { + Sentiment s = new Sentiment("Sample text") + .SetGenre("social-media"); + + Assert.Equal("social-media", s.Genre); + } + + [Fact] + public void CheckFluentAPI() + { + Sentiment s = new Sentiment("Great service!") + .SetLanguage("eng") + .SetGenre("review") + .SetOption("sentiment.threshold", 0.5); + + Assert.Equal("eng", s.Language); + Assert.Equal("review", s.Genre); + Assert.Equal(0.5, s.Options["sentiment.threshold"]); + } + } +} \ No newline at end of file diff --git a/tests/SimilarTermsTests.cs b/tests/SimilarTermsTests.cs new file mode 100644 index 0000000..b410a45 --- /dev/null +++ b/tests/SimilarTermsTests.cs @@ -0,0 +1,37 @@ +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class SimilarTermsTests + { + [Fact] + public void CheckBasicUsage() + { + SimilarTerms st = new SimilarTerms("happy"); + + Assert.Equal("semantics/similar", st.Endpoint); + Assert.Equal("happy", st.Content); + } + + [Fact] + public void CheckWithLanguage() + { + SimilarTerms st = new SimilarTerms("computer") + .SetLanguage("eng"); + + Assert.Equal("eng", st.Language); + Assert.Equal("computer", st.Content); + } + + [Fact] + public void CheckFluentAPI() + { + SimilarTerms st = new SimilarTerms("innovation") + .SetLanguage("eng") + .SetOption("count", 10); + + Assert.Equal("eng", st.Language); + Assert.Equal(10, st.Options["count"]); + } + } +} \ No newline at end of file diff --git a/tests/TokensTests.cs b/tests/TokensTests.cs new file mode 100644 index 0000000..3dfe7ee --- /dev/null +++ b/tests/TokensTests.cs @@ -0,0 +1,36 @@ +using Rosette.Api.Endpoints; + +namespace Rosette.Api.Tests +{ + public class TokensTests + { + [Fact] + public void CheckBasicUsage() + { + Tokens t = new Tokens("This is sample text"); + + Assert.Equal("tokens", t.Endpoint); + Assert.Equal("This is sample text", t.Content); + } + + [Fact] + public void CheckWithLanguage() + { + Tokens t = new Tokens("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", t.Language); + } + + [Fact] + public void CheckFluentAPI() + { + Tokens t = new Tokens("Sample text") + .SetLanguage("jpn") + .SetGenre("social-media"); + + Assert.Equal("jpn", t.Language); + Assert.Equal("social-media", t.Genre); + } + } +} \ No newline at end of file diff --git a/tests/TestForValidEndpoint.cs b/tests/ValidEndpointTests.cs old mode 100755 new mode 100644 similarity index 93% rename from tests/TestForValidEndpoint.cs rename to tests/ValidEndpointTests.cs index a985c28..a5f01b2 --- a/tests/TestForValidEndpoint.cs +++ b/tests/ValidEndpointTests.cs @@ -1,195 +1,207 @@ -using Rosette.Api.Endpoints; -using Rosette.Api.Models; - -namespace Rosette.Api.Tests -{ - public class TestForValidEndpoint - { - - [Fact] - public void AddressSimilarityEndpoint() - { - var a = new UnfieldedAddressRecord { Address = "foo" }; - - AddressSimilarity asim = new AddressSimilarity(a,a); - Assert.Equal("address-similarity", asim.Endpoint); - } - - [Fact] - public void CategoriesEndpoint() - { - Categories c = new Categories("foo"); - - Assert.Equal("categories", c.Endpoint); - Assert.Equal("foo", c.Content); - } - - [Fact] - public void EntitiesEndpoint() - { - Entities e = new Entities("foo"); - Assert.Equal("entities", e.Endpoint); - Assert.Equal("foo", e.Content); - } - - [Fact] - public void EventsEndpoint() - { - Events e = new Events("foo"); - Assert.Equal("events", e.Endpoint); - Assert.Equal("foo", e.Content); - } - - [Fact] - public void InfoEndpoint() - { - Info i = new Info(); - Assert.Equal("info", i.Endpoint); - } - - [Fact] - public void LanguageEndpoint() - { - Language l = new Language("foo"); - - Assert.Equal("language", l.Endpoint); - Assert.Equal("foo", l.Content); - } - - [Theory] - [InlineData(MorphologyFeature.complete)] - [InlineData(MorphologyFeature.compoundComponents)] - [InlineData(MorphologyFeature.hanReadings)] - [InlineData(MorphologyFeature.lemmas)] - [InlineData(MorphologyFeature.partsOfSpeech)] - public void MorphologyEndpoint(MorphologyFeature feature) - { - Morphology m = new Morphology("foo", feature); - - Assert.Equal("morphology/" + m.FeatureAsString(feature), m.Endpoint); - Assert.Equal("foo", m.Content); - } - - [Fact] - public void NameSimilarityEndpoint() - { - Name rn = new Name("foo"); - NameSimilarity ns = new NameSimilarity(rn, rn); - Assert.Equal("name-similarity", ns.Endpoint); - } - - [Fact] - public void NameTranslationEndpoint() - { - NameTranslation nt = new NameTranslation("foo"); - - Assert.Equal("name-translation", nt.Endpoint); - } - - [Fact] - public void PingEndpoint() - { - Ping p = new Ping(); - Assert.Equal("ping", p.Endpoint); - } - - [Fact] - public void RecordSimilarityEndpoint() - { - var fields = new Dictionary(); - var properties = new RecordSimilarityProperties(); - var records = new RecordSimilarityRecords(); - - RecordSimilarity rs = new RecordSimilarity(fields, properties, records); - - Assert.Equal("record-similarity", rs.Endpoint); - } - - [Fact] - public void RelationshipsEndpoint() - { - Relationships r = new Relationships("foo"); - - Assert.Equal("relationships", r.Endpoint); - Assert.Equal("foo", r.Content); - } - - [Fact] - public void SemanticVectorsEndpoint() - { - SemanticsVector s = new SemanticsVector("foo"); - Assert.Equal("semantics/vector", s.Endpoint); - Assert.Equal("foo", s.Content); - } - - [Fact] - public void SentencesEndpoint() - { - Sentences s = new Sentences("foo"); - Assert.Equal("sentences", s.Endpoint); - Assert.Equal("foo", s.Content); - } - - [Fact] - public void SentimentEndpoint() - { - Sentiment s = new Sentiment("foo"); - Assert.Equal("sentiment", s.Endpoint); - Assert.Equal("foo", s.Content); - } - - [Fact] - public void SimilarTermsEndpoint() - { - SimilarTerms st = new SimilarTerms("foo"); - - Assert.Equal("semantics/similar", st.Endpoint); - Assert.Equal("foo", st.Content); - } - - [Fact] - public void SyntaxDependenciesEndpoint() - { - SyntaxDependencies s = new SyntaxDependencies("foo"); - - Assert.Equal("syntax/dependencies", s.Endpoint); - Assert.Equal("foo", s.Content); - } - - [Fact] - public void TextEmbeddingEndpoint() - { - TextEmbedding t = new TextEmbedding("foo"); - - Assert.Equal("text-embedding", t.Endpoint); - Assert.Equal("foo", t.Content); - } - - [Fact] - public void TokensEndpoint() - { - Tokens t = new Tokens("foo"); - - Assert.Equal("tokens", t.Endpoint); - Assert.Equal("foo", t.Content); - } - - [Fact] - public void TopicsEndpoint() - { - Topics t = new Topics("foo"); - - Assert.Equal("topics", t.Endpoint); - Assert.Equal("foo", t.Content); - } - - [Fact] - public void TransliterationEndpoint() - { - Transliteration t = new Transliteration("foo"); - - Assert.Equal("transliteration", t.Endpoint); - Assert.Equal("foo", t.Content); - } - } +using Rosette.Api.Endpoints; +using Rosette.Api.Models; + +namespace Rosette.Api.Tests +{ + public class ValidEndpointTests + { + + [Fact] + public void AddressSimilarityEndpoint() + { + var a = new UnfieldedAddressRecord { Address = "foo" }; + + AddressSimilarity asim = new AddressSimilarity(a,a); + Assert.Equal("address-similarity", asim.Endpoint); + } + + [Fact] + public void CategoriesEndpoint() + { + Categories c = new Categories("foo"); + + Assert.Equal("categories", c.Endpoint); + Assert.Equal("foo", c.Content); + } + + [Fact] + public void EntitiesEndpoint() + { + Entities e = new Entities("foo"); + Assert.Equal("entities", e.Endpoint); + Assert.Equal("foo", e.Content); + } + + [Fact] + public void EventsEndpoint() + { + Events e = new Events("foo"); + Assert.Equal("events", e.Endpoint); + Assert.Equal("foo", e.Content); + } + + [Fact] + public void InfoEndpoint() + { + Info i = new Info(); + Assert.Equal("info", i.Endpoint); + } + + [Fact] + public void LanguageEndpoint() + { + Language l = new Language("foo"); + + Assert.Equal("language", l.Endpoint); + Assert.Equal("foo", l.Content); + } + + [Theory] + [InlineData(MorphologyFeature.complete)] + [InlineData(MorphologyFeature.compoundComponents)] + [InlineData(MorphologyFeature.hanReadings)] + [InlineData(MorphologyFeature.lemmas)] + [InlineData(MorphologyFeature.partsOfSpeech)] + public void MorphologyEndpoint(MorphologyFeature feature) + { + Morphology m = new Morphology("foo", feature); + + Assert.Equal("morphology/" + m.FeatureAsString(feature), m.Endpoint); + Assert.Equal("foo", m.Content); + } + + [Fact] + public void NameDeduplicationEndpoint() + { + List names = [ + new Name("foo"), + new Name("bar") + ]; + NameDeduplication nd = new NameDeduplication(names); + + Assert.Equal("name-deduplication", nd.Endpoint); + } + + [Fact] + public void NameSimilarityEndpoint() + { + Name rn = new Name("foo"); + NameSimilarity ns = new NameSimilarity(rn, rn); + Assert.Equal("name-similarity", ns.Endpoint); + } + + [Fact] + public void NameTranslationEndpoint() + { + NameTranslation nt = new NameTranslation("foo"); + + Assert.Equal("name-translation", nt.Endpoint); + } + + [Fact] + public void PingEndpoint() + { + Ping p = new Ping(); + Assert.Equal("ping", p.Endpoint); + } + + [Fact] + public void RecordSimilarityEndpoint() + { + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new RecordSimilarity(fields, properties, records); + + Assert.Equal("record-similarity", rs.Endpoint); + } + + [Fact] + public void RelationshipsEndpoint() + { + Relationships r = new Relationships("foo"); + + Assert.Equal("relationships", r.Endpoint); + Assert.Equal("foo", r.Content); + } + + [Fact] + public void SemanticVectorsEndpoint() + { + SemanticsVector s = new SemanticsVector("foo"); + Assert.Equal("semantics/vector", s.Endpoint); + Assert.Equal("foo", s.Content); + } + + [Fact] + public void SentencesEndpoint() + { + Sentences s = new Sentences("foo"); + Assert.Equal("sentences", s.Endpoint); + Assert.Equal("foo", s.Content); + } + + [Fact] + public void SentimentEndpoint() + { + Sentiment s = new Sentiment("foo"); + Assert.Equal("sentiment", s.Endpoint); + Assert.Equal("foo", s.Content); + } + + [Fact] + public void SimilarTermsEndpoint() + { + SimilarTerms st = new SimilarTerms("foo"); + + Assert.Equal("semantics/similar", st.Endpoint); + Assert.Equal("foo", st.Content); + } + + [Fact] + public void SyntaxDependenciesEndpoint() + { + SyntaxDependencies s = new SyntaxDependencies("foo"); + + Assert.Equal("syntax/dependencies", s.Endpoint); + Assert.Equal("foo", s.Content); + } + + [Fact] + public void TextEmbeddingEndpoint() + { + TextEmbedding t = new TextEmbedding("foo"); + + Assert.Equal("text-embedding", t.Endpoint); + Assert.Equal("foo", t.Content); + } + + [Fact] + public void TokensEndpoint() + { + Tokens t = new Tokens("foo"); + + Assert.Equal("tokens", t.Endpoint); + Assert.Equal("foo", t.Content); + } + + [Fact] + public void TopicsEndpoint() + { + Topics t = new Topics("foo"); + + Assert.Equal("topics", t.Endpoint); + Assert.Equal("foo", t.Content); + } + + [Fact] + public void TransliterationEndpoint() + { + Transliteration t = new Transliteration("foo"); + + Assert.Equal("transliteration", t.Endpoint); + Assert.Equal("foo", t.Content); + } + } } \ No newline at end of file From 959c0ecf6bc1ba7012ebe898911812842ab82ce0 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 17 Feb 2026 09:51:43 -0500 Subject: [PATCH 08/10] BX-68163: Updates the Entitites endpoint to ignore genre --- rosette_api/Endpoints/Entities.cs | 12 ++++++++++++ tests/EntitiesTests.cs | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/rosette_api/Endpoints/Entities.cs b/rosette_api/Endpoints/Entities.cs index 9a06295..ccfb875 100644 --- a/rosette_api/Endpoints/Entities.cs +++ b/rosette_api/Endpoints/Entities.cs @@ -11,4 +11,16 @@ public class Entities : ContentEndpointBase public Entities(object content) : base("entities", content) { } + + /// + /// SetGenre is not supported for the Entities endpoint and will be ignored. + /// This override prevents the genre from being set. + /// + /// document genre (ignored) + /// Updated endpoint instance + public new Entities SetGenre(string genre) + { + // Genre is not supported for Entities endpoint - ignore the value + return this; + } } diff --git a/tests/EntitiesTests.cs b/tests/EntitiesTests.cs index 7358616..600d40a 100644 --- a/tests/EntitiesTests.cs +++ b/tests/EntitiesTests.cs @@ -22,7 +22,7 @@ public void CheckWithLanguageAndGenre() .SetGenre("social-media"); Assert.Equal("eng", e.Language); - Assert.Equal("social-media", e.Genre); + Assert.Equal("", e.Genre); } [Fact] @@ -44,7 +44,6 @@ public void CheckFluentAPI() .SetUrlParameter("output", "rosette"); Assert.Equal("eng", e.Language); - Assert.Equal("news", e.Genre); Assert.True((bool)e.Options["linkEntities"]); Assert.Equal("rosette", e.UrlParameters["output"]); } From 48ac9963a4ffa2fc2afc46f54900ef5bbb5b6975 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Thu, 19 Feb 2026 14:07:09 -0500 Subject: [PATCH 09/10] BX-68163: Updates to examples README --- examples/README.md | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/examples/README.md b/examples/README.md index f2321ef..530f591 100644 --- a/examples/README.md +++ b/examples/README.md @@ -21,27 +21,25 @@ Here is one way to run the examples. - Set up the environment. ``` - apt update - apt install -y wget + apt-get update -y + apt-get install -y wget + apt-get install -y curl + apt-get install -y libicu76 + apt-get install -y libgssapi-krb5-2 - wget https://packages.microsoft.com/config/debian/13/packages-microsoft-prod.deb -O packages-microsoft-prod.deb - dpkg -i packages-microsoft-prod.deb - - apt update - apt install -y dotnet-sdk-10.0 - - dotnet --version - dotnet --list-sdks - dotnet --list-runtimes + wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh + chmod +x ./dotnet-install.sh + ./dotnet-install.sh --version latest + export PATH="$PATH:/root/.dotnet" ``` - Build the package from source. ``` cd /dotnet - dotnet restore Rosette.Api.slnx - dotnet build /p:Configuration=Release Rosette.Api.slnx - dotnet build /p:Configuration=Debug Rosette.Api.slnx + dotnet restore rosette_api.slnx + dotnet build /p:Configuration=Release rosette_api.slnx + dotnet build /p:Configuration=Debug rosette_api.slnx ``` @@ -60,7 +58,7 @@ Here is one way to run the examples. dotnet new console --framework net10.0 cp ../Language.cs ./Program.cs - dotnet add reference ../../rosette_api/Rosette.Api.csproj + dotnet add reference ../../rosette_api/rosette_api.csproj ``` From 441c43e677ebc6fb0a4c8287d2a13a0edb39afae Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Fri, 20 Feb 2026 19:55:40 -0500 Subject: [PATCH 10/10] BX-68163: Addresses PR comments --- rosette_api/Endpoints/NameTranslation.cs | 21 ++++++ rosette_api/Endpoints/TextEmbedding.cs | 67 ++----------------- rosette_api/Models/FieldedAddressRecord.cs | 10 +-- .../RecordSimilarityFieldConverter.cs | 52 ++------------ 4 files changed, 35 insertions(+), 115 deletions(-) diff --git a/rosette_api/Endpoints/NameTranslation.cs b/rosette_api/Endpoints/NameTranslation.cs index 6586193..c0f4c26 100644 --- a/rosette_api/Endpoints/NameTranslation.cs +++ b/rosette_api/Endpoints/NameTranslation.cs @@ -13,6 +13,7 @@ public class NameTranslation : EndpointBase private const string TARGET_LANGUAGE = "targetLanguage"; private const string TARGET_SCHEME = "targetScheme"; private const string TARGET_SCRIPT = "targetScript"; + private const string MAXIMUM_RESULTS = "maximumResults"; public NameTranslation(string name, string targetLanguage="eng") : base("name-translation") { SetName(name); @@ -139,6 +140,26 @@ public NameTranslation SetTargetScript(string targetScript) { string.Empty; } + /// + /// SetMaximumResults sets the optional maximum number of results to return + /// + /// maximum number of results + /// this + public NameTranslation SetMaximumResults(string maximumResults) + { + Params[MAXIMUM_RESULTS] = maximumResults; + + return this; + } + + public string? MaximumResults { get => + Params.ContainsKey(MAXIMUM_RESULTS) ? + Params[MAXIMUM_RESULTS].ToString() : + string.Empty; + } + + + public Response Call(ApiClient api) { return Funcs.PostCall(api); } diff --git a/rosette_api/Endpoints/TextEmbedding.cs b/rosette_api/Endpoints/TextEmbedding.cs index 3e785e6..7b809df 100644 --- a/rosette_api/Endpoints/TextEmbedding.cs +++ b/rosette_api/Endpoints/TextEmbedding.cs @@ -1,72 +1,15 @@ using Rosette.Api.Endpoints.Core; -using Rosette.Api.Models; namespace Rosette.Api.Endpoints { - public class TextEmbedding : EndpointBase { + public class TextEmbedding : ContentEndpointBase + { /// /// TextEmbeddingEndpoint returns the embedding for the input text /// /// text, Uri object or FileStream - public TextEmbedding(object content) : base("text-embedding") { - SetContent(content); - } - /// - /// SetContent sets the content to be reviewed - /// - /// text, Uri object or FileStream - /// update TextEmbedding endpoint - public TextEmbedding SetContent(object content) { - Funcs.Content = content; - - return this; - } - - public object Content => Funcs.Content; - /// - /// SetLanguage sets the optional ISO 639-3 language code - /// - /// ISO 639-3 language code - /// updated TextEmbedding endpoint - public TextEmbedding SetLanguage(string language) { - Funcs.Language = language; - - return this; - } - - public string? Language => Funcs.Language; - /// - /// SetGenre sets the optional document genre, e.g. social-media - /// - /// document genre - /// updated TextEmbedding endpoint - public TextEmbedding SetGenre(string genre) { - Funcs.Genre = genre; - - return this; - } - - public string? Genre => Funcs.Genre; - /// - /// SetFileContentType sets the content type of the file contents. Note that - /// it only applies when the content is a filename - /// - /// Content-Type - /// updated TextEmbedding endpoint - public TextEmbedding SetFileContentType(string contentType) { - Funcs.FileContentType = contentType; - - return this; - } - public string FileContentType => Funcs.FileContentType; - public string Filename => Funcs.Filename; - /// - /// Call passes the data to the server and returns the response - /// - /// RosetteAPI object - /// RosetteResponse - public Response Call(ApiClient api) { - return Funcs.PostCall(api); + public TextEmbedding(object content) : base("text-embedding", content) + { } } -} +} \ No newline at end of file diff --git a/rosette_api/Models/FieldedAddressRecord.cs b/rosette_api/Models/FieldedAddressRecord.cs index d9702ce..51eeeef 100644 --- a/rosette_api/Models/FieldedAddressRecord.cs +++ b/rosette_api/Models/FieldedAddressRecord.cs @@ -11,7 +11,7 @@ public class FieldedAddressRecord : AddressField [JsonPropertyName("house")] public string? House { get; set; } - [JsonPropertyName("house_number")] + [JsonPropertyName("houseNumber")] public string? HouseNumber { get; set; } [JsonPropertyName("road")] @@ -32,7 +32,7 @@ public class FieldedAddressRecord : AddressField [JsonPropertyName("suburb")] public string? Suburb { get; set; } - [JsonPropertyName("city_district")] + [JsonPropertyName("cityDistrict")] public string? CityDistrict { get; set; } [JsonPropertyName("city")] @@ -41,19 +41,19 @@ public class FieldedAddressRecord : AddressField [JsonPropertyName("island")] public string? Island { get; set; } - [JsonPropertyName("state_district")] + [JsonPropertyName("stateDistrict")] public string? StateDistrict { get; set; } [JsonPropertyName("state")] public string? State { get; set; } - [JsonPropertyName("country_region")] + [JsonPropertyName("countryRegion")] public string? CountryRegion { get; set; } [JsonPropertyName("country")] public string? Country { get; set; } - [JsonPropertyName("world_region")] + [JsonPropertyName("worldRegion")] public string? WorldRegion { get; set; } [JsonPropertyName("postcode")] diff --git a/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs b/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs index 0c3b8b0..9c83035 100644 --- a/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs +++ b/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs @@ -12,54 +12,10 @@ public class RecordSimilarityFieldConverter : JsonConverter new StringRecord { Text = reader.GetString() ?? string.Empty }, - JsonTokenType.Number => new NumberRecord(reader.GetDouble()), - JsonTokenType.True or JsonTokenType.False => new BooleanRecord(reader.GetBoolean()), - JsonTokenType.StartObject => DeserializeObject(ref reader, options), - _ => null - }; - } - - private static RecordSimilarityField? DeserializeObject(ref Utf8JsonReader reader, JsonSerializerOptions options) - { - using var doc = JsonDocument.ParseValue(ref reader); - var root = doc.RootElement; - - // Check for known property patterns to determine type - if (root.TryGetProperty("text", out _)) - { - if (root.TryGetProperty("language", out _) || - root.TryGetProperty("entityType", out _)) - { - return JsonSerializer.Deserialize(root.GetRawText(), options); - } - return JsonSerializer.Deserialize(root.GetRawText(), options); - } - - if (root.TryGetProperty("date", out _)) - { - if (root.TryGetProperty("format", out _)) - { - return JsonSerializer.Deserialize(root.GetRawText(), options); - } - return JsonSerializer.Deserialize(root.GetRawText(), options); - } - - if (root.TryGetProperty("address", out _)) - { - return JsonSerializer.Deserialize(root.GetRawText(), options); - } - - if (root.TryGetProperty("house", out _) || root.TryGetProperty("city", out _)) - { - return JsonSerializer.Deserialize(root.GetRawText(), options); - } - - // Default to unknown field - return new UnknownFieldRecord(JsonNode.Parse(root.GetRawText())); + throw new NotSupportedException( + "Deserialization of RecordSimilarityField is not supported. " + + "Unfielded records cannot be reliably reconstructed from JSON responses " + + "because type information is lost. Please work with the raw JSON response data instead."); } public override void Write(Utf8JsonWriter writer, RecordSimilarityField value, JsonSerializerOptions options)