diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 000000000..4a824d642 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "mindee.cli": { + "version": "4.4.0", + "commands": [ + "mindee" + ], + "rollForward": false + }, + "dotnet-delice": { + "version": "2.1.0", + "commands": [ + "dotnet-delice" + ], + "rollForward": false + }, + "husky": { + "version": "0.9.1", + "commands": [ + "husky" + ], + "rollForward": false + } + } +} \ No newline at end of file diff --git a/.github/workflows/_static-analysis.yml b/.github/workflows/_static-analysis.yml index acdd4cce3..ce1387262 100644 --- a/.github/workflows/_static-analysis.yml +++ b/.github/workflows/_static-analysis.yml @@ -21,3 +21,9 @@ jobs: - name: Run dotnet format run: dotnet format --verify-no-changes + + - name: Restore local tools (dotnet-delice) + run: dotnet tool restore + + - name: Check dependency licenses against whitelist + run: bash .husky/check-licenses.sh diff --git a/.gitignore b/.gitignore index ffc19a8d4..483449d25 100644 --- a/.gitignore +++ b/.gitignore @@ -371,5 +371,6 @@ _site # StrongName files *.snk *.snk.b64 -# Local CLI publish. -dotnet-tools.json + +# dotnet-delice output +/licenses.json diff --git a/.husky/check-licenses.sh b/.husky/check-licenses.sh new file mode 100755 index 000000000..47f62c6f5 --- /dev/null +++ b/.husky/check-licenses.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# +# License whitelist check for NuGet dependencies. +# +# Runs `dotnet delice` and fails if any package uses a license expression that +# isn't listed in .husky/licenses.allowed, unless the package name appears in +# .husky/licenses.allowed-packages. +# +# Requires: dotnet, jq, bash. No Python. +# +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SLN="$SCRIPT_DIR/../Mindee.sln" +ALLOWED_FILE="$SCRIPT_DIR/licenses.allowed" +ALLOWED_PKGS_FILE="$SCRIPT_DIR/licenses.allowed-packages" + +command -v jq >/dev/null 2>&1 || { + echo "[husky] jq is required for the license check but was not found in PATH." >&2 + exit 2 +} + +TMP_JSON="$(mktemp -t delice-XXXXXX.json)" +trap 'rm -f "$TMP_JSON"' EXIT + +echo "[husky] Running dotnet delice on ${SLN} ..." +dotnet delice "$SLN" -j --json-output "$TMP_JSON" >/dev/null + +strip_comments() { + # Drop blank lines and '#' comments, trim trailing whitespace. + sed -e 's/[[:space:]]*$//' -e '/^[[:space:]]*#/d' -e '/^[[:space:]]*$/d' "$1" +} + +ALLOWED_LICENSES="$(strip_comments "$ALLOWED_FILE" || true)" +ALLOWED_PACKAGES="$(strip_comments "$ALLOWED_PKGS_FILE" 2>/dev/null || true)" + +# Emit one tab-separated record per package: projectnameversionexpression +# We deliberately avoid jq's @tsv, which doubles backslashes (breaking matches +# against expressions like `licenses\LICENSE.txt`). +RECORDS="$(jq -r ' + .projects[] + | .projectName as $p + | .licenses[] + | .expression as $e + | .packages[] + | [$p, .name, (.version // "?"), $e] | join("\t") +' "$TMP_JSON")" + +violations="" +while IFS=$'\t' read -r project name version expression; do + [ -z "${name:-}" ] && continue + # Allowed license expression? + if printf '%s\n' "$ALLOWED_LICENSES" | grep -Fxq -- "$expression"; then + continue + fi + # Per-package exception? + if [ -n "$ALLOWED_PACKAGES" ] && printf '%s\n' "$ALLOWED_PACKAGES" | grep -Fxq -- "$name"; then + continue + fi + violations+=$'\n'" - ${name}@${version} (license: ${expression}) [project: ${project}]" +done <<< "$RECORDS" + +if [ -n "$violations" ]; then + { + echo "Disallowed package licenses detected:" + # De-duplicate while preserving order. + printf '%s\n' "$violations" | awk 'NF && !seen[$0]++' + echo + echo "Either remove the offending dependency, add the license expression to" + echo ".husky/licenses.allowed, or add the package name to" + echo ".husky/licenses.allowed-packages after review." + } >&2 + exit 1 +fi + +echo "License check passed: all packages use whitelisted licenses." diff --git a/.husky/licenses.allowed b/.husky/licenses.allowed new file mode 100644 index 000000000..d8ad2dc1d --- /dev/null +++ b/.husky/licenses.allowed @@ -0,0 +1,11 @@ +# One SPDX-style license expression per line. Blank lines and '#' comments allowed. +# Any package whose license expression is NOT in this list will fail the pre-push check, +# unless the package name appears in licenses.allowed-packages. +MIT +Apache-2.0 +BSD-3-Clause +LGPL-3.0-or-later +Apache-2.0 AND MIT +Microsoft Software License +licenses\LICENSE.txt +Project References diff --git a/.husky/licenses.allowed-packages b/.husky/licenses.allowed-packages new file mode 100644 index 000000000..704ccaa81 --- /dev/null +++ b/.husky/licenses.allowed-packages @@ -0,0 +1,6 @@ +# Per-package overrides for cases where delice cannot detect the SPDX license +# (legacy NuGet license structure). Verified manually to be acceptable. +# One "PackageName" per line (no version). +Microsoft.NETFramework.ReferenceAssemblies +Microsoft.NETFramework.ReferenceAssemblies.net472 +Microsoft.NETFramework.ReferenceAssemblies.net48 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..65130a63e --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,22 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +## husky task runner examples ------------------- +## Note : for local installation use 'dotnet' prefix. e.g. 'dotnet husky' + +## run all tasks +#husky run + +### run all tasks with group: 'group-name' +#husky run --group group-name + +## run task with name: 'task-name' +#husky run --name task-name + +## pass hook arguments to task +#husky run --args "$1" "$2" + +## or put your custom commands ------------------- +#echo 'Husky.Net is awesome!' + +dotnet husky run --group pre-commit diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 000000000..3af48fa0a --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +dotnet husky run --group pre-push diff --git a/.husky/run-unit-tests.sh b/.husky/run-unit-tests.sh new file mode 100755 index 000000000..b3bac2bcf --- /dev/null +++ b/.husky/run-unit-tests.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# Run unit tests for the target frameworks that are actually supported on the +# current OS. .NET Framework targets (net472 / net48) require Windows because +# Docnet.Core's native PDF binaries don't load under Mono on *nix. +# +# Mirrors the matrix used in .github/workflows/_test-units.yml. +# +set -euo pipefail + +PROJECT="tests/Mindee.UnitTests/Mindee.UnitTests.csproj" + +case "$(uname -s 2>/dev/null || echo Windows)" in + MINGW*|MSYS*|CYGWIN*|Windows*) + FRAMEWORKS=("net6.0" "net8.0" "net10.0" "net472" "net48") + ;; + *) + FRAMEWORKS=("net8.0" "net10.0") + ;; +esac + +echo "[husky] Running unit tests for: ${FRAMEWORKS[*]}" + +for tfm in "${FRAMEWORKS[@]}"; do + echo "[husky] --- $tfm ---" + dotnet test "$PROJECT" -f "$tfm" --nologo -v:quiet +done diff --git a/.husky/task-runner.json b/.husky/task-runner.json new file mode 100644 index 000000000..90a25c42f --- /dev/null +++ b/.husky/task-runner.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://alirezanet.github.io/Husky.Net/schema.json", + "tasks": [ + { + "name": "dotnet-format-staged", + "group": "pre-commit", + "command": "dotnet", + "args": [ "format", "Mindee.sln", "--include", "${staged}", "--verify-no-changes", "--no-restore" ], + "include": [ "**/*.cs" ] + }, + { + "name": "build", + "group": "pre-commit", + "command": "dotnet", + "args": [ "build", "Mindee.sln", "--nologo", "-clp:NoSummary", "-v:quiet" ] + }, + { + "name": "license-check", + "group": "pre-push", + "command": "bash", + "args": [ ".husky/check-licenses.sh" ] + }, + { + "name": "unit-tests", + "group": "pre-push", + "command": "bash", + "args": [ ".husky/run-unit-tests.sh" ] + } + ] +} diff --git a/Directory.Build.props b/Directory.Build.props index 1cbfb2955..158a04007 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -15,6 +15,16 @@ CHANGELOG.md true true + true + all + low + + + + + $(NoWarn);S1133 + + $(NoWarn);S6966 @@ -91,4 +101,11 @@ false $(NoWarn);CS8002 + + + + + + + diff --git a/src/Mindee.Cli/Commands/V1/PredictCommand.cs b/src/Mindee.Cli/Commands/V1/PredictCommand.cs index b1e7bddb9..3ccb02203 100644 --- a/src/Mindee.Cli/Commands/V1/PredictCommand.cs +++ b/src/Mindee.Cli/Commands/V1/PredictCommand.cs @@ -10,13 +10,13 @@ namespace Mindee.Cli.Commands.V1 { - internal struct CommandOptions(string name, string description, bool allWords, bool fullText, bool sync, bool async) + internal struct CommandOptions(string name, string description, bool allWords, bool fullText, bool sync, bool isAsync) { public readonly string Name = name; public readonly string Description = description; public readonly bool AllWords = allWords; public readonly bool FullText = fullText; - public readonly bool Async = async; + public readonly bool IsAsync = isAsync; public readonly bool Sync = sync; } @@ -72,7 +72,7 @@ public PredictCommand(CommandOptions options) Options.Add(_fullTextOption); } - switch (options.Async) + switch (options.IsAsync) { case true when !options.Sync: { diff --git a/src/Mindee.Cli/Commands/V2/BaseCommand.cs b/src/Mindee.Cli/Commands/V2/BaseCommand.cs index 0a9af49fd..b160792d7 100644 --- a/src/Mindee.Cli/Commands/V2/BaseCommand.cs +++ b/src/Mindee.Cli/Commands/V2/BaseCommand.cs @@ -9,7 +9,7 @@ abstract class BaseCommand : Command { protected readonly Option ApiKeyOption; - public BaseCommand(string name, string description) : base(name, description) + protected BaseCommand(string name, string description) : base(name, description) { ApiKeyOption = new Option("--api-key", "-k") { Description = "Mindee V2 API key." }; Options.Add(ApiKeyOption); diff --git a/src/Mindee.Cli/Commands/V2/InferenceCommand.cs b/src/Mindee.Cli/Commands/V2/InferenceCommand.cs index 75ec906b6..26108f3db 100644 --- a/src/Mindee.Cli/Commands/V2/InferenceCommand.cs +++ b/src/Mindee.Cli/Commands/V2/InferenceCommand.cs @@ -14,7 +14,6 @@ using Mindee.V2.Product.Ocr.Params; using Mindee.V2.Product.Split; using Mindee.V2.Product.Split.Params; -using SettingsV2 = Mindee.V2.Http.Settings; using V2Client = Mindee.V2.Client; namespace Mindee.Cli.Commands.V2 @@ -240,7 +239,7 @@ private async Task EnqueueAndGetResultAsync(InferenceOptions options, strin new OcrParameters(options.ModelId, options.Alias)), "split" => await mindeeClient.EnqueueAndGetResultAsync(inputSource, new SplitParameters(options.ModelId, options.Alias)), - _ => throw new ArgumentOutOfRangeException(nameof(options.Product)) + _ => throw new ArgumentOutOfRangeException(productName) }; PrintToConsole(Console.Out, options, response); diff --git a/src/Mindee.Cli/Commands/V2/SearchModelsCommand.cs b/src/Mindee.Cli/Commands/V2/SearchModelsCommand.cs index bd3a66ecb..83d75f698 100644 --- a/src/Mindee.Cli/Commands/V2/SearchModelsCommand.cs +++ b/src/Mindee.Cli/Commands/V2/SearchModelsCommand.cs @@ -4,7 +4,6 @@ using Microsoft.Extensions.Options; using Mindee.V2.Parsing.Search; using Mindee.V2.Search.Models; -using SettingsV2 = Mindee.V2.Http.Settings; using V2Client = Mindee.V2.Client; namespace Mindee.Cli.Commands.V2 @@ -19,7 +18,7 @@ class SearchModelsCommand : BaseCommand private readonly Option? _rawOption; /// - /// + /// Creates a new instance of . /// public SearchModelsCommand() : base("search-models", "Search available models.") { @@ -52,7 +51,7 @@ Filter by exact model type (case sensitive). } /// - /// + /// Configures an action. /// /// Service provider for dependency resolution public void ConfigureAction(IServiceProvider services) diff --git a/src/Mindee.Cli/Commands/V2/SearchRagDocumentsCommand.cs b/src/Mindee.Cli/Commands/V2/SearchRagDocumentsCommand.cs index 368d7fe57..165b0deb7 100644 --- a/src/Mindee.Cli/Commands/V2/SearchRagDocumentsCommand.cs +++ b/src/Mindee.Cli/Commands/V2/SearchRagDocumentsCommand.cs @@ -4,8 +4,6 @@ using Microsoft.Extensions.Options; using Mindee.V2.Parsing.Search; using Mindee.V2.Search.Model; -using Mindee.V2.Search.Models; -using SettingsV2 = Mindee.V2.Http.Settings; using V2Client = Mindee.V2.Client; namespace Mindee.Cli.Commands.V2 @@ -20,7 +18,7 @@ class SearchRagDocumentsCommand : BaseCommand private readonly Option? _rawOption; /// - /// + /// Creates a new instance of . /// public SearchRagDocumentsCommand() : base("search-rag-docs", "Search available RAG documents for a given model.") { @@ -44,7 +42,7 @@ class SearchRagDocumentsCommand : BaseCommand } /// - /// + /// Configures an action. /// /// Service provider for dependency resolution public void ConfigureAction(IServiceProvider services) diff --git a/src/Mindee/Geometry/Bbox.cs b/src/Mindee/Geometry/Bbox.cs index 0d19e9fda..b1fd0a73d 100644 --- a/src/Mindee/Geometry/Bbox.cs +++ b/src/Mindee/Geometry/Bbox.cs @@ -7,6 +7,7 @@ namespace Mindee.Geometry public class Bbox { /// + /// BBox from 4 coordinates. /// /// /// diff --git a/src/Mindee/Geometry/Point.cs b/src/Mindee/Geometry/Point.cs index 7c489b13d..c30a99821 100644 --- a/src/Mindee/Geometry/Point.cs +++ b/src/Mindee/Geometry/Point.cs @@ -10,6 +10,7 @@ namespace Mindee.Geometry public class Point : List { /// + /// Point from x and y coordinates. /// /// /// @@ -26,12 +27,12 @@ public Point(double x, double y) /// /// X coordinate. /// - public double X => this.First(); + public double X => this[0]; /// /// Y coordinate. /// - public double Y => this.Last(); + public double Y => this[1]; /// /// The default string representation. diff --git a/src/Mindee/Geometry/Polygon.cs b/src/Mindee/Geometry/Polygon.cs index dd16e37ac..1b5614664 100644 --- a/src/Mindee/Geometry/Polygon.cs +++ b/src/Mindee/Geometry/Polygon.cs @@ -10,6 +10,7 @@ namespace Mindee.Geometry public class Polygon : List { /// + /// Polygon from a list of coordinates. /// /// List of points of coordinates on X and Y. public Polygon(List> coordinates) @@ -21,11 +22,12 @@ public Polygon(List> coordinates) throw new InvalidOperationException("A point must have 2 coordinates."); } - Add(new Point(point.First(), point.Last())); + Add(new Point(point[0], point[point.Count - 1])); } } /// + /// Polygon from coordinates as IEnumerable. /// /// /// @@ -40,7 +42,7 @@ public Polygon(IEnumerable coordinates) /// public Point GetCentroid() { - var verticesCount = this.Count(); + var verticesCount = this.Count; var xSum = this.Sum(c => c.X); var ySum = this.Sum(c => c.Y); diff --git a/src/Mindee/Geometry/PolygonJsonConverter.cs b/src/Mindee/Geometry/PolygonJsonConverter.cs index c08b403ae..01f177ce0 100644 --- a/src/Mindee/Geometry/PolygonJsonConverter.cs +++ b/src/Mindee/Geometry/PolygonJsonConverter.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; @@ -13,6 +14,8 @@ public class PolygonJsonConverter : JsonConverter /// /// /// + [SuppressMessage("Minor Code Smell", "S1168: Return an empty collection instead of null.", + Justification = "Would be breaking for end-users to right now. TODO: return [] instead of null")] public override Polygon Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var points = JsonSerializer.Deserialize>>(ref reader, options); diff --git a/src/Mindee/Image/ExtractedImage.cs b/src/Mindee/Image/ExtractedImage.cs index c160522a7..16076039b 100644 --- a/src/Mindee/Image/ExtractedImage.cs +++ b/src/Mindee/Image/ExtractedImage.cs @@ -17,12 +17,12 @@ public class ExtractedImage /// /// Page number the image was extracted from. /// - public int PageId; + public int PageId { get; } /// /// ID of the image. /// - public int ElementId; + public int ElementId { get; } /// /// Initializes a new instance of the class. diff --git a/src/Mindee/Image/ImageUtils.cs b/src/Mindee/Image/ImageUtils.cs index bc2d5965e..743ec679c 100644 --- a/src/Mindee/Image/ImageUtils.cs +++ b/src/Mindee/Image/ImageUtils.cs @@ -48,7 +48,7 @@ public static class ImageUtils /// /// Raw array produce by DocLib's .GetImage() function. /// A valid SKBitmap. - public static SKBitmap ArrayToImage(byte[,,] pixelArray) + internal static SKBitmap ArrayToImage(byte[,,] pixelArray) { var width = pixelArray.GetLength(1); var height = pixelArray.GetLength(0); diff --git a/src/Mindee/Input/InputSource.cs b/src/Mindee/Input/InputSource.cs index bf96ff9bb..6fc563787 100644 --- a/src/Mindee/Input/InputSource.cs +++ b/src/Mindee/Input/InputSource.cs @@ -1,7 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + namespace Mindee.Input { /// /// Base class for input sources used in Mindee API requests. /// + [SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty", + Justification = "Used as a base type for strict pattern matching in the public API.")] public abstract class InputSource; } diff --git a/src/Mindee/Input/LocalInputSource.cs b/src/Mindee/Input/LocalInputSource.cs index 0cdefa0d4..c0d1fdb3e 100644 --- a/src/Mindee/Input/LocalInputSource.cs +++ b/src/Mindee/Input/LocalInputSource.cs @@ -20,8 +20,6 @@ public sealed class LocalInputSource : InputSource ".heic", ".heif", ".jpg", ".jpga", ".jpeg", ".pdf", ".png", ".tiff", ".tif", ".webp" ]; - private DocNetApi _pdfOperation; - /// /// Construct from bytes. /// @@ -105,6 +103,9 @@ public LocalInputSource(string base64Data, string filename) /// public string Extension { get; set; } + /// + /// Sets the file name. + /// /// /// private void SetFileName(string filename) @@ -190,16 +191,17 @@ public void ApplyPageOptions(PageOptions pageOptions) { var serviceCollection = new ServiceCollection(); var serviceProvider = serviceCollection.BuildServiceProvider(); - _pdfOperation = serviceProvider.GetService(); - if (_pdfOperation == null) + + var pdfOperation = serviceProvider.GetService(); + if (pdfOperation == null) { - _pdfOperation = new DocNetApi(); - serviceCollection.AddSingleton(_pdfOperation); + pdfOperation = new DocNetApi(); + serviceCollection.AddSingleton(pdfOperation); } if (pageOptions != null && IsPdf()) { - FileBytes = _pdfOperation.Split( + FileBytes = pdfOperation.Split( new SplitQuery(FileBytes, pageOptions)).File; } } @@ -210,12 +212,7 @@ public void ApplyPageOptions(PageOptions pageOptions) /// True if at least one character exists in one page. public bool HasSourceText() { - if (!IsPdf()) - { - return false; - } - - return PdfUtils.HasSourceText(FileBytes); + return IsPdf() && PdfUtils.HasSourceText(FileBytes); } } } diff --git a/src/Mindee/Input/PageOptions.cs b/src/Mindee/Input/PageOptions.cs index 33d958d76..69ea6b63e 100644 --- a/src/Mindee/Input/PageOptions.cs +++ b/src/Mindee/Input/PageOptions.cs @@ -6,6 +6,7 @@ namespace Mindee.Input public sealed class PageOptions { /// + /// Page options from indexes, operation and minimum pages. /// /// /// diff --git a/src/Mindee/Parsing/BaseLocalResponse.cs b/src/Mindee/Parsing/BaseLocalResponse.cs index 97881b176..0a2f8dd58 100644 --- a/src/Mindee/Parsing/BaseLocalResponse.cs +++ b/src/Mindee/Parsing/BaseLocalResponse.cs @@ -14,7 +14,7 @@ public abstract class BaseLocalResponse /// Load from a string. /// /// Will be decoded as UTF-8. - public BaseLocalResponse(string input) + protected BaseLocalResponse(string input) { FileBytes = Encoding.UTF8.GetBytes(input.Replace("\r", "").Replace("\n", "")); } @@ -23,7 +23,7 @@ public BaseLocalResponse(string input) /// Load from a file. /// /// Will be decoded as UTF-8. - public BaseLocalResponse(FileInfo input) + protected BaseLocalResponse(FileInfo input) { FileBytes = Encoding.UTF8.GetBytes( File.ReadAllText(input.FullName).Replace("\r", "").Replace("\n", "")); diff --git a/src/Mindee/Parsing/SummaryHelper.cs b/src/Mindee/Parsing/SummaryHelper.cs index ace90bcd8..a0fc609a9 100644 --- a/src/Mindee/Parsing/SummaryHelper.cs +++ b/src/Mindee/Parsing/SummaryHelper.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Globalization; using System.Text; @@ -10,7 +11,7 @@ internal static class SummaryHelper { public static string Clean(string summary) { - var cleanSpace = new Regex(" \n", RegexOptions.Multiline); + var cleanSpace = new Regex(" \n", RegexOptions.Multiline, TimeSpan.FromMilliseconds(1000)); return cleanSpace.Replace(summary, "\n"); } @@ -24,16 +25,17 @@ public static string FormatAmount(decimal? amount) return amount == null ? "" : amount.Value.ToString("0.00###", CultureInfo.InvariantCulture); } - public static string FormatString(string str) - { - return (str ?? "").Replace("\n", "\\n").Replace("\t", "\\t").Replace("\r", "\\r"); - } public static string FormatBool(bool? value) { return value.ToString(); } + public static string FormatString(string str) + { + return (str ?? "").Replace("\n", "\\n").Replace("\t", "\\t").Replace("\r", "\\r"); + } + public static string FormatString(string str, int maxLength) { var strSummary = FormatString(str); diff --git a/src/Mindee/Pdf/DocNetApi.cs b/src/Mindee/Pdf/DocNetApi.cs index 9baa6547c..029cbf950 100644 --- a/src/Mindee/Pdf/DocNetApi.cs +++ b/src/Mindee/Pdf/DocNetApi.cs @@ -101,7 +101,7 @@ private bool CanBeOpen(byte[] file) } } - private ushort GetTotalPagesNumber(byte[] file) + private static ushort GetTotalPagesNumber(byte[] file) { try { diff --git a/src/Mindee/Pdf/SplitQuery.cs b/src/Mindee/Pdf/SplitQuery.cs index 90f52831a..04306d7c6 100644 --- a/src/Mindee/Pdf/SplitQuery.cs +++ b/src/Mindee/Pdf/SplitQuery.cs @@ -8,6 +8,7 @@ namespace Mindee.Pdf public sealed class SplitQuery { /// + /// Split query from byte array. /// /// /// diff --git a/src/Mindee/Pdf/SplitdPdf.cs b/src/Mindee/Pdf/SplitdPdf.cs index f7ef718b2..071ec0f1c 100644 --- a/src/Mindee/Pdf/SplitdPdf.cs +++ b/src/Mindee/Pdf/SplitdPdf.cs @@ -6,6 +6,7 @@ namespace Mindee.Pdf public sealed class SplitPdf { /// + /// Split PDF from byte array. /// /// /// diff --git a/src/Mindee/V1/Client.cs b/src/Mindee/V1/Client.cs index ebfba87df..df681c290 100644 --- a/src/Mindee/V1/Client.cs +++ b/src/Mindee/V1/Client.cs @@ -13,6 +13,7 @@ using Mindee.V1.Parsing; using Mindee.V1.Parsing.Common; using Mindee.V1.Product.Generated; + // ReSharper disable once RedundantUsingDirective namespace Mindee.V1 @@ -27,6 +28,7 @@ public sealed class Client private readonly IPdfOperation _pdfOperation; /// + /// Default V1 constructor. /// /// The required API key to use Mindee. /// @@ -51,6 +53,7 @@ public Client(string apiKey, ILoggerFactory logger = null) } /// + /// API key-less constructor for V1 Client. /// /// /// @@ -79,6 +82,7 @@ public Client(Settings settings, ILoggerFactory logger = null) } /// + /// Custom HTTP module for V1 Client. /// /// /// @@ -91,11 +95,13 @@ public Client(IPdfOperation pdfOperation, IHttpApi httpApi, ILoggerFactory logge { _pdfOperation = pdfOperation; _mindeeApi = httpApi; - if (logger != null) + if (logger == null) { - MindeeLogger.Assign(logger); - _logger = MindeeLogger.GetLogger(); + return; } + + MindeeLogger.Assign(logger); + _logger = MindeeLogger.GetLogger(); } /// @@ -124,12 +130,9 @@ LocalInputSource inputSource , PageOptions pageOptions = null) where TInferenceModel : GeneratedV1, new() { - _logger?.LogInformation("Synchronous parsing of {} ...", nameof(TInferenceModel)); + _logger?.LogInformation("Synchronous parsing of {TInferenceModel} ...", nameof(TInferenceModel)); - if (predictOptions == null) - { - predictOptions = new PredictOptions(); - } + predictOptions ??= new PredictOptions(); if (pageOptions != null && inputSource.IsPdf()) { @@ -175,12 +178,9 @@ UrlInputSource inputSource , PredictOptions predictOptions = null) where TInferenceModel : GeneratedV1, new() { - _logger?.LogInformation("Synchronous parsing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Synchronous parsing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (predictOptions == null) - { - predictOptions = new PredictOptions(); - } + predictOptions ??= new PredictOptions(); return await _mindeeApi.PredictPostAsync( new PredictParameter( @@ -195,14 +195,11 @@ UrlInputSource inputSource } /// - /// Add a local input source to a Generated async queue. + /// Call Standard prediction API on a local input source and parse the results. /// /// /// /// - /// - /// - /// /// /// /// @@ -214,30 +211,26 @@ UrlInputSource inputSource /// The response object will be instantiated based on this parameter. /// /// - /// + /// /// /// - public async Task> EnqueueAsync( + public async Task> ParseAsync( LocalInputSource inputSource - , CustomEndpoint endpoint , PredictOptions predictOptions = null , PageOptions pageOptions = null) - where TInferenceModel : GeneratedV1, new() + where TInferenceModel : class, new() { - _logger?.LogInformation("Enqueuing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Synchronous parsing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (predictOptions == null) - { - predictOptions = new PredictOptions(); - } + predictOptions ??= new PredictOptions(); if (pageOptions != null && inputSource.IsPdf()) { - inputSource.FileBytes = _pdfOperation.Split( - new SplitQuery(inputSource.FileBytes, pageOptions)).File; + var splitPdf = _pdfOperation.Split(new SplitQuery(inputSource.FileBytes, pageOptions)); + inputSource.FileBytes = splitPdf.File; } - return await _mindeeApi.PredictAsyncPostAsync( + return await _mindeeApi.PredictPostAsync( new PredictParameter( inputSource, null, @@ -245,20 +238,16 @@ LocalInputSource inputSource predictOptions.FullText, predictOptions.Cropper, predictOptions.WorkflowId, - predictOptions.Rag - ) - , endpoint); + predictOptions.Rag)); } + /// - /// Add a URL input source to an async queue. + /// Call Standard prediction API on a URL input source and parse the results. /// /// /// /// - /// - /// - /// /// /// /// @@ -267,23 +256,19 @@ LocalInputSource inputSource /// The response object will be instantiated based on this parameter. /// /// - /// + /// /// /// - public async Task> EnqueueAsync( + public async Task> ParseAsync( UrlInputSource inputSource - , CustomEndpoint endpoint , PredictOptions predictOptions = null) - where TInferenceModel : GeneratedV1, new() + where TInferenceModel : class, new() { - _logger?.LogInformation("Enqueuing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Synchronous parsing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (predictOptions == null) - { - predictOptions = new PredictOptions(); - } + predictOptions ??= new PredictOptions(); - return await _mindeeApi.PredictAsyncPostAsync( + return await _mindeeApi.PredictPostAsync( new PredictParameter( null, inputSource, @@ -291,43 +276,11 @@ UrlInputSource inputSource predictOptions.FullText, predictOptions.Cropper, predictOptions.WorkflowId, - predictOptions.Rag) - , endpoint); - } - - /// - /// Parse a document from a Generated async queue. - /// - /// - /// - /// - /// The job id. - /// Cancellation token. - /// - /// Set the prediction model used to parse the document. - /// The response object will be instantiated based on this parameter. - /// - /// - /// - /// - public async Task> ParseQueuedAsync( - CustomEndpoint endpoint - , string jobId - , CancellationToken ct = default) - where TInferenceModel : GeneratedV1, new() - { - _logger?.LogInformation("Parse from queue of {} ...", typeof(TInferenceModel).Name); - - if (string.IsNullOrWhiteSpace(jobId)) - { - throw new ArgumentNullException(jobId); - } - - return await _mindeeApi.DocumentQueueGetAsync(jobId, endpoint, ct); + predictOptions.Rag)); } /// - /// Add the document to an async queue, poll, and parse when complete. + /// Add a local input source to a Generated async queue. /// /// /// @@ -341,10 +294,6 @@ CustomEndpoint endpoint /// /// /// - /// - /// - /// - /// Cancellation token. /// /// Set the prediction model used to parse the document. /// The response object will be instantiated based on this parameter. @@ -353,81 +302,39 @@ CustomEndpoint endpoint /// /// /// - public async Task> EnqueueAndParseAsync( + public async Task> EnqueueAsync( LocalInputSource inputSource , CustomEndpoint endpoint , PredictOptions predictOptions = null - , PageOptions pageOptions = null - , AsyncPollingOptions pollingOptions = null - , CancellationToken ct = default) + , PageOptions pageOptions = null) where TInferenceModel : GeneratedV1, new() { - _logger?.LogInformation("Asynchronous parsing of {} ...", typeof(TInferenceModel).Name); - - if (pollingOptions == null) - { - pollingOptions = new AsyncPollingOptions(); - } + _logger?.LogInformation("Enqueuing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - var enqueueResponse = await EnqueueAsync( - inputSource, - endpoint, - predictOptions, - pageOptions); - - return await PollForResultsAsync(enqueueResponse, endpoint, pollingOptions, ct); - } - - - /// - /// Add the document to an async queue, poll, and parse when complete. URL input version. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// Cancellation token. - /// - /// Set the prediction model used to parse the document. - /// The response object will be instantiated based on this parameter. - /// - /// - /// - /// - /// - public async Task> EnqueueAndParseAsync( - UrlInputSource inputSource - , CustomEndpoint endpoint - , PredictOptions predictOptions = null - , AsyncPollingOptions pollingOptions = null - , CancellationToken ct = default) - where TInferenceModel : GeneratedV1, new() - { - _logger?.LogInformation("Asynchronous parsing of {} ...", typeof(TInferenceModel).Name); + predictOptions ??= new PredictOptions(); - if (pollingOptions == null) + if (pageOptions != null && inputSource.IsPdf()) { - pollingOptions = new AsyncPollingOptions(); + inputSource.FileBytes = _pdfOperation.Split( + new SplitQuery(inputSource.FileBytes, pageOptions)).File; } - var enqueueResponse = await EnqueueAsync( - inputSource, - endpoint, - predictOptions); - - return await PollForResultsAsync(enqueueResponse, endpoint, pollingOptions, ct); + return await _mindeeApi.PredictAsyncPostAsync( + new PredictParameter( + inputSource, + null, + predictOptions.AllWords, + predictOptions.FullText, + predictOptions.Cropper, + predictOptions.WorkflowId, + predictOptions.Rag + ) + , endpoint); } + /// - /// Call Standard prediction API on a local input source and parse the results. + /// Add a local input source to a Standard async queue. /// /// /// @@ -443,29 +350,26 @@ UrlInputSource inputSource /// The response object will be instantiated based on this parameter. /// /// - /// + /// /// /// - public async Task> ParseAsync( + public async Task> EnqueueAsync( LocalInputSource inputSource , PredictOptions predictOptions = null , PageOptions pageOptions = null) where TInferenceModel : class, new() { - _logger?.LogInformation("Synchronous parsing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Enqueuing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (predictOptions == null) - { - predictOptions = new PredictOptions(); - } + predictOptions ??= new PredictOptions(); if (pageOptions != null && inputSource.IsPdf()) { - var splitPdf = _pdfOperation.Split(new SplitQuery(inputSource.FileBytes, pageOptions)); - inputSource.FileBytes = splitPdf.File; + inputSource.FileBytes = _pdfOperation.Split( + new SplitQuery(inputSource.FileBytes, pageOptions)).File; } - return await _mindeeApi.PredictPostAsync( + return await _mindeeApi.PredictAsyncPostAsync( new PredictParameter( inputSource, null, @@ -477,7 +381,7 @@ LocalInputSource inputSource } /// - /// Call Standard prediction API on a URL input source and parse the results. + /// Add a URL input source to an async queue. /// /// /// @@ -490,22 +394,19 @@ LocalInputSource inputSource /// The response object will be instantiated based on this parameter. /// /// - /// + /// /// /// - public async Task> ParseAsync( + public async Task> EnqueueAsync( UrlInputSource inputSource , PredictOptions predictOptions = null) where TInferenceModel : class, new() { - _logger?.LogInformation("Synchronous parsing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Enqueuing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (predictOptions == null) - { - predictOptions = new PredictOptions(); - } + predictOptions ??= new PredictOptions(); - return await _mindeeApi.PredictPostAsync( + return await _mindeeApi.PredictAsyncPostAsync( new PredictParameter( null, inputSource, @@ -517,17 +418,17 @@ UrlInputSource inputSource } /// - /// Add a local input source to a Standard async queue. + /// Add a URL input source to an async queue. /// /// /// /// + /// + /// + /// /// /// /// - /// - /// - /// /// /// Set the prediction model used to parse the document. /// The response object will be instantiated based on this parameter. @@ -537,44 +438,35 @@ UrlInputSource inputSource /// /// public async Task> EnqueueAsync( - LocalInputSource inputSource - , PredictOptions predictOptions = null - , PageOptions pageOptions = null) - where TInferenceModel : class, new() + UrlInputSource inputSource + , CustomEndpoint endpoint + , PredictOptions predictOptions = null) + where TInferenceModel : GeneratedV1, new() { - _logger?.LogInformation("Enqueuing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Enqueuing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (predictOptions == null) - { - predictOptions = new PredictOptions(); - } - - if (pageOptions != null && inputSource.IsPdf()) - { - inputSource.FileBytes = _pdfOperation.Split( - new SplitQuery(inputSource.FileBytes, pageOptions)).File; - } + predictOptions ??= new PredictOptions(); return await _mindeeApi.PredictAsyncPostAsync( new PredictParameter( - inputSource, null, + inputSource, predictOptions.AllWords, predictOptions.FullText, predictOptions.Cropper, predictOptions.WorkflowId, - predictOptions.Rag)); + predictOptions.Rag) + , endpoint); } /// - /// Add a URL input source to an async queue. + /// Parse a document from a Generated async queue. /// - /// - /// - /// - /// - /// + /// + /// /// + /// The job id. + /// Cancellation token. /// /// Set the prediction model used to parse the document. /// The response object will be instantiated based on this parameter. @@ -582,30 +474,23 @@ LocalInputSource inputSource /// /// /// - /// - public async Task> EnqueueAsync( - UrlInputSource inputSource - , PredictOptions predictOptions = null) - where TInferenceModel : class, new() + public async Task> ParseQueuedAsync( + CustomEndpoint endpoint + , string jobId + , CancellationToken ct = default) + where TInferenceModel : GeneratedV1, new() { - _logger?.LogInformation("Enqueuing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Parse from queue of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (predictOptions == null) + if (string.IsNullOrWhiteSpace(jobId)) { - predictOptions = new PredictOptions(); + throw new ArgumentNullException(jobId); } - return await _mindeeApi.PredictAsyncPostAsync( - new PredictParameter( - null, - inputSource, - predictOptions.AllWords, - predictOptions.FullText, - predictOptions.Cropper, - predictOptions.WorkflowId, - predictOptions.Rag)); + return await _mindeeApi.DocumentQueueGetAsync(jobId, endpoint, ct); } + /// /// Parse a document from an async queue. /// @@ -623,7 +508,7 @@ public async Task> ParseQueuedAsync> ParseQueuedAsync(jobId, null, ct); } + /// + /// Add the document to an async queue, poll, and parse when complete. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Cancellation token. + /// + /// Set the prediction model used to parse the document. + /// The response object will be instantiated based on this parameter. + /// + /// + /// + /// + /// + public async Task> EnqueueAndParseAsync( + LocalInputSource inputSource + , CustomEndpoint endpoint + , PredictOptions predictOptions = null + , PageOptions pageOptions = null + , AsyncPollingOptions pollingOptions = null + , CancellationToken ct = default) + where TInferenceModel : GeneratedV1, new() + { + _logger?.LogInformation("Asynchronous parsing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); + + pollingOptions ??= new AsyncPollingOptions(); + + var enqueueResponse = await EnqueueAsync( + inputSource, + endpoint, + predictOptions, + pageOptions); + + return await PollForResultsAsync(enqueueResponse, endpoint, pollingOptions, ct); + } + + + /// + /// Add the document to an async queue, poll, and parse when complete. URL input version. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Cancellation token. + /// + /// Set the prediction model used to parse the document. + /// The response object will be instantiated based on this parameter. + /// + /// + /// + /// + /// + public async Task> EnqueueAndParseAsync( + UrlInputSource inputSource + , CustomEndpoint endpoint + , PredictOptions predictOptions = null + , AsyncPollingOptions pollingOptions = null + , CancellationToken ct = default) + where TInferenceModel : GeneratedV1, new() + { + _logger?.LogInformation("Asynchronous parsing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); + + pollingOptions ??= new AsyncPollingOptions(); + + var enqueueResponse = await EnqueueAsync( + inputSource, + endpoint, + predictOptions); + + return await PollForResultsAsync(enqueueResponse, endpoint, pollingOptions, ct); + } + /// /// Add the document to an async queue, poll, and parse when complete. /// @@ -665,12 +644,9 @@ LocalInputSource inputSource , CancellationToken ct = default) where TInferenceModel : class, new() { - _logger?.LogInformation("Asynchronous parsing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Asynchronous parsing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (pollingOptions == null) - { - pollingOptions = new AsyncPollingOptions(); - } + pollingOptions ??= new AsyncPollingOptions(); var enqueueResponse = await EnqueueAsync( inputSource, @@ -709,12 +685,9 @@ UrlInputSource inputSource , CancellationToken ct = default) where TInferenceModel : class, new() { - _logger?.LogInformation("Asynchronous parsing of {} ...", typeof(TInferenceModel).Name); + _logger?.LogInformation("Asynchronous parsing of {TInferenceModelName} ...", typeof(TInferenceModel).Name); - if (pollingOptions == null) - { - pollingOptions = new AsyncPollingOptions(); - } + pollingOptions ??= new AsyncPollingOptions(); var enqueueResponse = await EnqueueAsync( inputSource, @@ -745,7 +718,8 @@ public async Task> ExecuteWorkflowAsync( WorkflowOptions workflowOptions = null, PageOptions pageOptions = null) { - _logger?.LogInformation("Sending '{Filename}' to workflow '{WorkflowId}'...", inputSource.Filename, workflowId); + _logger?.LogInformation("Sending '{Filename}' to workflow '{WorkflowId}'...", inputSource.Filename, + workflowId); if (pageOptions != null && inputSource.IsPdf()) { @@ -786,12 +760,9 @@ public async Task> ExecuteWorkflowAsync( UrlInputSource inputSource, WorkflowOptions workflowOptions = null) { - _logger?.LogInformation("Asynchronous parsing of {} ...", inputSource.FileUrl); + _logger?.LogInformation("Asynchronous parsing of {TInferenceModelName} ...", inputSource.FileUrl); - if (workflowOptions == null) - { - workflowOptions = new WorkflowOptions(); - } + workflowOptions ??= new WorkflowOptions(); return await _mindeeApi.PostWorkflowExecution( workflowId, @@ -853,9 +824,9 @@ private async Task> PollForResultsAsync> PollForResultsAsync /// Error sub-object. /// + [SuppressMessage("Minor Code Smell", "S3376:Classes should not be empty", + Justification = "Would be breaking to remove right now. TODO: remove it.")] public class MindeeHttpExceptionV1 : MindeeException { /// @@ -55,5 +58,19 @@ public MindeeHttpExceptionV1(string name, string message, ErrorDetails details, Code = code; } + /// + public MindeeHttpExceptionV1() + { + } + + /// + public MindeeHttpExceptionV1(string message) : base(message) + { + } + + /// + public MindeeHttpExceptionV1(string message, System.Exception innerException) : base(message, innerException) + { + } } } diff --git a/src/Mindee/V1/Http/CustomEndpointAttribute.cs b/src/Mindee/V1/Http/CustomEndpointAttribute.cs index 806bac27d..8e00e3ac9 100644 --- a/src/Mindee/V1/Http/CustomEndpointAttribute.cs +++ b/src/Mindee/V1/Http/CustomEndpointAttribute.cs @@ -9,12 +9,12 @@ namespace Mindee.V1.Http public class CustomEndpointAttribute : EndpointAttribute { /// + /// Custom endpoint attribute for models. /// - /// The name of the product associated to the expected model. - /// The name of the account wich hold the API. Usefull when using custom builder. + /// The name of the product associated with the expected model. + /// The name of the account wich hold the API. Useful when using custom builder. /// - /// The version number of the API. Without the v (for example for the v1.2: 1.2). By default set - /// to 1.0 + /// The version number of the API. Without the v (for example, the v1.2: 1.2). Default to `1`. /// public CustomEndpointAttribute( string modelName diff --git a/src/Mindee/V1/Http/EndpointAttribute.cs b/src/Mindee/V1/Http/EndpointAttribute.cs index 68d7a8fe8..3d7ec2378 100644 --- a/src/Mindee/V1/Http/EndpointAttribute.cs +++ b/src/Mindee/V1/Http/EndpointAttribute.cs @@ -13,12 +13,13 @@ public class EndpointAttribute : Attribute private readonly string _modelVersion; /// + /// Endpoint attribute to target model info. /// - /// The name of the product associated to the expected model. - /// The version number of the API. Without the v (for example for the v1.2: 1.2). + /// The name of the product associated with the expected model. + /// The version number of the API. Without the v (for example, for the v1.2: 1.2). /// - /// The name of the organization wich hold the API. Usefull when using custom builder. By default - /// to mindee. + /// The name of the organization that holds the API. Useful when using custom builder. + /// Defaults to `mindee`. /// public EndpointAttribute( string modelName diff --git a/src/Mindee/V1/Http/GenericParameter.cs b/src/Mindee/V1/Http/GenericParameter.cs index 395e4852a..c964ff531 100644 --- a/src/Mindee/V1/Http/GenericParameter.cs +++ b/src/Mindee/V1/Http/GenericParameter.cs @@ -9,6 +9,7 @@ namespace Mindee.V1.Http public class GenericParameter { /// + /// Generic prediction parameters. /// /// /// diff --git a/src/Mindee/V1/Parsing/Common/ApiRequest.cs b/src/Mindee/V1/Parsing/Common/ApiRequest.cs index da39713d4..a7a3f1a8a 100644 --- a/src/Mindee/V1/Parsing/Common/ApiRequest.cs +++ b/src/Mindee/V1/Parsing/Common/ApiRequest.cs @@ -15,6 +15,7 @@ public class ApiRequest public Error Error { get; set; } /// + /// Resources used by the request. /// [JsonPropertyName("resources")] [System.Diagnostics.CodeAnalysis.SuppressMessage("ReSharper", "CollectionNeverUpdated.Global")] diff --git a/src/Mindee/V1/Parsing/Common/DateTimeJsonConverter.cs b/src/Mindee/V1/Parsing/Common/DateTimeJsonConverter.cs index c4e89770e..908bbec57 100644 --- a/src/Mindee/V1/Parsing/Common/DateTimeJsonConverter.cs +++ b/src/Mindee/V1/Parsing/Common/DateTimeJsonConverter.cs @@ -25,7 +25,7 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso } // If the string ends with "Z", replace it with "+00:00" to represent UTC. - if (dateString.EndsWith("Z")) + if (dateString.EndsWith("Z", StringComparison.Ordinal)) { dateString = dateString.Substring(0, dateString.Length - 1) + "+00:00"; } @@ -38,7 +38,7 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso } } - return DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); + return DateTime.Parse(dateString, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToUniversalTime(); } /// diff --git a/src/Mindee/V1/Parsing/Common/ErrorDetails.cs b/src/Mindee/V1/Parsing/Common/ErrorDetails.cs index a4e9b61e9..134451a9b 100644 --- a/src/Mindee/V1/Parsing/Common/ErrorDetails.cs +++ b/src/Mindee/V1/Parsing/Common/ErrorDetails.cs @@ -3,12 +3,13 @@ namespace Mindee.V1.Parsing.Common { /// - /// Represent an error information from the API response. + /// Represent error information from the API response. /// [JsonConverter(typeof(ErrorDetailsJsonConverter))] public class ErrorDetails { /// + /// Error details from string. /// /// /// diff --git a/src/Mindee/V1/Parsing/Common/Inference.cs b/src/Mindee/V1/Parsing/Common/Inference.cs index 0216e8cd6..55e6e5ead 100644 --- a/src/Mindee/V1/Parsing/Common/Inference.cs +++ b/src/Mindee/V1/Parsing/Common/Inference.cs @@ -45,7 +45,7 @@ public InferenceExtras Extras if (Pages.Count > 0 && _extras?.FullTextOcr == null) { _extras ??= new InferenceExtras(); - if (Pages.First().Extras is { FullTextOcr: not null }) + if (Pages[0].Extras is { FullTextOcr: not null }) { _extras.FullTextOcr = string.Join("\n", Pages.Select(page => page.Extras.FullTextOcr.Content)); diff --git a/src/Mindee/V1/Parsing/Common/Ocr.cs b/src/Mindee/V1/Parsing/Common/Ocr.cs index 231402f3a..80ec28ddf 100644 --- a/src/Mindee/V1/Parsing/Common/Ocr.cs +++ b/src/Mindee/V1/Parsing/Common/Ocr.cs @@ -151,7 +151,13 @@ protected List> ToLines() return lines; } - private bool AreWordsOnSameLine(Word currentWord, Word nextWord) + /// + /// Checks whether two words are on the same line. + /// + /// + /// + /// + private static bool AreWordsOnSameLine(Word currentWord, Word nextWord) { var currentInNext = currentWord.Polygon.IsPointInY(nextWord.Polygon.GetCentroid()); var nextInCurrent = nextWord.Polygon.IsPointInY(currentWord.Polygon.GetCentroid()); diff --git a/src/Mindee/V1/Parsing/Common/Pages.cs b/src/Mindee/V1/Parsing/Common/Pages.cs index a4a68f165..e35f366da 100644 --- a/src/Mindee/V1/Parsing/Common/Pages.cs +++ b/src/Mindee/V1/Parsing/Common/Pages.cs @@ -24,7 +24,7 @@ public override string ToString() /// public bool HasPredictions() { - return Count > 0 && this.First().Prediction != null; + return Count > 0 && !object.Equals(this[0].Prediction, default(TPagePrediction)); } } } diff --git a/src/Mindee/V1/Parsing/Common/PagesJsonConverter.cs b/src/Mindee/V1/Parsing/Common/PagesJsonConverter.cs index 4352e703f..bbddc207f 100644 --- a/src/Mindee/V1/Parsing/Common/PagesJsonConverter.cs +++ b/src/Mindee/V1/Parsing/Common/PagesJsonConverter.cs @@ -38,6 +38,7 @@ public override Pages Read(ref Utf8JsonReader reader, Type typeToConvert, } /// + /// Write the pages to the JSON writer. /// public override void Write(Utf8JsonWriter writer, Pages value, JsonSerializerOptions options) { diff --git a/src/Mindee/V1/Parsing/Generated/GeneratedFeature.cs b/src/Mindee/V1/Parsing/Generated/GeneratedFeature.cs index 4d5c872f5..ea39bfc29 100644 --- a/src/Mindee/V1/Parsing/Generated/GeneratedFeature.cs +++ b/src/Mindee/V1/Parsing/Generated/GeneratedFeature.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Text; using Mindee.Exceptions; using Mindee.V1.Parsing.Standard; @@ -39,7 +38,7 @@ public StringField AsStringField() throw new MindeeException("Cannot convert a list feature into a StringField."); } - return this.First().AsStringField(); + return this[0].AsStringField(); } /// @@ -54,7 +53,7 @@ public AmountField AsAmountField() throw new MindeeException("Cannot convert a list feature into an AmountField."); } - return this.First().AsAmountField(); + return this[0].AsAmountField(); } /// @@ -69,7 +68,7 @@ public DecimalField AsDecimalField() throw new MindeeException("Cannot convert a list feature into a DecimalField."); } - return this.First().AsDecimalField(); + return this[0].AsDecimalField(); } /// @@ -84,7 +83,7 @@ public DateField AsDateField() throw new MindeeException("Cannot convert a list feature into a DateField."); } - return this.First().AsDateField(); + return this[0].AsDateField(); } /// @@ -99,7 +98,7 @@ public ClassificationField AsClassificationField() throw new MindeeException("Cannot convert a list feature into a ClassificationField."); } - return this.First().AsClassificationField(); + return this[0].AsClassificationField(); } /// @@ -114,7 +113,7 @@ public BooleanField AsBooleanField() throw new MindeeException("Cannot convert a list feature into a BooleanField."); } - return this.First().AsBooleanField(); + return this[0].AsBooleanField(); } /// @@ -140,7 +139,7 @@ public override string ToString() } else { - result.Append($"\n{this.First().ToString(2)}"); + result.Append($"\n{this[0].ToString(2)}"); } return result.ToString(); diff --git a/src/Mindee/V1/Parsing/Generated/GeneratedObject.cs b/src/Mindee/V1/Parsing/Generated/GeneratedObject.cs index 04b06bb0d..2e50098dd 100644 --- a/src/Mindee/V1/Parsing/Generated/GeneratedObject.cs +++ b/src/Mindee/V1/Parsing/Generated/GeneratedObject.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text; using System.Text.Json; using Mindee.Geometry; @@ -185,14 +186,11 @@ public Polygon Polygon() /// /// Get the specified key as a object. /// + [SuppressMessage("Minor Code Smell", "S1168: Return an empty collection instead of null.", + Justification = "Would be breaking for end-users to right now. TODO: return [] instead of null")] public Polygon TryGetPolygon(string key) { - if (ContainsKey(key)) - { - return ConvertElementToPolygon(this[key]); - } - - return null; + return ContainsKey(key) ? ConvertElementToPolygon(this[key]) : null; } /// diff --git a/src/Mindee/V1/Parsing/Standard/BaseField.cs b/src/Mindee/V1/Parsing/Standard/BaseField.cs index e0186ff1a..21a51a4ce 100644 --- a/src/Mindee/V1/Parsing/Standard/BaseField.cs +++ b/src/Mindee/V1/Parsing/Standard/BaseField.cs @@ -9,6 +9,7 @@ namespace Mindee.V1.Parsing.Standard public abstract class BaseField { /// + /// Base for V1 fields. /// /// /// @@ -27,6 +28,7 @@ protected BaseField(double? confidence, Polygon polygon, int? pageId) } /// + /// Empty base field. /// protected BaseField() { } diff --git a/src/Mindee/V1/Parsing/Standard/BooleanField.cs b/src/Mindee/V1/Parsing/Standard/BooleanField.cs index db004cc81..6fa9e3097 100644 --- a/src/Mindee/V1/Parsing/Standard/BooleanField.cs +++ b/src/Mindee/V1/Parsing/Standard/BooleanField.cs @@ -10,6 +10,7 @@ namespace Mindee.V1.Parsing.Standard public class BooleanField : BaseField { /// + /// Boolean field. /// /// /// diff --git a/src/Mindee/V1/Parsing/Standard/DateField.cs b/src/Mindee/V1/Parsing/Standard/DateField.cs index 252c02341..aaa755a9f 100644 --- a/src/Mindee/V1/Parsing/Standard/DateField.cs +++ b/src/Mindee/V1/Parsing/Standard/DateField.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using Microsoft.Extensions.Logging; using Mindee.Geometry; @@ -45,11 +46,11 @@ public DateField( try { - DateObject = DateTime.Parse(Value); + DateObject = DateTime.Parse(Value, CultureInfo.InvariantCulture); } - catch (FormatException) + catch (FormatException exc) { - logger?.LogWarning("Unable to parse the date: {}", Value); + logger?.LogWarning(exc, "Unable to parse the date: {Value}", Value); } } diff --git a/src/Mindee/V1/Parsing/Standard/DecimalJsonConverter.cs b/src/Mindee/V1/Parsing/Standard/DecimalJsonConverter.cs index 54632111a..d42fb5be1 100644 --- a/src/Mindee/V1/Parsing/Standard/DecimalJsonConverter.cs +++ b/src/Mindee/V1/Parsing/Standard/DecimalJsonConverter.cs @@ -6,10 +6,12 @@ namespace Mindee.V1.Parsing.Standard { /// + /// Custom JSON converter for decimal values. /// public class DecimalJsonConverter : JsonConverter { /// + /// Read a decimal value from a JSON string. /// public override decimal? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/src/Mindee/V1/Parsing/Standard/Locale.cs b/src/Mindee/V1/Parsing/Standard/Locale.cs index 355c7a547..46a0fe840 100644 --- a/src/Mindee/V1/Parsing/Standard/Locale.cs +++ b/src/Mindee/V1/Parsing/Standard/Locale.cs @@ -78,6 +78,7 @@ public Locale( public string Value { get; set; } /// + /// String representation. /// /// A pretty summary of the value. public override string ToString() diff --git a/src/Mindee/V1/Parsing/Standard/StringField.cs b/src/Mindee/V1/Parsing/Standard/StringField.cs index 9efcd9386..e2b1e74ec 100644 --- a/src/Mindee/V1/Parsing/Standard/StringField.cs +++ b/src/Mindee/V1/Parsing/Standard/StringField.cs @@ -10,6 +10,7 @@ namespace Mindee.V1.Parsing.Standard public class StringField : BaseField { /// + /// String field. /// /// /// diff --git a/src/Mindee/V1/Parsing/Standard/TimeField.cs b/src/Mindee/V1/Parsing/Standard/TimeField.cs index 7f2f4a996..ac60e9cb5 100644 --- a/src/Mindee/V1/Parsing/Standard/TimeField.cs +++ b/src/Mindee/V1/Parsing/Standard/TimeField.cs @@ -8,6 +8,7 @@ namespace Mindee.V1.Parsing.Standard public class TimeField : StringField { /// + /// Time representation. /// /// /// diff --git a/src/Mindee/V1/Product/Generated/GeneratedV1DocumentJsonConverter.cs b/src/Mindee/V1/Product/Generated/GeneratedV1DocumentJsonConverter.cs index 060499b42..ba8d5b42b 100644 --- a/src/Mindee/V1/Product/Generated/GeneratedV1DocumentJsonConverter.cs +++ b/src/Mindee/V1/Product/Generated/GeneratedV1DocumentJsonConverter.cs @@ -27,10 +27,10 @@ public override GeneratedV1Document Read(ref Utf8JsonReader reader, Type typeToC { GeneratedFeature feature; - if (jsonNode.Value is JsonArray) + if (jsonNode.Value is JsonArray jsonArray) { feature = new GeneratedFeature(true); - foreach (var featureValue in (JsonArray)jsonNode.Value) + foreach (var featureValue in jsonArray) { feature.Add(featureValue.Deserialize()); } diff --git a/src/Mindee/V2/Client.cs b/src/Mindee/V2/Client.cs index c5a542780..70f076a28 100644 --- a/src/Mindee/V2/Client.cs +++ b/src/Mindee/V2/Client.cs @@ -30,6 +30,7 @@ public sealed class Client private readonly HttpApiV2 _mindeeApi; /// + /// Default Client constructor for V2. /// /// The required API key to use the Mindee V2 API. /// Factory for the logger. @@ -53,6 +54,7 @@ public Client(string apiKey, ILoggerFactory loggerFactory = null) } /// + /// API Key-less constructor for V2. /// /// /// @@ -80,6 +82,7 @@ public Client(SettingsV2 settings, ILoggerFactory logger = null) } /// + /// Constructor with custom API module. /// /// /// @@ -139,7 +142,7 @@ InputSource inputSource /// public async Task GetJobFromUrlAsync(string pollingUrl, CancellationToken ct = default) { - _logger?.LogInformation("Getting Job at: {}", pollingUrl); + _logger?.LogInformation("Getting Job at: {PollingUrl}", pollingUrl); if (string.IsNullOrWhiteSpace(pollingUrl)) { @@ -160,7 +163,7 @@ public async Task GetJobFromUrlAsync(string pollingUrl, Cancellatio public async Task GetResultFromUrlAsync(string resultUrl, CancellationToken ct = default) where TResponse : BaseResponse, new() { - _logger?.LogInformation("Getting result at: {}", resultUrl); + _logger?.LogInformation("Getting result at: {ResultUrl}", resultUrl); if (string.IsNullOrWhiteSpace(resultUrl)) { @@ -181,7 +184,7 @@ public async Task GetResultFromUrlAsync(string resultUrl, public async Task GetResultAsync(string jobId, CancellationToken ct = default) where TResponse : BaseResponse, new() { - _logger?.LogInformation("Getting result with ID: {}", jobId); + _logger?.LogInformation("Getting result with ID: {JobId}", jobId); if (string.IsNullOrWhiteSpace(jobId)) { @@ -201,7 +204,7 @@ public async Task GetResultAsync(string jobId, Cancellatio /// public async Task GetJobAsync(string jobId, CancellationToken ct = default) { - _logger?.LogInformation("Getting job ID: {}", jobId); + _logger?.LogInformation("Getting job ID: {JobId}", jobId); if (string.IsNullOrWhiteSpace(jobId)) { @@ -245,26 +248,27 @@ InputSource inputSource } /// - /// Returns a list of models matching the given criteria. + /// Returns a list of RAG documents matching the given criteria. /// - /// + /// /// Cancellation token. - public async Task SearchModels( - ModelSearchParameters searchParameters, CancellationToken ct = default) + public async Task SearchRagDocuments( + RagDocumentSearchParameters searchParameters, CancellationToken ct = default) { - var parameters = searchParameters ?? new ModelSearchParameters(); - return await _mindeeApi.SearchModels(parameters, ct); + return await _mindeeApi.SearchRagDocuments(searchParameters, ct); } + /// - /// Returns a list of RAG documents matching the given criteria. + /// Returns a list of models matching the given criteria. /// - /// + /// /// Cancellation token. - public async Task SearchRagDocuments( - RagDocumentSearchParameters searchParameters, CancellationToken ct = default) + public async Task SearchModels( + ModelSearchParameters searchParameters, CancellationToken ct = default) { - return await _mindeeApi.SearchRagDocuments(searchParameters, ct); + var parameters = searchParameters ?? new ModelSearchParameters(); + return await _mindeeApi.SearchModels(parameters, ct); } /// @@ -302,9 +306,9 @@ private async Task PollForResultsAsync( { var maxRetries = pollingOptions.MaxRetries + 1; var pollingUrl = enqueueResponse.Job.PollingUrl; - _logger?.LogInformation("Enqueued with job ID: {}", enqueueResponse.Job.Id); + _logger?.LogInformation("Enqueued with job ID: {JobId}", enqueueResponse.Job.Id); _logger?.LogInformation( - "Waiting {} seconds before attempting to retrieve the document...", + "Waiting {InitialDelaySec} seconds before attempting to retrieve the document...", pollingOptions.InitialDelaySec); await Task.Delay(pollingOptions.InitialDelayMilliSec, ct); var retryCount = 1; diff --git a/src/Mindee/V2/Exceptions/MindeeHttpExceptionV2.cs b/src/Mindee/V2/Exceptions/MindeeHttpExceptionV2.cs index 03ca5dad8..0cfd5ab7a 100644 --- a/src/Mindee/V2/Exceptions/MindeeHttpExceptionV2.cs +++ b/src/Mindee/V2/Exceptions/MindeeHttpExceptionV2.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Mindee.V2.Parsing; namespace Mindee.V2.Exceptions @@ -7,6 +8,8 @@ namespace Mindee.V2.Exceptions /// /// Representation of a Mindee API V2 exception. /// + [SuppressMessage("Minor Code Smell", "S3376:Classes should not be empty", + Justification = "Would be breaking to remove right now. TODO: remove it.")] public class MindeeHttpExceptionV2 : Exception, IErrorResponse { /// @@ -23,6 +26,21 @@ public MindeeHttpExceptionV2(ErrorResponse error) Errors = error.Errors; } + /// + public MindeeHttpExceptionV2() + { + } + + /// + public MindeeHttpExceptionV2(string message) : base(message) + { + } + + /// + public MindeeHttpExceptionV2(string message, Exception innerException) : base(message, innerException) + { + } + /// public string Detail { get; set; } diff --git a/src/Mindee/V2/FileOperations/Crop.cs b/src/Mindee/V2/FileOperations/Crop.cs index 596011152..2711188d8 100644 --- a/src/Mindee/V2/FileOperations/Crop.cs +++ b/src/Mindee/V2/FileOperations/Crop.cs @@ -19,7 +19,7 @@ public sealed class Crop private readonly LocalInputSource _localInput; /// - /// + /// Crop object from an input source. /// /// public Crop(LocalInputSource inputSource) diff --git a/src/Mindee/V2/FileOperations/CropFiles.cs b/src/Mindee/V2/FileOperations/CropFiles.cs index f547761a4..991d4672f 100644 --- a/src/Mindee/V2/FileOperations/CropFiles.cs +++ b/src/Mindee/V2/FileOperations/CropFiles.cs @@ -10,7 +10,7 @@ namespace Mindee.V2.FileOperations public class CropFiles : List { /// - /// + /// Crop files from a collection of extracted images. /// /// public CropFiles(IEnumerable collection) : base(collection) @@ -18,9 +18,9 @@ public CropFiles(IEnumerable collection) : base(collection) } /// - /// + /// Empty crop files. /// - public CropFiles() : base() + public CropFiles() { } diff --git a/src/Mindee/V2/FileOperations/SplitFiles.cs b/src/Mindee/V2/FileOperations/SplitFiles.cs index 443f08478..f497c1fb2 100644 --- a/src/Mindee/V2/FileOperations/SplitFiles.cs +++ b/src/Mindee/V2/FileOperations/SplitFiles.cs @@ -10,7 +10,7 @@ namespace Mindee.V2.FileOperations public sealed class SplitFiles : List { /// - /// + /// Split files from a collection of extracted PDFs. /// /// public SplitFiles(IEnumerable collection) : base(collection) @@ -18,9 +18,9 @@ public SplitFiles(IEnumerable collection) : base(collection) } /// - /// + /// Empty split files. /// - public SplitFiles() : base() + public SplitFiles() { } diff --git a/src/Mindee/V2/Http/MindeeApiV2.cs b/src/Mindee/V2/Http/MindeeApiV2.cs index 097bf06e0..1798a6e59 100644 --- a/src/Mindee/V2/Http/MindeeApiV2.cs +++ b/src/Mindee/V2/Http/MindeeApiV2.cs @@ -57,9 +57,9 @@ public override async Task ReqPostEnqueueAsync( CancellationToken ct = default ) { - var productAttributes = parameters.GetType().GetCustomAttribute(); + var productAttributes = parameters.GetType().GetCustomAttribute(); if (productAttributes == null) - throw new Exception($"ProductAttributes must be set for class: {parameters.GetType().Name}"); + throw new MindeeException($"ProductAttributes must be set for class: {parameters.GetType().Name}"); var request = new RestRequest( $"v2/products/{productAttributes.Slug}/enqueue", Method.Post); @@ -145,9 +145,9 @@ public override async Task ReqGetJobFromUrlAsync(string pollingUrl, public override async Task ReqGetResultAsync(string inferenceId, CancellationToken ct = default) { - var productAttributes = typeof(TResponse).GetCustomAttribute(); + var productAttributes = typeof(TResponse).GetCustomAttribute(); if (productAttributes == null) - throw new Exception($"ProductAttributes must be set for class: {typeof(TResponse).Name}"); + throw new MindeeException($"ProductAttributes must be set for class: {typeof(TResponse).Name}"); var request = new RestRequest( $"v2/products/{productAttributes.Slug}/results/{inferenceId}"); Logger?.LogInformation("HTTP GET to {RequestResource}...", request.Resource); diff --git a/src/Mindee/V2/Http/Settings.cs b/src/Mindee/V2/Http/Settings.cs index 0e583a077..b56683ea1 100644 --- a/src/Mindee/V2/Http/Settings.cs +++ b/src/Mindee/V2/Http/Settings.cs @@ -1,7 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + namespace Mindee.V2.Http { /// /// Mindee V2 settings. /// + [SuppressMessage("Minor Code Smell", "S2094:Classes should not be empty", + Justification = "Used as a base type for strict pattern matching in the public API.")] public class Settings : V1.Http.Settings; } diff --git a/src/Mindee/V2/Parsing/DateTimeJsonConverter.cs b/src/Mindee/V2/Parsing/DateTimeJsonConverter.cs index 85b6089f7..b2e99b3f9 100644 --- a/src/Mindee/V2/Parsing/DateTimeJsonConverter.cs +++ b/src/Mindee/V2/Parsing/DateTimeJsonConverter.cs @@ -25,7 +25,7 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso } // If the string ends with "Z", replace it with "+00:00" to represent UTC. - if (dateString.EndsWith("Z")) + if (dateString.EndsWith("Z", StringComparison.Ordinal)) { dateString = dateString.Substring(0, dateString.Length - 1) + "+00:00"; } @@ -38,7 +38,7 @@ public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, Jso } } - return DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind).ToUniversalTime(); + return DateTime.Parse(dateString, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind).ToUniversalTime(); } /// diff --git a/src/Mindee/V2/Parsing/Inference/BaseInference.cs b/src/Mindee/V2/Parsing/Inference/BaseInference.cs index a27dc9701..470b22c81 100644 --- a/src/Mindee/V2/Parsing/Inference/BaseInference.cs +++ b/src/Mindee/V2/Parsing/Inference/BaseInference.cs @@ -1,4 +1,3 @@ -using System; using System.Text; using System.Text.Json.Serialization; using Mindee.Parsing; @@ -36,12 +35,7 @@ public abstract class BaseInference public InferenceJob Job { get; set; } /// - /// Type of the product's response. - /// - public virtual Type ResponseType { get; set; } - - /// - /// + /// String representation of the inference. /// /// public override string ToString() diff --git a/src/Mindee/V2/Parsing/Inference/Field/DynamicField.cs b/src/Mindee/V2/Parsing/Inference/Field/DynamicField.cs index 3d56872c3..d1472eeff 100644 --- a/src/Mindee/V2/Parsing/Inference/Field/DynamicField.cs +++ b/src/Mindee/V2/Parsing/Inference/Field/DynamicField.cs @@ -29,6 +29,7 @@ public enum FieldType [JsonConverter(typeof(DynamicFieldJsonConverter))] public class DynamicField { +#pragma warning disable S1104 // TODO: Remove this if applicable. /// /// Value as list field. /// @@ -48,7 +49,7 @@ public class DynamicField /// The type of field. /// public FieldType Type; - +#pragma warning restore S1104 /// /// Return the field class dynamically. /// diff --git a/src/Mindee/V2/Parsing/Inference/Field/ListField.cs b/src/Mindee/V2/Parsing/Inference/Field/ListField.cs index d349b56c8..c69d58839 100644 --- a/src/Mindee/V2/Parsing/Inference/Field/ListField.cs +++ b/src/Mindee/V2/Parsing/Inference/Field/ListField.cs @@ -10,10 +10,6 @@ namespace Mindee.V2.Parsing.Inference.Field /// public class ListField : BaseField { - private List _objectItems; - - private List _simpleItems; - /// /// List field. /// @@ -35,21 +31,18 @@ public List SimpleItems { get { - if (_simpleItems != null) + if (field != null) { - return _simpleItems; + return field; } - _simpleItems = new List(); - foreach (var item in Items) + field = []; + foreach (var item in Items.Where(item => item.SimpleField != null)) { - if (item.SimpleField != null) - { - _simpleItems.Add(item.SimpleField); - } + field.Add(item.SimpleField); } - return _simpleItems; + return field; } } @@ -60,18 +53,18 @@ public List ObjectItems { get { - if (_objectItems != null) + if (field != null) { - return _objectItems; + return field; } - _objectItems = []; + field = []; foreach (var item in Items.Where(item => item.ObjectField != null)) { - _objectItems.Add(item.ObjectField); + field.Add(item.ObjectField); } - return _objectItems; + return field; } } diff --git a/src/Mindee/V2/Product/Classification/ClassificationResponse.cs b/src/Mindee/V2/Product/Classification/ClassificationResponse.cs index b3567af71..92dddbff3 100644 --- a/src/Mindee/V2/Product/Classification/ClassificationResponse.cs +++ b/src/Mindee/V2/Product/Classification/ClassificationResponse.cs @@ -6,11 +6,11 @@ namespace Mindee.V2.Product.Classification /// /// Response for a classification utility inference. /// - [ProductAttributes("classification")] + [Product("classification")] public class ClassificationResponse : BaseResponse { /// - /// + /// Inference for a classification utility. /// [JsonPropertyName("inference")] public ClassificationInference Inference { get; set; } diff --git a/src/Mindee/V2/Product/Classification/Params/ClassificationParameters.cs b/src/Mindee/V2/Product/Classification/Params/ClassificationParameters.cs index daeb9f649..4603f48de 100644 --- a/src/Mindee/V2/Product/Classification/Params/ClassificationParameters.cs +++ b/src/Mindee/V2/Product/Classification/Params/ClassificationParameters.cs @@ -6,7 +6,7 @@ namespace Mindee.V2.Product.Classification.Params /// /// Parameters for a classification utility inference. /// - [ProductAttributes("classification")] + [Product("classification")] public class ClassificationParameters : BaseProductParameters { /// diff --git a/src/Mindee/V2/Product/Crop/CropResponse.cs b/src/Mindee/V2/Product/Crop/CropResponse.cs index 1e876fe1a..e2733c164 100644 --- a/src/Mindee/V2/Product/Crop/CropResponse.cs +++ b/src/Mindee/V2/Product/Crop/CropResponse.cs @@ -6,7 +6,7 @@ namespace Mindee.V2.Product.Crop /// /// Represent a crop response from Mindee V2 API. /// - [ProductAttributes("crop")] + [Product("crop")] public class CropResponse : BaseResponse { /// diff --git a/src/Mindee/V2/Product/Crop/Params/CropParameters.cs b/src/Mindee/V2/Product/Crop/Params/CropParameters.cs index 2cd88d32e..1576491e0 100644 --- a/src/Mindee/V2/Product/Crop/Params/CropParameters.cs +++ b/src/Mindee/V2/Product/Crop/Params/CropParameters.cs @@ -6,7 +6,7 @@ namespace Mindee.V2.Product.Crop.Params /// /// Parameters accepted by the crop utility v2 endpoint. /// - [ProductAttributes("crop")] + [Product("crop")] public class CropParameters : BaseProductParameters { /// diff --git a/src/Mindee/V2/Product/Extraction/ExtractionResponse.cs b/src/Mindee/V2/Product/Extraction/ExtractionResponse.cs index a7134a9ca..9012bdea6 100644 --- a/src/Mindee/V2/Product/Extraction/ExtractionResponse.cs +++ b/src/Mindee/V2/Product/Extraction/ExtractionResponse.cs @@ -6,7 +6,7 @@ namespace Mindee.V2.Product.Extraction /// /// Response for an extraction inference. /// - [ProductAttributes("extraction")] + [Product("extraction")] public class ExtractionResponse : BaseResponse { /// diff --git a/src/Mindee/V2/Product/Extraction/Params/ExtractionParameters.cs b/src/Mindee/V2/Product/Extraction/Params/ExtractionParameters.cs index 9c785438b..188396c1e 100644 --- a/src/Mindee/V2/Product/Extraction/Params/ExtractionParameters.cs +++ b/src/Mindee/V2/Product/Extraction/Params/ExtractionParameters.cs @@ -9,7 +9,7 @@ namespace Mindee.V2.Product.Extraction.Params /// /// Parameters for an extraction inference. /// - [ProductAttributes("extraction")] + [Product("extraction")] public class ExtractionParameters : BaseProductParameters { /// diff --git a/src/Mindee/V2/Product/Ocr/OcrResponse.cs b/src/Mindee/V2/Product/Ocr/OcrResponse.cs index 81f80160f..efe87b77a 100644 --- a/src/Mindee/V2/Product/Ocr/OcrResponse.cs +++ b/src/Mindee/V2/Product/Ocr/OcrResponse.cs @@ -6,7 +6,7 @@ namespace Mindee.V2.Product.Ocr /// /// Response for an OCR utility inference. /// - [ProductAttributes("ocr")] + [Product("ocr")] public class OcrResponse : BaseResponse { /// diff --git a/src/Mindee/V2/Product/Ocr/Params/OcrParameters.cs b/src/Mindee/V2/Product/Ocr/Params/OcrParameters.cs index 371669a34..3557c9257 100644 --- a/src/Mindee/V2/Product/Ocr/Params/OcrParameters.cs +++ b/src/Mindee/V2/Product/Ocr/Params/OcrParameters.cs @@ -6,7 +6,7 @@ namespace Mindee.V2.Product.Ocr.Params /// /// Parameters accepted by the OCR utility v2 endpoint. /// - [ProductAttributes("ocr")] + [Product("ocr")] public class OcrParameters : BaseProductParameters { diff --git a/src/Mindee/V2/Product/ProductAttributes.cs b/src/Mindee/V2/Product/ProductAttribute.cs similarity index 76% rename from src/Mindee/V2/Product/ProductAttributes.cs rename to src/Mindee/V2/Product/ProductAttribute.cs index 5a1fdb1af..78e1edf2e 100644 --- a/src/Mindee/V2/Product/ProductAttributes.cs +++ b/src/Mindee/V2/Product/ProductAttribute.cs @@ -6,17 +6,17 @@ namespace Mindee.V2.Product /// Attribute to specify various product metadata. /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] - public sealed class ProductAttributes : Attribute + public sealed class ProductAttribute : Attribute { /// /// URL slug of the product. /// - public string Slug; + public string Slug { get; } /// /// Attribute to specify various product metadata. /// - public ProductAttributes(string slug) + public ProductAttribute(string slug) { Slug = slug; } diff --git a/src/Mindee/V2/Product/Split/Params/SplitParameters.cs b/src/Mindee/V2/Product/Split/Params/SplitParameters.cs index 83ac55ca0..e9d1c7f46 100644 --- a/src/Mindee/V2/Product/Split/Params/SplitParameters.cs +++ b/src/Mindee/V2/Product/Split/Params/SplitParameters.cs @@ -6,7 +6,7 @@ namespace Mindee.V2.Product.Split.Params /// /// Parameters accepted by the split utility v2 endpoint. /// - [ProductAttributes("split")] + [Product("split")] public class SplitParameters : BaseProductParameters { /// diff --git a/src/Mindee/V2/Product/Split/SplitResponse.cs b/src/Mindee/V2/Product/Split/SplitResponse.cs index 270c48293..47cc8e862 100644 --- a/src/Mindee/V2/Product/Split/SplitResponse.cs +++ b/src/Mindee/V2/Product/Split/SplitResponse.cs @@ -6,7 +6,7 @@ namespace Mindee.V2.Product.Split /// /// Represent a split response from Mindee V2 API. /// - [ProductAttributes("split")] + [Product("split")] public class SplitResponse : BaseResponse { /// diff --git a/tests/Mindee.IntegrationTests/V1/ClientTest.cs b/tests/Mindee.IntegrationTests/V1/ClientTest.cs index 7ca6d6302..d165ece0c 100644 --- a/tests/Mindee.IntegrationTests/V1/ClientTest.cs +++ b/tests/Mindee.IntegrationTests/V1/ClientTest.cs @@ -1,4 +1,4 @@ -using Mindee.Exceptions; +using System.Diagnostics.CodeAnalysis; using Mindee.Input; using Mindee.V1; using Mindee.V1.ClientOptions; @@ -36,7 +36,7 @@ public async Task Parse_File_Standard_MultiplePages_MustSucceed() Assert.Null(response.Document.Ocr); Assert.NotNull(response.Document.Inference); Assert.NotNull(response.Document.Inference.Prediction); - Assert.Null(response.Document.Inference.Pages.First().Extras); + Assert.Null(response.Document.Inference.Pages[0].Extras); Assert.Equal(2, response.Document.Inference.Pages.Count); } @@ -52,7 +52,7 @@ public async Task Parse_File_Standard_SinglePage_MustSucceed() Assert.NotNull(response.Document.Inference); Assert.NotNull(response.Document.Inference.Prediction); Assert.Single(response.Document.Inference.Pages); - Assert.Null(response.Document.Inference.Pages.First().Extras); + Assert.Null(response.Document.Inference.Pages[0].Extras); } [Fact(Timeout = 180000)] @@ -69,7 +69,7 @@ public async Task Parse_Url_Standard_SinglePage_MustSucceed() Assert.NotNull(response.Document.Inference); Assert.NotNull(response.Document.Inference.Prediction); Assert.Single(response.Document.Inference.Pages); - Assert.Null(response.Document.Inference.Pages.First().Extras); + Assert.Null(response.Document.Inference.Pages[0].Extras); } [Fact(Timeout = 180000)] @@ -92,8 +92,8 @@ public async Task Parse_File_Cropper_MustSucceed() Assert.NotNull(response.Document.Inference); Assert.NotNull(response.Document.Inference.Prediction); Assert.Single(response.Document.Inference.Pages); - Assert.NotNull(response.Document.Inference.Pages.First().Extras.Cropper); - Assert.Single(response.Document.Inference.Pages.First().Extras.Cropper.Cropping); + Assert.NotNull(response.Document.Inference.Pages[0].Extras.Cropper); + Assert.Single(response.Document.Inference.Pages[0].Extras.Cropper.Cropping); } [Fact(Timeout = 180000)] @@ -107,11 +107,11 @@ public async Task Parse_File_Standard_AllWords_MustSucceed() Assert.Equal(201, response.ApiRequest.StatusCode); Assert.NotNull(response.Document.Ocr.ToString()); Assert.Single(response.Document.Ocr.MvisionV1.Pages); - Assert.NotEmpty(response.Document.Ocr.MvisionV1.Pages.First().AllWords); + Assert.NotEmpty(response.Document.Ocr.MvisionV1.Pages[0].AllWords); Assert.NotNull(response.Document.Inference); Assert.NotNull(response.Document.Inference.Prediction); Assert.Single(response.Document.Inference.Pages); - Assert.Null(response.Document.Inference.Pages.First().Extras); + Assert.Null(response.Document.Inference.Pages[0].Extras); } [Fact(Timeout = 180000)] @@ -123,9 +123,9 @@ public async Task Parse_File_Standard_FullText_MustSucceed() Assert.NotNull(response); Assert.Equal("success", response.ApiRequest.Status); Assert.Equal(200, response.ApiRequest.StatusCode); - Assert.NotNull(response.Document.Inference.Pages.First().Extras.FullTextOcr); + Assert.NotNull(response.Document.Inference.Pages[0].Extras.FullTextOcr); Assert.NotNull(response.Document.Inference.Extras.FullTextOcr); - Assert.Equal(response.Document.Inference.Pages.First().Extras.FullTextOcr.Content, + Assert.Equal(response.Document.Inference.Pages[0].Extras.FullTextOcr.Content, response.Document.Inference.Extras.FullTextOcr); Assert.True(response.Document.Inference.Extras.FullTextOcr.Replace(" ", "").Length > 100); } @@ -142,12 +142,12 @@ public async Task Parse_File_Standard_AllWords_And_Cropper_MustSucceed() Assert.Equal(201, response.ApiRequest.StatusCode); Assert.NotNull(response.Document.Ocr); Assert.Single(response.Document.Ocr.MvisionV1.Pages); - Assert.NotEmpty(response.Document.Ocr.MvisionV1.Pages.First().AllWords); + Assert.NotEmpty(response.Document.Ocr.MvisionV1.Pages[0].AllWords); Assert.NotNull(response.Document.Inference); Assert.NotNull(response.Document.Inference.Prediction); Assert.Single(response.Document.Inference.Pages); - Assert.NotNull(response.Document.Inference.Pages.First().Extras.Cropper); - Assert.Single(response.Document.Inference.Pages.First().Extras.Cropper.Cropping); + Assert.NotNull(response.Document.Inference.Pages[0].Extras.Cropper); + Assert.Single(response.Document.Inference.Pages[0].Extras.Cropper.Cropping); } [Fact(Timeout = 180000)] @@ -312,6 +312,8 @@ await Assert.ThrowsAsync(() => _client.ParseQueuedAsync(endpoint, jobId)); } + [SuppressMessage("Minor Code Smell", "SCS0005: Weak random number generator.", + Justification = "Making this use proper RNG is really not worth the candle here...")] private static string RandomString(int length) { var random = new Random(); diff --git a/tests/Mindee.IntegrationTests/V2/ClientTest.cs b/tests/Mindee.IntegrationTests/V2/ClientTest.cs index ad2f8f4aa..e1dd3324f 100644 --- a/tests/Mindee.IntegrationTests/V2/ClientTest.cs +++ b/tests/Mindee.IntegrationTests/V2/ClientTest.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Mindee.Input; using Mindee.V2; using Mindee.V2.ClientOptions; @@ -44,6 +45,8 @@ private static void AssertActiveOptions( [InlineData(true, false, true)] [InlineData(false, true, true)] [InlineData(true, true, true)] + [SuppressMessage("Minor Code Smell", "S125: Remove this commented out code.", + Justification = "Keeping this since it might change in the future.")] public async Task Parse_File_Empty_MultiplePages_ParameterVariations_MustSucceed( bool rawText, bool polygon, bool confidence) { @@ -174,7 +177,7 @@ public async Task FailedWebhook_Retrieve_Job_MustSucceed() Assert.Equal(_findocModelId, job.ModelId); Assert.NotNull(job.Webhooks); - var webhook = job.Webhooks.First(); + var webhook = job.Webhooks[0]; Assert.NotNull(webhook); Assert.Equal(webhookId, webhook.Id); Assert.Equal("Processing", webhook.Status); @@ -184,7 +187,7 @@ public async Task FailedWebhook_Retrieve_Job_MustSucceed() await Task.Delay(1000); var loopJobResponse = await _client.GetJobFromUrlAsync(jobUrl); - var loopWebhook = loopJobResponse.Job.Webhooks.First(); + var loopWebhook = loopJobResponse.Job.Webhooks[0]; Assert.NotNull(loopWebhook); Assert.Equal(webhookId, loopWebhook.Id); @@ -226,6 +229,9 @@ public async Task NotFound_Job_MustThrowError() } [Fact(Timeout = 180000)] + [SuppressMessage("Minor Code Smell", "S4144: Update this method so that its implementation " + + "is not identical to 'NotFound_Job_MustThrowError'.", + Justification = "More of a sanity test.")] public async Task NotFound_Inference_MustThrowError() { var ex = await Assert.ThrowsAsync(() => diff --git a/tests/Mindee.IntegrationTests/V2/FileOperations/CropTest.cs b/tests/Mindee.IntegrationTests/V2/FileOperations/CropTest.cs index 83f72d848..c3f0c723a 100644 --- a/tests/Mindee.IntegrationTests/V2/FileOperations/CropTest.cs +++ b/tests/Mindee.IntegrationTests/V2/FileOperations/CropTest.cs @@ -10,7 +10,7 @@ namespace Mindee.IntegrationTests.V2.FileOperations { [Trait("Category", "V2")] [Trait("Category", "FileOperations")] - public class CropTest : IDisposable + public sealed class CropTest : IDisposable { private readonly string? _cropModelId; private readonly string? _findocModelId; @@ -40,7 +40,7 @@ public void Dispose() if (File.Exists(file2)) File.Delete(file2); } - private void CheckFindocReturn(ExtractionResponse findocResponse) + private static void CheckFindocReturn(ExtractionResponse findocResponse) { Assert.True(findocResponse.Inference.Model.Id.Length > 0); diff --git a/tests/Mindee.IntegrationTests/V2/FileOperations/SplitTest.cs b/tests/Mindee.IntegrationTests/V2/FileOperations/SplitTest.cs index 1801d90d1..615b2623b 100644 --- a/tests/Mindee.IntegrationTests/V2/FileOperations/SplitTest.cs +++ b/tests/Mindee.IntegrationTests/V2/FileOperations/SplitTest.cs @@ -10,7 +10,7 @@ namespace Mindee.IntegrationTests.V2.FileOperations { [Trait("Category", "V2")] [Trait("Category", "FileOperations")] - public class SplitTest : IDisposable + public sealed class SplitTest : IDisposable { private readonly string? _splitModelId; private readonly string? _findocModelId; @@ -40,7 +40,7 @@ public void Dispose() if (File.Exists(file2)) File.Delete(file2); } - private void CheckFindocReturn(ExtractionResponse findocResponse) + private static void CheckFindocReturn(ExtractionResponse findocResponse) { Assert.True(findocResponse.Inference.Model.Id.Length > 0); diff --git a/tests/Mindee.UnitTests/Extraction/PdfExtractorTest.cs b/tests/Mindee.UnitTests/Extraction/PdfExtractorTest.cs index a62072732..0891ac973 100644 --- a/tests/Mindee.UnitTests/Extraction/PdfExtractorTest.cs +++ b/tests/Mindee.UnitTests/Extraction/PdfExtractorTest.cs @@ -11,7 +11,7 @@ public class PdfExtractorTest [Fact] public void GivenAnImage_ShouldExtractAPDF() { - var jpg = Constants.V1ProductDir + "invoices/default_sample.jpg"; + const string jpg = Constants.V1ProductDir + "invoices/default_sample.jpg"; var localInput = new LocalInputSource(jpg); Assert.False(localInput.IsPdf()); var extractor = new PdfExtractor(localInput); @@ -57,7 +57,7 @@ public async Task GivenAPDF_ShouldExtractInvoicesStrict() Assert.Equal(4, extractedPDFStrict[1].GetPageCount()); } - private async Task> GetPrediction() + private static async Task> GetPrediction() { const string fileName = Constants.V1ProductDir + "invoice_splitter/response_v1/complete.json"; var mindeeAPi = UnitTestBase.GetMindeeApi(fileName); diff --git a/tests/Mindee.UnitTests/Geometry/PolygonJsonConverterTest.cs b/tests/Mindee.UnitTests/Geometry/PolygonJsonConverterTest.cs index bcdf9d0c9..af9d141f3 100644 --- a/tests/Mindee.UnitTests/Geometry/PolygonJsonConverterTest.cs +++ b/tests/Mindee.UnitTests/Geometry/PolygonJsonConverterTest.cs @@ -5,32 +5,25 @@ namespace Mindee.UnitTests.Geometry { [Trait("Category", "Geometry - JSON converter")] - public class PolygonJsonConverterTest + public sealed class PolygonJsonConverterTest { [Fact] public async Task Deserialize() { - using (var file = new FileInfo("Resources/geometry/polygon.json").OpenRead()) - { - var fake = await JsonSerializer.DeserializeAsync(file); + using var file = new FileInfo("Resources/geometry/polygon.json").OpenRead(); + var fake = await JsonSerializer.DeserializeAsync(file); - Assert.NotNull(fake?.Polygon); - Assert.Equal(4, fake.Polygon.Count()); - Assert.Equal(0.238, fake.Polygon.First().X); - Assert.Equal(0.161, fake.Polygon.Last().Y); - } + Assert.NotNull(fake?.Polygon); + Assert.Equal(4, fake.Polygon.Count); + Assert.Equal(0.238, fake.Polygon[0].X); + Assert.Equal(0.161, fake.Polygon[fake.Polygon.Count - 1].Y); } - public class Fake + public class Fake(Polygon polygon) { - public Fake(Polygon polygon) - { - Polygon = polygon; - } - [JsonPropertyName("polygon")] [JsonConverter(typeof(PolygonJsonConverter))] - public Polygon Polygon { get; } + public Polygon Polygon { get; } = polygon; } } } diff --git a/tests/Mindee.UnitTests/Input/LocalInputSourceTest.cs b/tests/Mindee.UnitTests/Input/LocalInputSourceTest.cs index 699b8be37..1ee62e9b9 100644 --- a/tests/Mindee.UnitTests/Input/LocalInputSourceTest.cs +++ b/tests/Mindee.UnitTests/Input/LocalInputSourceTest.cs @@ -291,10 +291,9 @@ public void Pdf_Compress_With_Text_Does_Not_Compress() public void ApplyPageOperation_KeepFirstPage_Should_Work() { var inputSource = new LocalInputSource(Constants.RootDir + "file_types/pdf/multipage.pdf"); - var pageOptions = new PageOptions( - operation: PageOptionsOperation.KeepOnly - , pageIndexes: new short[] { 0 }); - inputSource.ApplyPageOptions(pageOptions); + var pageOptions = new PageOptions(pageIndexes: [0], operation: PageOptionsOperation.KeepOnly); + var exception = Record.Exception(() => inputSource.ApplyPageOptions(pageOptions)); + Assert.Null(exception); } [Fact] @@ -306,10 +305,11 @@ public void ApplyPageOperation_Keep5FirstPages_Should_Work() var pageOptions = new PageOptions( operation: PageOptionsOperation.Remove, onMinPages: 10, - pageIndexes: new short[] { 0, 1, 2, 3, 4 } + pageIndexes: [0, 1, 2, 3, 4] ); - initialWithText.ApplyPageOptions(pageOptions); + var exception = Record.Exception(() => initialWithText.ApplyPageOptions(pageOptions)); + Assert.Null(exception); } [Fact] @@ -322,9 +322,11 @@ public void ApplyPageOperation_Keep3VariousPages_Should_Work() var pageOptions = new PageOptions( operation: PageOptionsOperation.KeepOnly, onMinPages: 2, - pageIndexes: new short[] { 0, -2, -1 } + pageIndexes: [0, -2, -1] ); - initialWithText.ApplyPageOptions(pageOptions); + + var exception = Record.Exception(() => initialWithText.ApplyPageOptions(pageOptions)); + Assert.Null(exception); } } } diff --git a/tests/Mindee.UnitTests/V1/Parsing/Common/CropperTest.cs b/tests/Mindee.UnitTests/V1/Parsing/Common/CropperTest.cs index 3aea4dff6..96c40919e 100644 --- a/tests/Mindee.UnitTests/V1/Parsing/Common/CropperTest.cs +++ b/tests/Mindee.UnitTests/V1/Parsing/Common/CropperTest.cs @@ -15,10 +15,10 @@ public async Task Should_GetCropperResult() Assert.NotNull(response); Assert.NotEmpty(response.Document.Inference.Pages); - var page = response.Document.Inference.Pages.First(); + var page = response.Document.Inference.Pages[0]; Assert.NotNull(page.Extras); var cropping = page.Extras.Cropper.Cropping; - Assert.Equal("Polygon with 24 points.", cropping.First().ToString()); + Assert.Equal("Polygon with 24 points.", cropping[0].ToString()); } } } diff --git a/tests/Mindee.UnitTests/V1/Parsing/Common/FullTextOcrTest.cs b/tests/Mindee.UnitTests/V1/Parsing/Common/FullTextOcrTest.cs index 20c7d358f..3f1066dcd 100644 --- a/tests/Mindee.UnitTests/V1/Parsing/Common/FullTextOcrTest.cs +++ b/tests/Mindee.UnitTests/V1/Parsing/Common/FullTextOcrTest.cs @@ -9,7 +9,7 @@ public class FullTextOcrTest { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true }; - private Inference LoadInference() + private static Inference LoadInference() { var json = File.ReadAllText(Constants.V1RootDir + "extras/full_text_ocr/complete.json"); var prediction = JsonSerializer.Deserialize>(json, JsonOptions); @@ -21,16 +21,11 @@ private Inference LoadInfe return prediction.Document.Inference; } - private List> LoadPages() + private static List> LoadPages() { var json = File.ReadAllText(Constants.V1RootDir + "extras/full_text_ocr/complete.json"); var prediction = JsonSerializer.Deserialize>(json, JsonOptions); - if (prediction == null) - { - throw new Exception(); - } - - return prediction.Document.Inference.Pages; + return prediction == null ? throw new Exception() : prediction.Document.Inference.Pages; } [Fact] diff --git a/tests/Mindee.UnitTests/V1/Parsing/Common/OcrTest.cs b/tests/Mindee.UnitTests/V1/Parsing/Common/OcrTest.cs index 997ab62f2..62b726944 100644 --- a/tests/Mindee.UnitTests/V1/Parsing/Common/OcrTest.cs +++ b/tests/Mindee.UnitTests/V1/Parsing/Common/OcrTest.cs @@ -7,7 +7,7 @@ namespace Mindee.UnitTests.V1.Parsing.Common [Trait("Category", "OCR")] public class OcrTest { - private async Task LoadOcr() + private static async Task LoadOcr() { var response = await JsonSerializer.DeserializeAsync>( new FileInfo(Constants.V1RootDir + "extras/ocr/complete.json").OpenRead()); diff --git a/tests/Mindee.UnitTests/V1/Product/Cropper/CropperV1Test.cs b/tests/Mindee.UnitTests/V1/Product/Cropper/CropperV1Test.cs index dab462a75..3b5bcfd5f 100644 --- a/tests/Mindee.UnitTests/V1/Product/Cropper/CropperV1Test.cs +++ b/tests/Mindee.UnitTests/V1/Product/Cropper/CropperV1Test.cs @@ -10,7 +10,7 @@ public class CropperV1Test public async Task Predict_CheckEmpty() { var response = await GetPrediction("empty"); - var pagePrediction = response.Document.Inference.Pages.First().Prediction; + var pagePrediction = response.Document.Inference.Pages[0].Prediction; Assert.Empty(pagePrediction.Cropping); } diff --git a/tests/Mindee.UnitTests/V1/Product/FinancialDocument/FinancialDocumentV1Test.cs b/tests/Mindee.UnitTests/V1/Product/FinancialDocument/FinancialDocumentV1Test.cs index bbb405700..08a582bb1 100644 --- a/tests/Mindee.UnitTests/V1/Product/FinancialDocument/FinancialDocumentV1Test.cs +++ b/tests/Mindee.UnitTests/V1/Product/FinancialDocument/FinancialDocumentV1Test.cs @@ -57,7 +57,7 @@ public async Task Predict_Invoice_FirstPage_CheckSummary() File.ReadAllText(Constants.V1ProductDir + "financial_document/response_v1/summary_page0_invoice.rst"); Assert.Equal( expected, - response.Document.Inference.Pages.First().ToString()); + response.Document.Inference.Pages[0].ToString()); } [Fact] @@ -79,7 +79,7 @@ public async Task Predict_Receipt_FirstPage_CheckSummary() File.ReadAllText(Constants.V1ProductDir + "financial_document/response_v1/summary_page0_receipt.rst"); Assert.Equal( expected, - response.Document.Inference.Pages.First().ToString()); + response.Document.Inference.Pages[0].ToString()); } private static async Task> GetPrediction(string name) diff --git a/tests/Mindee.UnitTests/V1/Product/Fr/IdCard/IdCardV1Test.cs b/tests/Mindee.UnitTests/V1/Product/Fr/IdCard/IdCardV1Test.cs index fd56e6bad..919d6672e 100644 --- a/tests/Mindee.UnitTests/V1/Product/Fr/IdCard/IdCardV1Test.cs +++ b/tests/Mindee.UnitTests/V1/Product/Fr/IdCard/IdCardV1Test.cs @@ -22,7 +22,7 @@ public async Task Predict_CheckEmpty() Assert.Null(docPrediction.Gender.Value); Assert.Null(docPrediction.Mrz1.Value); Assert.Null(docPrediction.Mrz2.Value); - var pagePrediction = response.Document.Inference.Pages.First().Prediction; + var pagePrediction = response.Document.Inference.Pages[0].Prediction; Assert.IsType(pagePrediction.DocumentSide); } diff --git a/tests/Mindee.UnitTests/V1/Product/Fr/IdCard/IdCardV2Test.cs b/tests/Mindee.UnitTests/V1/Product/Fr/IdCard/IdCardV2Test.cs index 9f5440763..a1892c0a8 100644 --- a/tests/Mindee.UnitTests/V1/Product/Fr/IdCard/IdCardV2Test.cs +++ b/tests/Mindee.UnitTests/V1/Product/Fr/IdCard/IdCardV2Test.cs @@ -27,7 +27,7 @@ public async Task Predict_CheckEmpty() Assert.Null(docPrediction.Mrz3.Value); Assert.Null(docPrediction.IssueDate.Value); Assert.Null(docPrediction.Authority.Value); - var pagePrediction = response.Document.Inference.Pages.First().Prediction; + var pagePrediction = response.Document.Inference.Pages[0].Prediction; Assert.IsType(pagePrediction.DocumentType); Assert.IsType(pagePrediction.DocumentSide); } diff --git a/tests/Mindee.UnitTests/V1/Product/Generated/GeneratedV1Test.cs b/tests/Mindee.UnitTests/V1/Product/Generated/GeneratedV1Test.cs index e2834cf39..51b8ab3a2 100644 --- a/tests/Mindee.UnitTests/V1/Product/Generated/GeneratedV1Test.cs +++ b/tests/Mindee.UnitTests/V1/Product/Generated/GeneratedV1Test.cs @@ -31,7 +31,7 @@ public async Task AsyncPredict_WhenEmpty_MustHaveValidProperties() } else { - Assert.Null(field.Value.First()["value"].GetString()); + Assert.Null(field.Value[0]["value"].GetString()); } } } @@ -46,22 +46,22 @@ public async Task AsyncPredict_WhenComplete_MustHaveValidProperties() // Direct access to the dictionary Assert.False(features["address"].IsList); - Assert.Equal("AVDA DE MADRID S-N MADRID MADRID", features["address"].First()["value"].GetString()); + Assert.Equal("AVDA DE MADRID S-N MADRID MADRID", features["address"][0]["value"].GetString()); Assert.False(features["birth_date"].IsList); - Assert.Equal("1980-01-01", features["birth_date"].First()["value"].GetString()); + Assert.Equal("1980-01-01", features["birth_date"][0]["value"].GetString()); Assert.False(features["birth_place"].IsList); - Assert.Equal("MADRID", features["birth_place"].First()["value"].GetString()); + Assert.Equal("MADRID", features["birth_place"][0]["value"].GetString()); Assert.False(features["country_of_issue"].IsList); - Assert.Equal("ESP", features["country_of_issue"].First()["value"].GetString()); + Assert.Equal("ESP", features["country_of_issue"][0]["value"].GetString()); Assert.False(features["document_number"].IsList); - Assert.Equal("99999999R", features["document_number"].First()["value"].GetString()); + Assert.Equal("99999999R", features["document_number"][0]["value"].GetString()); Assert.True(features["given_names"].IsList); - Assert.Equal("CARMEN", features["given_names"].First()["value"].GetString()); + Assert.Equal("CARMEN", features["given_names"][0]["value"].GetString()); Assert.True(features["surnames"].IsList); Assert.Equal("ESPAÑOLA", features["surnames"][0]["value"].GetString()); @@ -100,15 +100,15 @@ public async Task SyncPredict_WhenComplete_MustHaveValidProperties() // Direct access to the dictionary var customerName = features["customer_name"]; Assert.False(customerName.IsList); - Assert.Equal("JIRO DOI", customerName.First()["value"].ToString()); - Assert.Equal("JIRO DOI", customerName.First().TryGetString("value")); - Assert.Equal("Jiro Doi", customerName.First()["raw_value"].ToString()); - Assert.Equal("Jiro Doi", customerName.First()["raw_value"].GetString()); - Assert.Equal(0.87, customerName.First()["confidence"].GetDouble()); - Assert.Equal(1, customerName.First()["page_id"].GetInt16()); + Assert.Equal("JIRO DOI", customerName[0]["value"].ToString()); + Assert.Equal("JIRO DOI", customerName[0].TryGetString("value")); + Assert.Equal("Jiro Doi", customerName[0]["raw_value"].ToString()); + Assert.Equal("Jiro Doi", customerName[0]["raw_value"].GetString()); + Assert.Equal(0.87, customerName[0]["confidence"].GetDouble()); + Assert.Equal(1, customerName[0]["page_id"].GetInt16()); Assert.Equal( "((0.037,0.284), (0.099,0.284), (0.099,0.297), (0.037,0.297))", - customerName.First().TryGetPolygon("polygon").ToString()); + customerName[0].TryGetPolygon("polygon").ToString()); // Access as a StringField with raw_value var customerNameField = customerName.AsStringField(); @@ -146,7 +146,7 @@ public async Task SyncPredict_WhenComplete_MustHaveValidProperties() Assert.NotNull(lineItem["description"].GetString()); } - var firstLineItem = lineItems.First(); + var firstLineItem = lineItems[0]; Assert.Equal(0.84, firstLineItem["confidence"].GetDouble()); Assert.Equal("S)BOIE 5X500 FEUILLES A4", firstLineItem["description"].GetString()); Assert.Equal(0, firstLineItem["page_id"].GetInt16()); @@ -160,7 +160,7 @@ public async Task ReceiptsItemsClassifierPredict_WhenComplete_MustHaveValidIntFi { var response = await GetReceiptsItemsClassifierPrediction(); var features = response.Document.Inference.Prediction.Fields; - Assert.Equal(1.0, features["line_items"].First()["quantity"].GetDouble()); + Assert.Equal(1.0, features["line_items"][0]["quantity"].GetDouble()); } [Fact] diff --git a/tests/Mindee.UnitTests/V1/Product/Receipt/ReceiptV4Test.cs b/tests/Mindee.UnitTests/V1/Product/Receipt/ReceiptV4Test.cs index fecad64c4..722c4198e 100644 --- a/tests/Mindee.UnitTests/V1/Product/Receipt/ReceiptV4Test.cs +++ b/tests/Mindee.UnitTests/V1/Product/Receipt/ReceiptV4Test.cs @@ -23,7 +23,7 @@ public async Task Predict_CheckSummary_WithMultiplePages() var expected = File.ReadAllText(Constants.V1ProductDir + "expense_receipts/response_v4/summary_page0.rst"); Assert.Equal( expected, - response.Document.Inference.Pages.First().ToString()); + response.Document.Inference.Pages[0].ToString()); } [Fact] @@ -31,8 +31,8 @@ public async Task Predict_MustSuccessForCategory() { var response = await GetPrediction(); - Assert.Equal(0.94, response.Document.Inference.Pages.First().Prediction.Category.Confidence); - Assert.Equal("food", response.Document.Inference.Pages.First().Prediction.Category.Value); + Assert.Equal(0.94, response.Document.Inference.Pages[0].Prediction.Category.Confidence); + Assert.Equal("food", response.Document.Inference.Pages[0].Prediction.Category.Value); } [Fact] @@ -40,9 +40,9 @@ public async Task Predict_MustSuccessForDate() { var response = await GetPrediction(); - Assert.Equal(0.99, response.Document.Inference.Pages.First().Prediction.Date.Confidence); - Assert.Equal(0, response.Document.Inference.Pages.First().Id); - Assert.Equal("2014-07-07", response.Document.Inference.Pages.First().Prediction.Date.Value); + Assert.Equal(0.99, response.Document.Inference.Pages[0].Prediction.Date.Confidence); + Assert.Equal(0, response.Document.Inference.Pages[0].Id); + Assert.Equal("2014-07-07", response.Document.Inference.Pages[0].Prediction.Date.Value); } [Fact] @@ -50,21 +50,21 @@ public async Task Predict_MustSuccessForTime() { var response = await GetPrediction(); - Assert.Equal(0.99, response.Document.Inference.Pages.First().Prediction.Time.Confidence); - Assert.Equal(0, response.Document.Inference.Pages.First().Id); - Assert.Equal("20:20", response.Document.Inference.Pages.First().Prediction.Time.Value); + Assert.Equal(0.99, response.Document.Inference.Pages[0].Prediction.Time.Confidence); + Assert.Equal(0, response.Document.Inference.Pages[0].Id); + Assert.Equal("20:20", response.Document.Inference.Pages[0].Prediction.Time.Value); Assert.Equal(new List> { new() { 0.635, 0.142 }, new() { 0.778, 0.142 }, new() { 0.778, 0.168 }, new() { 0.635, 0.168 } } - , response.Document.Inference.Pages.First().Prediction.Time.Polygon); + , response.Document.Inference.Pages[0].Prediction.Time.Polygon); } [Fact] public async Task Predict_WithReceiptData_MustSuccessForOrientation() { var response = await GetPrediction(); - Assert.Equal(0, response.Document.Inference.Pages.First().Orientation.Value); + Assert.Equal(0, response.Document.Inference.Pages[0].Orientation.Value); } [Fact] @@ -74,25 +74,25 @@ public async Task Predict_WithCropping_MustSuccess() var mindeeAPi = UnitTestBase.GetMindeeApi(fileName); var response = await mindeeAPi.PredictPostAsync(UnitTestBase.GetFakePredictParameter()); - Assert.NotNull(response.Document.Inference.Pages.First().Extras.Cropper); - Assert.Single(response.Document.Inference.Pages.First().Extras.Cropper.Cropping); + Assert.NotNull(response.Document.Inference.Pages[0].Extras.Cropper); + Assert.Single(response.Document.Inference.Pages[0].Extras.Cropper.Cropping); Assert.Equal(new List> { new() { 0.057, 0.008 }, new() { 0.846, 0.008 }, new() { 0.846, 1.0 }, new() { 0.057, 1.0 } } - , response.Document.Inference.Pages.First().Extras.Cropper.Cropping.First().BoundingBox); + , response.Document.Inference.Pages[0].Extras.Cropper.Cropping[0].BoundingBox); Assert.Equal(new List> { new() { 0.161, 0.016 }, new() { 0.744, 0.009 }, new() { 0.845, 0.996 }, new() { 0.058, 0.999 } } - , response.Document.Inference.Pages.First().Extras.Cropper.Cropping.First().Quadrangle); + , response.Document.Inference.Pages[0].Extras.Cropper.Cropping[0].Quadrangle); Assert.Equal(new List> { new() { 0.052, 0.011 }, new() { 0.839, 0.007 }, new() { 0.844, 0.994 }, new() { 0.057, 0.998 } } - , response.Document.Inference.Pages.First().Extras.Cropper.Cropping.First().Rectangle); + , response.Document.Inference.Pages[0].Extras.Cropper.Cropping[0].Rectangle); Assert.Equal(new List> { @@ -121,7 +121,7 @@ public async Task Predict_WithCropping_MustSuccess() new() { 0.086, 0.732 }, new() { 0.113, 0.514 } } - , response.Document.Inference.Pages.First().Extras.Cropper.Cropping.First().Polygon); + , response.Document.Inference.Pages[0].Extras.Cropper.Cropping[0].Polygon); } private static async Task> GetPrediction() diff --git a/tests/Mindee.UnitTests/V1/Product/Us/BankCheck/BankCheckV1Test.cs b/tests/Mindee.UnitTests/V1/Product/Us/BankCheck/BankCheckV1Test.cs index e337fc9a6..84747db0d 100644 --- a/tests/Mindee.UnitTests/V1/Product/Us/BankCheck/BankCheckV1Test.cs +++ b/tests/Mindee.UnitTests/V1/Product/Us/BankCheck/BankCheckV1Test.cs @@ -17,7 +17,7 @@ public async Task Predict_CheckEmpty() Assert.Null(docPrediction.RoutingNumber.Value); Assert.Null(docPrediction.AccountNumber.Value); Assert.Null(docPrediction.CheckNumber.Value); - var pagePrediction = response.Document.Inference.Pages.First().Prediction; + var pagePrediction = response.Document.Inference.Pages[0].Prediction; Assert.Null(pagePrediction.CheckPosition.Polygon); Assert.Null(pagePrediction.CheckPosition.BoundingBox); Assert.Empty(pagePrediction.SignaturesPositions); diff --git a/tests/Mindee.UnitTests/V1/Product/Us/PayrollCheckRegister/PayrollCheckRegisterV1Test.cs b/tests/Mindee.UnitTests/V1/Product/Us/PayrollCheckRegister/PayrollCheckRegisterV1Test.cs index 52d2414bc..d21cea377 100644 --- a/tests/Mindee.UnitTests/V1/Product/Us/PayrollCheckRegister/PayrollCheckRegisterV1Test.cs +++ b/tests/Mindee.UnitTests/V1/Product/Us/PayrollCheckRegister/PayrollCheckRegisterV1Test.cs @@ -25,9 +25,6 @@ public async Task Predict_CheckDocument() var docPrediction = response.Document.Inference.Prediction; Assert.Equal(13, docPrediction.Payments.Count); Assert.Equal("Economists For Hire, LLC", docPrediction.CompanyName.Value); - - // broken output, need to add recursive table support - // Console.Out.Write(response.Document.ToString()); } [Fact] diff --git a/tests/Mindee.UnitTests/V2/ClientTest.cs b/tests/Mindee.UnitTests/V2/ClientTest.cs index b909d1dd0..5a17a7979 100644 --- a/tests/Mindee.UnitTests/V2/ClientTest.cs +++ b/tests/Mindee.UnitTests/V2/ClientTest.cs @@ -14,7 +14,7 @@ namespace Mindee.UnitTests.V2 [Trait("Category", "Mindee client")] public class ClientTest { - private Client MakeCustomMindeeClientV2(Mock predictable) + private static Client MakeCustomMindeeClientV2(Mock predictable) { predictable.Setup(x => x.ReqPostEnqueueAsync( It.IsAny(), It.IsAny(), It.IsAny()) diff --git a/tests/Mindee.UnitTests/V2/Parsing/JobTest.cs b/tests/Mindee.UnitTests/V2/Parsing/JobTest.cs index cfa26b6af..c3b2728d1 100644 --- a/tests/Mindee.UnitTests/V2/Parsing/JobTest.cs +++ b/tests/Mindee.UnitTests/V2/Parsing/JobTest.cs @@ -37,7 +37,7 @@ public void OkProcessed_WebhooksOk_MustHaveValidProperties() Assert.Null(response.Job.Error); Assert.Equal("Processed", response.Job.Status); Assert.NotEmpty(response.Job.Webhooks); - var webhook = response.Job.Webhooks.First(); + var webhook = response.Job.Webhooks[0]; Assert.NotNull(webhook.Id); Assert.Equal(2026, webhook.CreatedAt.Year); Assert.Equal("Processed", webhook.Status); @@ -58,7 +58,7 @@ public void Error_422_MustHaveValidProperties() Assert.Equal(422, error.Status); Assert.StartsWith("422-", error.Code); Assert.Single(error.Errors); - Assert.Contains("must be a valid", error.Errors.First().Detail); + Assert.Contains("must be a valid", error.Errors[0].Detail); Assert.Equal("Failed", response.Job.Status); } diff --git a/tests/Mindee.UnitTests/V2/Product/ClassificationTest.cs b/tests/Mindee.UnitTests/V2/Product/ClassificationTest.cs index c31bcbffc..8522a9a5e 100644 --- a/tests/Mindee.UnitTests/V2/Product/ClassificationTest.cs +++ b/tests/Mindee.UnitTests/V2/Product/ClassificationTest.cs @@ -17,7 +17,7 @@ public void Parameters_MustInit() var productParams = new ClassificationParameters("invalid-model-id"); Assert.Equal("invalid-model-id", productParams.ModelId); - var productAttributes = productParams.GetType().GetCustomAttribute(); + var productAttributes = productParams.GetType().GetCustomAttribute(); Assert.Equal("classification", productAttributes?.Slug); } @@ -75,7 +75,7 @@ private static ClassificationResponse GetInference(string path) return localResponse.DeserializeResponse(); } - private void AssertInferenceResponse(ClassificationResponse response) + private static void AssertInferenceResponse(ClassificationResponse response) { Assert.NotNull(response.Inference); Assert.NotNull(response.Inference.Id); diff --git a/tests/Mindee.UnitTests/V2/Product/CropTest.cs b/tests/Mindee.UnitTests/V2/Product/CropTest.cs index 5b13d30f9..e0bfa4521 100644 --- a/tests/Mindee.UnitTests/V2/Product/CropTest.cs +++ b/tests/Mindee.UnitTests/V2/Product/CropTest.cs @@ -17,7 +17,7 @@ public void Parameters_MustInit() var productParams = new CropParameters("invalid-model-id"); Assert.Equal("invalid-model-id", productParams.ModelId); - var productAttributes = productParams.GetType().GetCustomAttribute(); + var productAttributes = productParams.GetType().GetCustomAttribute(); Assert.Equal("crop", productAttributes?.Slug); } @@ -41,7 +41,7 @@ public void Crop_WhenSingle_MustHaveValidProperties() Assert.NotNull(crops); Assert.Single(crops); - var firstCrop = crops.First(); + var firstCrop = crops[0]; Assert.Equal("invoice", firstCrop.ObjectType); Assert.Equal(0, firstCrop.Location.Page); @@ -175,7 +175,7 @@ private static CropResponse GetInference(string path) return localResponse.DeserializeResponse(); } - private void AssertInferenceResponse(CropResponse response) + private static void AssertInferenceResponse(CropResponse response) { Assert.NotNull(response.Inference); Assert.NotNull(response.Inference.Id); diff --git a/tests/Mindee.UnitTests/V2/Product/ExtractionTest.cs b/tests/Mindee.UnitTests/V2/Product/ExtractionTest.cs index 5f4dc85f9..5bd811c03 100644 --- a/tests/Mindee.UnitTests/V2/Product/ExtractionTest.cs +++ b/tests/Mindee.UnitTests/V2/Product/ExtractionTest.cs @@ -18,7 +18,7 @@ public void Parameters_MustInit() var productParams = new ExtractionParameters("invalid-model-id"); Assert.Equal("invalid-model-id", productParams.ModelId); - var productAttributes = productParams.GetType().GetCustomAttribute(); + var productAttributes = productParams.GetType().GetCustomAttribute(); Assert.Equal("extraction", productAttributes?.Slug); } @@ -78,8 +78,8 @@ public void FinancialDocument_WhenComplete_MustHaveValidProperties() Assert.Equal(21, fields.Count); Assert.Single(fields["taxes"].ListField.Items); Assert.NotNull(fields["taxes"].ToString()); - Assert.Equal(3, fields["taxes"].ListField.Items.First().ObjectField.Fields.Count); - Assert.Equal(31.5, fields["taxes"].ListField.Items.First().ObjectField.Fields["base"].SimpleField.Value); + Assert.Equal(3, fields["taxes"].ListField.Items[0].ObjectField.Fields.Count); + Assert.Equal(31.5, fields["taxes"].ListField.Items[0].ObjectField.Fields["base"].SimpleField.Value); Assert.Equal(195.0, fields["total_net"].SimpleField.Value); Assert.Null(fields["tips_gratuity"].SimpleField.Value); @@ -127,9 +127,9 @@ public void DeepNestedFields_mustExposeCorrectTypes() var nestedList = lvl2["sub_object_object_sub_object_list"].ListField!; var items = nestedList.Items; Assert.NotEmpty(items); - Assert.NotNull(items.First().ObjectField); + Assert.NotNull(items[0].ObjectField); - var firstItem = items.First().ObjectField!; + var firstItem = items[0].ObjectField!; var deepSimple = firstItem.Fields["sub_object_object_sub_object_list_simple"].SimpleField!; Assert.Equal("value_9", deepSimple.Value); } @@ -292,8 +292,8 @@ public void StandardFieldTypes_mustHaveLocations() Assert.NotNull(simpleField.Locations); List locations = simpleField.Locations; Assert.Single(locations); - Assert.Equal(0, locations.First().Page); - var polygon = locations.First().Polygon; + Assert.Equal(0, locations[0].Page); + var polygon = locations[0].Polygon; Assert.Equal(new Point(0, 0), polygon[0]); Assert.Equal(new Point(0, 0), polygon[1]); Assert.Equal(new Point(1, 1), polygon[2]); @@ -399,7 +399,7 @@ private static ExtractionResponse GetInference(string path) return localResponse.DeserializeResponse(); } - private void AssertInferenceResponse(ExtractionResponse response) + private static void AssertInferenceResponse(ExtractionResponse response) { Assert.NotNull(response.Inference); Assert.NotNull(response.Inference.Id); diff --git a/tests/Mindee.UnitTests/V2/Product/OcrTest.cs b/tests/Mindee.UnitTests/V2/Product/OcrTest.cs index 36bbef403..b15fcab82 100644 --- a/tests/Mindee.UnitTests/V2/Product/OcrTest.cs +++ b/tests/Mindee.UnitTests/V2/Product/OcrTest.cs @@ -16,7 +16,7 @@ public void Parameters_MustInit() var productParams = new OcrParameters("invalid-model-id"); Assert.Equal("invalid-model-id", productParams.ModelId); - var productAttributes = productParams.GetType().GetCustomAttribute(); + var productAttributes = productParams.GetType().GetCustomAttribute(); Assert.Equal("ocr", productAttributes?.Slug); } @@ -39,7 +39,7 @@ public void Ocr_WhenSingle_MustHaveValidProperties() Assert.NotNull(pages); Assert.Single(pages); - var firstPage = pages.First(); + var firstPage = pages[0]; Assert.NotNull(firstPage.Words); var firstWord = firstPage.Words[0]; @@ -84,7 +84,7 @@ private static OcrResponse GetInference(string path) return localResponse.DeserializeResponse(); } - private void AssertInferenceResponse(OcrResponse response) + private static void AssertInferenceResponse(OcrResponse response) { Assert.NotNull(response.Inference); Assert.NotNull(response.Inference.Id); diff --git a/tests/Mindee.UnitTests/V2/Product/SplitTest.cs b/tests/Mindee.UnitTests/V2/Product/SplitTest.cs index 608ff5959..dc1389f97 100644 --- a/tests/Mindee.UnitTests/V2/Product/SplitTest.cs +++ b/tests/Mindee.UnitTests/V2/Product/SplitTest.cs @@ -16,7 +16,7 @@ public void Parameters_MustInit() var productParams = new SplitParameters("invalid-model-id"); Assert.Equal("invalid-model-id", productParams.ModelId); - var productAttributes = productParams.GetType().GetCustomAttribute(); + var productAttributes = productParams.GetType().GetCustomAttribute(); Assert.Equal("split", productAttributes?.Slug); } @@ -35,7 +35,7 @@ public void Split_WhenSingle_MustHaveValidProperties() Assert.NotNull(splits); Assert.Single(splits); - var firstSplit = splits.First(); + var firstSplit = splits[0]; Assert.Equal("receipt", firstSplit.DocumentType); Assert.NotNull(firstSplit.PageRange); @@ -125,7 +125,7 @@ private static SplitResponse GetInference(string path) return localResponse.DeserializeResponse(); } - private void AssertInferenceResponse(SplitResponse response) + private static void AssertInferenceResponse(SplitResponse response) { Assert.NotNull(response.Inference); Assert.NotNull(response.Inference.Id); diff --git a/tests/Mindee.UnitTests/V2/Search/ModelSearchTest.cs b/tests/Mindee.UnitTests/V2/Search/ModelSearchTest.cs index f56c738e1..58c4f9b5b 100644 --- a/tests/Mindee.UnitTests/V2/Search/ModelSearchTest.cs +++ b/tests/Mindee.UnitTests/V2/Search/ModelSearchTest.cs @@ -22,7 +22,7 @@ public void ModelSearchResponse_LoadsLocally() Assert.Equal(50, response.Pagination.PerPage); Assert.Equal(1, response.Pagination.TotalPages); - var firstItem = response.Models.First(); + var firstItem = response.Models[0]; Assert.Equal("Extraction With Webhooks", firstItem.Name); Assert.Equal("afde5151-aa11-aa11-9289-fa04e50ca3b9", firstItem.Id); Assert.Equal("extraction", firstItem.ModelType); @@ -32,7 +32,7 @@ public void ModelSearchResponse_LoadsLocally() Assert.Equal("FAILURE", firstItem.Webhooks[0].Name); Assert.Equal("https://failure.mindee.com", firstItem.Webhooks[0].Url); - var lastItem = response.Models.Last(); + var lastItem = response.Models[response.Models.Count - 1]; Assert.Equal("Extraction Without Webhooks Key", lastItem.Name); Assert.Equal("e14e0923-ee55-ee55-a335-8d2110917d7b", lastItem.Id); } diff --git a/tests/Mindee.UnitTests/V2/Search/RagDocumentsSearchTest.cs b/tests/Mindee.UnitTests/V2/Search/RagDocumentsSearchTest.cs index 8f42021bd..7d1d173ee 100644 --- a/tests/Mindee.UnitTests/V2/Search/RagDocumentsSearchTest.cs +++ b/tests/Mindee.UnitTests/V2/Search/RagDocumentsSearchTest.cs @@ -1,3 +1,4 @@ +using System.Globalization; using Mindee.V2.Parsing; using Mindee.V2.Parsing.Search; @@ -22,50 +23,50 @@ public void RagDocumentSearchResponse_LoadsLocally() Assert.Equal(50, response.Pagination.PerPage); Assert.Equal(1, response.Pagination.TotalPages); - var firstItem = response.RagDocuments.First(); + var firstItem = response.RagDocuments[0]; Assert.Equal("cc831599-c545-48b7-aa27-6d7ccd5b8d32", firstItem.Id); Assert.Equal("12345678-1234-1234-1234-123456789abc", firstItem.ModelId); Assert.Equal("invoice_01.pdf", firstItem.Filename); Assert.Equal( DateTime.Parse( "2026-06-30T13:13:46.168586Z", - null, - System.Globalization.DateTimeStyles.RoundtripKind), + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind), firstItem.CreatedAt); Assert.Equal(0, firstItem.TotalMatches); Assert.Null(firstItem.LastMatchAt); Assert.Equal("Processing", firstItem.Status); - var secondItem = response.RagDocuments.ElementAt(1); + var secondItem = response.RagDocuments[1]; Assert.Equal("27467e4c-5602-4315-90d9-3d2da69b05ab", secondItem.Id); Assert.Equal("12345678-1234-1234-1234-123456789abc", secondItem.ModelId); Assert.Equal("invoice_02.pdf", secondItem.Filename); Assert.Equal( DateTime.Parse( "2026-06-30T13:13:46.168586Z", - null, - System.Globalization.DateTimeStyles.RoundtripKind), + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind), secondItem.CreatedAt); Assert.Equal(0, secondItem.TotalMatches); Assert.Null(secondItem.LastMatchAt); Assert.Equal("Draft", secondItem.Status); - var thirdItem = response.RagDocuments.ElementAt(2); + var thirdItem = response.RagDocuments[2]; Assert.Equal("a6bcae7d-0439-476b-8a63-5a39ec05dc21", thirdItem.Id); Assert.Equal("12345678-1234-1234-1234-jobid1234567", thirdItem.ModelId); Assert.Equal("invoice_03.pdf", thirdItem.Filename); Assert.Equal( DateTime.Parse( "2026-06-17T14:35:46.228006Z", - null, - System.Globalization.DateTimeStyles.RoundtripKind), + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind), thirdItem.CreatedAt); Assert.Equal(5, thirdItem.TotalMatches); Assert.Equal( DateTime.Parse( "2026-06-18T14:35:46.248006Z", - null, - System.Globalization.DateTimeStyles.RoundtripKind), + CultureInfo.InvariantCulture, + DateTimeStyles.RoundtripKind), thirdItem.LastMatchAt); Assert.Equal("Active", thirdItem.Status); }