diff --git a/My1kWordsEe/Components/Pages/Word.razor b/My1kWordsEe/Components/Pages/Word.razor
index f1b8d05d..1c6ff41a 100644
--- a/My1kWordsEe/Components/Pages/Word.razor
+++ b/My1kWordsEe/Components/Pages/Word.razor
@@ -8,8 +8,8 @@
@using My1kWordsEe.Services.Cqs;
@inject IJSRuntime JS
-@inject EnsureWordCommand EnsureWordCommand
-@inject CreateSampleCommand CreateSampleCommand
+@inject GetOrAddSampleWordCommand EnsureWordCommand
+@inject AddSampleSentenceCommand CreateSampleCommand
@code {
[Parameter]
diff --git a/My1kWordsEe/Models/Ee1kWords.cs b/My1kWordsEe/Models/Ee1kWords.cs
index 21ccb3f3..ea842826 100644
--- a/My1kWordsEe/Models/Ee1kWords.cs
+++ b/My1kWordsEe/Models/Ee1kWords.cs
@@ -4,6 +4,9 @@
namespace My1kWordsEe.Models
{
+ ///
+ /// Represents a collection of 1k most common Estonian words.
+ ///
public class Ee1kWords
{
public Ee1kWords()
@@ -32,7 +35,7 @@ public Ee1kWords WithSearch(string search)
Search = search,
SelectedWords = AllWords
.Where(w => w.Value.Contains(search, StringComparison.InvariantCultureIgnoreCase)
- || _allWordsDiacriticsFree[w.Value].Contains(search, StringComparison.InvariantCultureIgnoreCase))
+ || AllWordsDiacriticsFree[w.Value].Contains(search, StringComparison.InvariantCultureIgnoreCase))
.ToArray()
};
}
@@ -56,10 +59,10 @@ public Ee1kWords WithSelectedWord(string selectedWord)
public static readonly EeWord[] AllWords = Load1kEeWords();
- private static readonly IReadOnlyDictionary _allWordsDiacriticsFree =
+ private static readonly IReadOnlyDictionary AllWordsDiacriticsFree =
AllWords.ToDictionary(w => w.Value, q => RemoveDiacritics(q.Value));
- static string RemoveDiacritics(string stIn)
+ private static string RemoveDiacritics(string stIn)
{
string stFormD = stIn.Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
diff --git a/My1kWordsEe/Models/EeWord.cs b/My1kWordsEe/Models/EeWord.cs
index 2cc710eb..1f79c5d5 100644
--- a/My1kWordsEe/Models/EeWord.cs
+++ b/My1kWordsEe/Models/EeWord.cs
@@ -2,6 +2,10 @@
namespace My1kWordsEe.Models
{
+ ///
+ /// currently serves as view model for the 1k words page
+ /// todo: must be unified with
+ ///
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(EePartOfSpeech))]
public record EeWord
diff --git a/My1kWordsEe/Models/PartsOfSpeech.cs b/My1kWordsEe/Models/PartsOfSpeech.cs
index 31eede81..d3139694 100644
--- a/My1kWordsEe/Models/PartsOfSpeech.cs
+++ b/My1kWordsEe/Models/PartsOfSpeech.cs
@@ -54,6 +54,9 @@ public enum EePartOfSpeech
Kaassõna
}
+ ///
+ /// Standard English parts of speech
+ ///
public enum EnPartOfSpeech
{
Noun,
diff --git a/My1kWordsEe/Models/SampleSentence.cs b/My1kWordsEe/Models/SampleSentence.cs
index 3503635a..6c6adf45 100644
--- a/My1kWordsEe/Models/SampleSentence.cs
+++ b/My1kWordsEe/Models/SampleSentence.cs
@@ -1,15 +1,33 @@
namespace My1kWordsEe.Models
{
- public class SampleSentence
+ ///
+ /// Sample sentence illustrating the use of a give Estonian word
+ ///
+ public record SampleSentence
{
- public string EeWord { get; set; }
+ ///
+ /// Target Estonian word
+ ///
+ public string EeWord { get; init; }
- public string EeSentence { get; set; }
+ ///
+ /// Illustrative sentence in Estonian
+ ///
+ public string EeSentence { get; init; }
- public string EnSentence { get; set; }
+ ///
+ /// Translation of the illustrative sentence in English
+ ///
+ public string EnSentence { get; init; }
- public Uri EeAudioUrl { get; set; }
+ ///
+ /// Sentence spoken in Estonian
+ ///
+ public Uri EeAudioUrl { get; init; }
- public Uri ImageUrl { get; set; }
+ ///
+ /// Image associated with the sentence
+ ///
+ public Uri ImageUrl { get; init; }
}
}
diff --git a/My1kWordsEe/Models/SampleWord.cs b/My1kWordsEe/Models/SampleWord.cs
index 5bcbedc0..04edf74f 100644
--- a/My1kWordsEe/Models/SampleWord.cs
+++ b/My1kWordsEe/Models/SampleWord.cs
@@ -1,5 +1,8 @@
namespace My1kWordsEe.Models
{
+ ///
+ /// A word of the Estonian language with the respective translations and usage examples
+ ///
public record SampleWord
{
private readonly string eeWord = "";
@@ -21,7 +24,7 @@ public string EeWord
public string EnWord { get; init; }
///
- /// Alternatives to EnWord
+ /// Alternative translations to English
///
public string[] EnWords
{
@@ -29,10 +32,19 @@ public string[] EnWords
init => this.enWords = value ?? this.enWords;
}
+ ///
+ /// Explaining the word in English
+ ///
public string EnExplanation { get; init; }
+ ///
+ /// Sample pronunciation of the word
+ ///
public Uri EeAudioUrl { get; init; }
+ ///
+ /// Word usage examples
+ ///
public SampleSentence[] Samples
{
get => this.samples;
diff --git a/My1kWordsEe/Program.cs b/My1kWordsEe/Program.cs
index dc51ae2b..0e0955e4 100644
--- a/My1kWordsEe/Program.cs
+++ b/My1kWordsEe/Program.cs
@@ -41,11 +41,16 @@ public static void Main(string[] args)
}
builder.Services.AddSingleton(new StabilityAiService(stabilityAiKey));
- builder.Services.AddSingleton((p) => new OpenAiService(p.GetRequiredService>(), openAiKey));
- builder.Services.AddSingleton(new AzureBlobService(azureBlobConnectionString));
- builder.Services.AddSingleton(new TartuNlpService());
- builder.Services.AddSingleton();
- builder.Services.AddSingleton();
+ builder.Services.AddSingleton((p) => new OpenAiService(
+ p.GetRequiredService>(), openAiKey));
+ builder.Services.AddSingleton((p) => new AzureBlobService(
+ p.GetRequiredService>(), azureBlobConnectionString));
+ builder.Services.AddSingleton((p) => new TartuNlpService(
+ p.GetRequiredService>()));
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
// Add services to the container.
builder.Services.AddRazorComponents()
diff --git a/My1kWordsEe/Services/Cqs/AddAudioCommand.cs b/My1kWordsEe/Services/Cqs/AddAudioCommand.cs
new file mode 100644
index 00000000..df3184a6
--- /dev/null
+++ b/My1kWordsEe/Services/Cqs/AddAudioCommand.cs
@@ -0,0 +1,24 @@
+using CSharpFunctionalExtensions;
+
+using My1kWordsEe.Services.Db;
+
+namespace My1kWordsEe.Services.Cqs
+{
+ public class AddAudioCommand
+ {
+ private readonly TartuNlpService tartuNlpService;
+ private readonly AzureBlobService azureBlobService;
+
+ public AddAudioCommand(
+ TartuNlpService tartuNlpService,
+ AzureBlobService azureBlobService)
+ {
+ this.tartuNlpService = tartuNlpService;
+ this.azureBlobService = azureBlobService;
+ }
+
+ public Task> Invoke(string text) =>
+ this.tartuNlpService.GetSpeech(text).Bind(
+ this.azureBlobService.SaveAudio);
+ }
+}
diff --git a/My1kWordsEe/Services/Cqs/CreateSampleCommand.cs b/My1kWordsEe/Services/Cqs/AddSampleSentenceCommand.cs
similarity index 80%
rename from My1kWordsEe/Services/Cqs/CreateSampleCommand.cs
rename to My1kWordsEe/Services/Cqs/AddSampleSentenceCommand.cs
index 3dc11ac7..ff6075b8 100644
--- a/My1kWordsEe/Services/Cqs/CreateSampleCommand.cs
+++ b/My1kWordsEe/Services/Cqs/AddSampleSentenceCommand.cs
@@ -5,22 +5,22 @@
namespace My1kWordsEe.Services.Cqs
{
- public class CreateSampleCommand
+ public class AddSampleSentenceCommand
{
private readonly AzureBlobService azureBlobService;
private readonly OpenAiService openAiService;
- private readonly TartuNlpService tartuNlpService;
+ private readonly AddAudioCommand addAudioCommand;
private readonly StabilityAiService stabilityAiService;
- public CreateSampleCommand(
+ public AddSampleSentenceCommand(
AzureBlobService azureBlobService,
OpenAiService openAiService,
- TartuNlpService tartuNlpService,
+ AddAudioCommand createAudioCommand,
StabilityAiService stabilityAiService)
{
this.azureBlobService = azureBlobService;
this.openAiService = openAiService;
- this.tartuNlpService = tartuNlpService;
+ this.addAudioCommand = createAudioCommand;
this.stabilityAiService = stabilityAiService;
}
@@ -64,12 +64,11 @@ public async Task> Invoke(SampleWord word)
}
private Task> GenerateImage(Sentence sentence) =>
- this.openAiService.GetDallEPrompt(sentence.En)
- .Bind(this.stabilityAiService.GenerateImage)
- .Bind(image => Result.Of(this.azureBlobService.SaveImage(image)));
+ this.openAiService.GetDallEPrompt(sentence.En).Bind(
+ this.stabilityAiService.GenerateImage).Bind(
+ this.azureBlobService.SaveImage);
private Task> GenerateSpeech(Sentence sentence) =>
- this.tartuNlpService.GetSpeech(sentence.Ee)
- .Bind(speech => Result.Of(this.azureBlobService.SaveAudio(speech)));
+ this.addAudioCommand.Invoke(sentence.Ee);
}
}
\ No newline at end of file
diff --git a/My1kWordsEe/Services/Cqs/AddSampleWordCommand.cs b/My1kWordsEe/Services/Cqs/AddSampleWordCommand.cs
new file mode 100644
index 00000000..d261d259
--- /dev/null
+++ b/My1kWordsEe/Services/Cqs/AddSampleWordCommand.cs
@@ -0,0 +1,47 @@
+using CSharpFunctionalExtensions;
+
+using My1kWordsEe.Models;
+using My1kWordsEe.Services.Db;
+
+namespace My1kWordsEe.Services.Cqs
+{
+ public class AddSampleWordCommand
+ {
+ private readonly OpenAiService openAiService;
+ private readonly AzureBlobService azureBlobService;
+ private readonly AddAudioCommand addAudioCommand;
+
+ public AddSampleWordCommand(
+ OpenAiService openAiService,
+ AzureBlobService azureBlobService,
+ AddAudioCommand createAudioCommand)
+ {
+ this.azureBlobService = azureBlobService;
+ this.openAiService = openAiService;
+ this.addAudioCommand = createAudioCommand;
+ }
+
+ public async Task> Invoke(string eeWord)
+ {
+ (await openAiService.GetWordMetadata(eeWord)).Deconstruct(
+ out bool _,
+ out bool isAiFailure,
+ out SampleWord sampleWord,
+ out string aiError);
+
+ if (isAiFailure)
+ {
+ return Result.Failure(aiError);
+ }
+
+ (await this.addAudioCommand.Invoke(eeWord)).Deconstruct(
+ out bool isAudioSaved,
+ out bool _,
+ out Uri audioUri);
+
+ sampleWord = isAudioSaved ? sampleWord with { EeAudioUrl = audioUri } : sampleWord;
+
+ return (await azureBlobService.SaveWordData(sampleWord)).Bind(_ => Result.Of(sampleWord));
+ }
+ }
+}
diff --git a/My1kWordsEe/Services/Cqs/EnsureWordCommand.cs b/My1kWordsEe/Services/Cqs/EnsureWordCommand.cs
deleted file mode 100644
index 7a91d787..00000000
--- a/My1kWordsEe/Services/Cqs/EnsureWordCommand.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using CSharpFunctionalExtensions;
-
-using My1kWordsEe.Models;
-using My1kWordsEe.Services.Db;
-
-namespace My1kWordsEe.Services.Cqs
-{
- public class EnsureWordCommand
- {
- private readonly AzureBlobService azureBlobService;
- private readonly OpenAiService openAiService;
-
- public EnsureWordCommand(
- AzureBlobService azureBlobService,
- OpenAiService openAiService)
- {
- this.azureBlobService = azureBlobService;
- this.openAiService = openAiService;
- }
-
- public async Task> Invoke(string eeWord)
- {
- var existingRecord = await azureBlobService.GetWordData(eeWord);
-
- if (existingRecord.IsSuccess)
- {
- return existingRecord;
- }
-
- var sampleWord = await openAiService.GetWordMetadata(eeWord);
-
- if (sampleWord.IsSuccess)
- {
- // generate audio
- await azureBlobService.SaveWordData(sampleWord.Value);
- }
-
- return sampleWord;
- }
- }
-}
\ No newline at end of file
diff --git a/My1kWordsEe/Services/Cqs/GetOrAddSampleWordCommand.cs b/My1kWordsEe/Services/Cqs/GetOrAddSampleWordCommand.cs
new file mode 100644
index 00000000..628b11c1
--- /dev/null
+++ b/My1kWordsEe/Services/Cqs/GetOrAddSampleWordCommand.cs
@@ -0,0 +1,42 @@
+using CSharpFunctionalExtensions;
+
+using My1kWordsEe.Models;
+using My1kWordsEe.Services.Db;
+
+namespace My1kWordsEe.Services.Cqs
+{
+ public class GetOrAddSampleWordCommand
+ {
+ private readonly AzureBlobService azureBlobService;
+ private readonly AddSampleWordCommand addSampleWordCommand;
+
+ public GetOrAddSampleWordCommand(
+ AzureBlobService azureBlobService,
+ AddSampleWordCommand addSampleWordCommand)
+ {
+ this.azureBlobService = azureBlobService;
+ this.addSampleWordCommand = addSampleWordCommand;
+ }
+
+ public async Task> Invoke(string eeWord)
+ {
+ (await azureBlobService.GetWordData(eeWord)).Deconstruct(
+ out bool _,
+ out bool isBlobAccessFailure,
+ out Maybe savedWord,
+ out string blobAccessError);
+
+ if (isBlobAccessFailure)
+ {
+ return Result.Failure(blobAccessError);
+ }
+
+ if (savedWord.HasValue)
+ {
+ return savedWord.Value;
+ }
+
+ return await this.addSampleWordCommand.Invoke(eeWord);
+ }
+ }
+}
\ No newline at end of file
diff --git a/My1kWordsEe/Services/Db/AzureBlobService.cs b/My1kWordsEe/Services/Db/AzureBlobService.cs
index 392079b0..67a07388 100644
--- a/My1kWordsEe/Services/Db/AzureBlobService.cs
+++ b/My1kWordsEe/Services/Db/AzureBlobService.cs
@@ -1,5 +1,6 @@
using System.Text.Json;
+using Azure;
using Azure.Storage.Blobs;
using CSharpFunctionalExtensions;
@@ -8,82 +9,119 @@
namespace My1kWordsEe.Services.Db
{
+ ///
+ /// Facade for Azure blob storage API
+ ///
public class AzureBlobService
{
+ private readonly ILogger logger;
private readonly string connectionString;
- public AzureBlobService(string connectionString)
+ public AzureBlobService(
+ ILogger logger,
+ string connectionString)
{
+ this.logger = logger;
this.connectionString = connectionString;
}
- public async Task> GetWordData(string word)
+ public async Task>> GetWordData(string word)
{
- BlobContainerClient container = await GetWordsContainer();
- BlobClient blob = container.GetBlobClient(JsonBlobName(word));
+ var container = await GetWordsContainer();
- if (await blob.ExistsAsync())
+ if (container.IsFailure)
+ {
+ return Result.Failure>(container.Error);
+ }
+
+ BlobClient blob = container.Value.GetBlobClient(JsonBlobName(word));
+
+ if (!await blob.ExistsAsync())
+ {
+ return Maybe.None;
+ }
+
+ try
{
var response = await blob.DownloadContentAsync();
if (response != null && response.HasValue)
{
var sampleWord = JsonSerializer.Deserialize(response.Value.Content);
-
- if (sampleWord != null)
- {
- return Result.Success(sampleWord);
- }
+ return Maybe.From(sampleWord);
+ }
+ else
+ {
+ return Maybe.None;
}
}
-
- return Result.Failure($"Word '{word}' is not recorded");
+ catch (RequestFailedException ex)
+ {
+ this.logger.LogError(ex, "Failure to download data from blob {name}", blob.Name);
+ return Result.Failure>("Failure to download data from blob");
+ }
+ catch (Exception ex) when (ex is JsonException || ex is NotSupportedException)
+ {
+ this.logger.LogError(ex, "Failure to parse JSON from blob {name}", blob.Name);
+ return Result.Failure>("Failure to parse JSON data from blob");
+ }
}
- public async Task SaveWordData(SampleWord word)
- {
- BlobContainerClient container = await GetWordsContainer();
+ public Task> SaveWordData(SampleWord word) =>
+ this.GetWordsContainer().Bind(container =>
+ this.UploadStreamAsync(
+ container.GetBlobClient(JsonBlobName(word.EeWord)),
+ new MemoryStream(JsonSerializer.SerializeToUtf8Bytes(word))));
- // Get a reference to a blob
- BlobClient blob = container.GetBlobClient(JsonBlobName(word.EeWord));
+ public Task> SaveAudio(Stream audioStream, string blobName) =>
+ this.GetAudioContainer().Bind((container) =>
+ this.UploadStreamAsync(
+ container.GetBlobClient(blobName),
+ audioStream));
- // Upload file data
- await blob.UploadAsync(
- new MemoryStream(JsonSerializer.SerializeToUtf8Bytes(word)),
- overwrite: true);
- }
+ public Task> SaveAudio(Stream audioStream) =>
+ this.SaveAudio(audioStream, WavBlobName());
- public async Task SaveAudio(Stream audioStream, string blobName)
- {
- BlobContainerClient container = await GetAudioContainer();
- BlobClient blob = container.GetBlobClient(blobName);
- await blob.UploadAsync(audioStream, overwrite: true);
- return blob.Uri;
- }
-
- public Task SaveAudio(Stream audioStream) => SaveAudio(audioStream, WavBlobName());
-
- public async Task SaveImage(Stream imageStream)
- {
- BlobContainerClient container = await GetImageContainer();
- BlobClient blob = container.GetBlobClient(JpgBlobName());
- await blob.UploadAsync(imageStream);
- return blob.Uri;
- }
+ public Task> SaveImage(Stream imageStream) =>
+ this.GetImageContainer().Bind((container) =>
+ this.UploadStreamAsync(
+ container.GetBlobClient(JpgBlobName()),
+ imageStream));
+ private Task> GetWordsContainer() => this.GetOrCreateContainer("words");
- private async Task GetWordsContainer() => await this.GetContainer("words");
+ private Task> GetAudioContainer() => this.GetOrCreateContainer("audio");
- private async Task GetAudioContainer() => await this.GetContainer("audio");
+ private Task> GetImageContainer() => this.GetOrCreateContainer("image");
- private async Task GetImageContainer() => await this.GetContainer("image");
+ private async Task> GetOrCreateContainer(string containerId)
+ {
+ try
+ {
+ var container = new BlobContainerClient(
+ this.connectionString,
+ containerId);
+ await container.CreateIfNotExistsAsync();
+ return container;
+ }
+ catch (RequestFailedException exception)
+ {
+ this.logger.LogError(exception, "Failure to get or create container {ContainerId}", containerId);
+ return Result.Failure("Azure storage access error");
+ }
+ }
- private async Task GetContainer(string containerId)
+ private async Task> UploadStreamAsync(BlobClient blob, Stream stream)
{
- BlobContainerClient container = new BlobContainerClient(
- this.connectionString,
- containerId);
- await container.CreateIfNotExistsAsync();
- return container;
+ try
+ {
+ await blob.UploadAsync(stream, overwrite: true);
+ return blob.Uri;
+ }
+ catch (RequestFailedException exception)
+ {
+ this.logger.LogError(exception, "Failure to upload data to blob {name}", blob.Name);
+ return Result.Failure("Azure storage upload error");
+ }
}
static string JsonBlobName(string word) => word.ToLower() + ".json";
diff --git a/My1kWordsEe/Services/OpenAiService.cs b/My1kWordsEe/Services/OpenAiService.cs
index e3f30687..f67118d6 100644
--- a/My1kWordsEe/Services/OpenAiService.cs
+++ b/My1kWordsEe/Services/OpenAiService.cs
@@ -180,28 +180,28 @@ public async Task> GetWordMetadata(string word)
return Result.Failure("Empty response");
}
}
-}
-public class Sentence
-{
- [JsonPropertyName("ee_sentence")]
- public string Ee { get; set; }
+ public class Sentence
+ {
+ [JsonPropertyName("ee_sentence")]
+ public string Ee { get; set; }
- [JsonPropertyName("en_sentence")]
- public string En { get; set; }
-}
+ [JsonPropertyName("en_sentence")]
+ public string En { get; set; }
+ }
-public class WordMetadata
-{
- [JsonPropertyName("ee_word")]
- public string EeWord { get; set; }
+ public class WordMetadata
+ {
+ [JsonPropertyName("ee_word")]
+ public string EeWord { get; set; }
- [JsonPropertyName("en_word")]
- public string EnWord { get; set; }
+ [JsonPropertyName("en_word")]
+ public string EnWord { get; set; }
- [JsonPropertyName("en_explanation")]
- public string EnExplanation { get; set; }
+ [JsonPropertyName("en_explanation")]
+ public string EnExplanation { get; set; }
- [JsonPropertyName("en_words")]
- public string[] EnWords { get; set; }
-}
\ No newline at end of file
+ [JsonPropertyName("en_words")]
+ public string[] EnWords { get; set; }
+ }
+}
diff --git a/My1kWordsEe/Services/StabilityAiService.cs b/My1kWordsEe/Services/StabilityAiService.cs
index 0ce4970e..214c789e 100644
--- a/My1kWordsEe/Services/StabilityAiService.cs
+++ b/My1kWordsEe/Services/StabilityAiService.cs
@@ -7,6 +7,9 @@
namespace My1kWordsEe.Services
{
+ ///
+ /// Facade for https://platform.stability.ai/docs/getting-started/stable-image
+ ///
public class StabilityAiService
{
public const string ApiHost = "https://api.stability.ai";
diff --git a/My1kWordsEe/Services/TartuNlpService.cs b/My1kWordsEe/Services/TartuNlpService.cs
index 2478dd84..69c495a3 100644
--- a/My1kWordsEe/Services/TartuNlpService.cs
+++ b/My1kWordsEe/Services/TartuNlpService.cs
@@ -1,10 +1,21 @@
using System.Net.Http.Headers;
+
using CSharpFunctionalExtensions;
namespace My1kWordsEe.Services
{
+ ///
+ /// Facade for https://neurokone.ee/.
+ ///
public class TartuNlpService
{
+ private readonly ILogger logger;
+
+ public TartuNlpService(ILogger logger)
+ {
+ this.logger = logger;
+ }
+
public async Task> GetSpeech(string text)
{
using HttpClient client = new HttpClient();
@@ -13,20 +24,33 @@ public async Task> GetSpeech(string text)
request.Headers.Add("accept", "audio/wav");
- request.Content = new StringContent($"{{\n\"text\": \"{text}\",\n\"speaker\": \"mari\",\n\"speed\": 0.64\n}}");
+ request.Content = new StringContent(
+ $"{{\n\"text\": \"{text}\",\n\"speaker\": \"mari\",\n\"speed\": 0.64\n}}");
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
- HttpResponseMessage response = await client.SendAsync(request);
-
- if (response.IsSuccessStatusCode)
+ try
{
- var stream = await response.Content.ReadAsStreamAsync();
- return Result.Success(stream);
+ HttpResponseMessage response = await client.SendAsync(request);
+
+ if (response.IsSuccessStatusCode)
+ {
+ var stream = await response.Content.ReadAsStreamAsync();
+ return Result.Success(stream);
+ }
+ else
+ {
+ var errorStr = await response.Content.ReadAsStringAsync();
+ this.logger.LogError(
+ "Tartu NLP HTTP error. Reason phrase: {reason}. Content: {content}",
+ response.ReasonPhrase,
+ errorStr);
+ return Result.Failure($"Tartu NLP HTTP error. {response.ReasonPhrase}. {errorStr}");
+ }
}
- else
+ catch (HttpRequestException httpException)
{
- var errorStr = await response.Content.ReadAsStringAsync();
- return Result.Failure($"{response.ReasonPhrase}: {errorStr}");
+ this.logger.LogError(httpException, "Tartu NLP HTTP exception");
+ return Result.Failure($"Tartu NLP HTTP exception");
}
}
}