Skip to content

Commit 7512235

Browse files
committed
✨ improved v2 error messages and exceptions
1 parent 9df30e4 commit 7512235

File tree

8 files changed

+136
-36
lines changed

8 files changed

+136
-36
lines changed

src/Mindee/Parsing/V2/ErrorItem.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace Mindee.Parsing.V2
4+
{
5+
/// <summary>
6+
/// Explicit details on a problem.
7+
/// </summary>
8+
public class ErrorItem
9+
{
10+
/// <summary>
11+
/// A JSON Pointer to the location of the body property.
12+
/// </summary>
13+
[JsonPropertyName("pointer")]
14+
public string Pointer { get; set; }
15+
16+
/// <summary>
17+
/// Explicit information on the issue.
18+
/// </summary>
19+
[JsonPropertyName("detail")]
20+
public string Detail { get; set; }
21+
}
22+
}

src/Mindee/Parsing/V2/ErrorResponse.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,42 @@
1+
using System.Collections.Generic;
12
using System.Text.Json.Serialization;
23

34
namespace Mindee.Parsing.V2
45
{
56
/// <summary>
6-
/// Represent an error information from the API response.
7+
/// Error response detailing a problem. The format adheres to RFC 9457.
78
/// </summary>
89
public class ErrorResponse
910
{
1011
/// <summary>
11-
/// Detail relevant to the error.
12+
/// The HTTP status code returned by the server.
13+
/// </summary>
14+
[JsonPropertyName("status")]
15+
public int Status { get; set; }
16+
17+
/// <summary>
18+
/// A human-readable explanation specific to the occurrence of the problem.
1219
/// </summary>
1320
[JsonPropertyName("detail")]
1421
public string Detail { get; set; }
1522

1623
/// <summary>
17-
/// Http error code.
24+
/// A short, human-readable summary of the problem.
1825
/// </summary>
19-
[JsonPropertyName("status")]
20-
public int Status { get; set; }
26+
[JsonPropertyName("title")]
27+
public string Title { get; set; }
28+
29+
/// <summary>
30+
/// A machine-readable code specific to the occurrence of the problem.
31+
/// </summary>
32+
[JsonPropertyName("code")]
33+
public string Code { get; set; }
34+
35+
/// <summary>
36+
/// A list of explicit details on the problem.
37+
/// </summary>
38+
[JsonPropertyName("errors")]
39+
public List<ErrorItem> Errors { get; set; }
2140

2241
/// <summary>
2342
/// To make the error prettier to display.

src/Mindee/Parsing/V2/Job.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
namespace Mindee.Parsing.V2
66
{
77
/// <summary>
8-
/// Defines an enqueued job.
8+
/// Information on the processing of a file sent to the Mindee API.
99
/// </summary>
1010
public class Job
1111
{
1212
/// <summary>
13-
/// Date and time the job was created at.
13+
/// Date and time of the Job creation.
1414
/// </summary>
1515
[JsonPropertyName("created_at")]
1616
[JsonConverter(typeof(DateTimeJsonConverter))]
1717
public DateTime CreatedAt { get; set; }
1818

1919
/// <summary>
20-
/// Unique identifier of the job.
20+
/// UUID of the Job.
2121
/// </summary>
2222
[JsonPropertyName("id")]
2323
public string Id { get; set; }
@@ -29,43 +29,43 @@ public class Job
2929
public string Status { get; set; }
3030

3131
/// <summary>
32-
/// An error encountered while processing the job.
32+
/// If an error occurred during processing, contains the problem details.
3333
/// </summary>
3434
[JsonPropertyName("error")]
3535
public ErrorResponse Error { get; set; }
3636

3737
/// <summary>
38-
/// ID of the model.
38+
/// UUID of the model to be used for the inference.
3939
/// </summary>
4040
[JsonPropertyName("model_id")]
4141
public string ModelId { get; set; }
4242

4343
/// <summary>
44-
/// Name of the file.
44+
/// Name of the file sent.
4545
/// </summary>
4646
[JsonPropertyName("file_name")]
4747
public string FileName { get; set; }
4848

4949
/// <summary>
50-
/// Optional Alias for the file.
50+
/// Optional alias sent for the file.
5151
/// </summary>
5252
[JsonPropertyName("file_alias")]
5353
public string FileAlias { get; set; }
5454

5555
/// <summary>
56-
/// URL to use for polling.
56+
/// URL to poll for the Job status.
5757
/// </summary>
5858
[JsonPropertyName("polling_url")]
5959
public string PollingUrl { get; set; }
6060

6161
/// <summary>
62-
/// URL to follow for the final result.
62+
/// URL to retrieve the inference results. Will be filled once the inference is ready.
6363
/// </summary>
6464
[JsonPropertyName("result_url")]
6565
public string ResultUrl { get; set; }
6666

6767
/// <summary>
68-
/// Webhooks to call.
68+
/// List of responses from webhooks called. Empty until processing is finished.
6969
/// </summary>
7070
[JsonPropertyName("webhooks")]
7171
public List<JobWebhook> Webhooks { get; set; }

tests/Mindee.UnitTests/V1/MindeeClientOptionsTest.cs renamed to tests/Mindee.UnitTests/V1/PollingOptionsTest.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
namespace Mindee.UnitTests
44
{
5+
[Trait("Category", "V1")]
56
[Trait("Category", "Mindee client options")]
6-
public class MindeeClientOptionsTest
7+
public class PollingOptionsTest
78
{
89
[Fact]
910
public void InvalidPollingOptions_MustFail()

tests/Mindee.UnitTests/V2/MindeeClientTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Mindee.UnitTests.V2
77
{
88
[Trait("Category", "V2")]
99
[Trait("Category", "Mindee client")]
10-
public class MindeeV1ClientTest
10+
public class MindeeClientTest
1111
{
1212
private MindeeClientV2 MakeCustomMindeeClientV2(Mock<HttpApiV2> predictable)
1313
{

tests/Mindee.UnitTests/V2/Parsing/InferenceV2Test.cs renamed to tests/Mindee.UnitTests/V2/Parsing/InferenceTest.cs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Mindee.UnitTests.V2.Parsing
77
{
88
[Trait("Category", "V2")]
99
[Trait("Category", "Inference")]
10-
public class InferenceV2Test
10+
public class InferenceTest
1111
{
1212
[Fact]
1313
public void AsyncPredict_WhenEmpty_MustHaveValidProperties()
@@ -49,7 +49,7 @@ public void AsyncPredict_WhenEmpty_MustHaveValidProperties()
4949
[Fact]
5050
public void AsyncPredict_WhenComplete_MustHaveValidProperties()
5151
{
52-
var response = GetInference("Resources/v2/products/financial_document/complete.json");
52+
InferenceResponse response = GetInference("Resources/v2/products/financial_document/complete.json");
5353
InferenceActiveOptions activeOptions = response.Inference.ActiveOptions;
5454
Assert.NotNull(activeOptions);
5555
Assert.False(activeOptions.Rag);
@@ -83,8 +83,8 @@ public void AsyncPredict_WhenComplete_MustHaveValidProperties()
8383
[Fact(DisplayName = "deep_nested_fields.json – all nested structures must be typed correctly")]
8484
public void DeepNestedFields_mustExposeCorrectTypes()
8585
{
86-
var resp = GetInference("Resources/v2/inference/deep_nested_fields.json");
87-
Inference? inf = resp.Inference;
86+
InferenceResponse response = GetInference("Resources/v2/inference/deep_nested_fields.json");
87+
Inference? inf = response.Inference;
8888
Assert.NotNull(inf);
8989

9090
InferenceFields fields = inf.Result.Fields;
@@ -113,8 +113,8 @@ public void DeepNestedFields_mustExposeCorrectTypes()
113113
[Fact(DisplayName = "standard_field_types.json – file metadata must be recognised")]
114114
public void StandardFieldTypes_mustExposeFileValues()
115115
{
116-
var resp = GetInference("Resources/v2/inference/standard_field_types.json");
117-
Inference? inference = resp.Inference;
116+
InferenceResponse response = GetInference("Resources/v2/inference/standard_field_types.json");
117+
Inference? inference = response.Inference;
118118
Assert.NotNull(inference);
119119
InferenceFile file = inference.File;
120120
Assert.NotNull(file);
@@ -135,8 +135,8 @@ public void StandardFieldTypes_mustExposeFileValues()
135135
[Fact(DisplayName = "standard_field_types.json – simple fields must be recognised")]
136136
public void StandardFieldTypes_mustExposeSimpleFieldValues()
137137
{
138-
var resp = GetInference("Resources/v2/inference/standard_field_types.json");
139-
Inference? inference = resp.Inference;
138+
InferenceResponse response = GetInference("Resources/v2/inference/standard_field_types.json");
139+
Inference? inference = response.Inference;
140140
Assert.NotNull(inference);
141141
InferenceFields fields = inference.Result.Fields;
142142

@@ -175,8 +175,8 @@ public void StandardFieldTypes_mustExposeSimpleFieldValues()
175175
[Fact(DisplayName = "standard_field_types.json – simple list fields must be recognised")]
176176
public void StandardFieldTypes_mustExposeSimpleListFieldValues()
177177
{
178-
var resp = GetInference("Resources/v2/inference/standard_field_types.json");
179-
Inference? inference = resp.Inference;
178+
InferenceResponse response = GetInference("Resources/v2/inference/standard_field_types.json");
179+
Inference? inference = response.Inference;
180180
Assert.NotNull(inference);
181181
InferenceFields fields = inference.Result.Fields;
182182

@@ -196,8 +196,8 @@ public void StandardFieldTypes_mustExposeSimpleListFieldValues()
196196
[Fact(DisplayName = "standard_field_types.json – object fields must be recognised")]
197197
public void StandardFieldTypes_mustExposeObjectFieldValues()
198198
{
199-
var resp = GetInference("Resources/v2/inference/standard_field_types.json");
200-
Inference? inference = resp.Inference;
199+
InferenceResponse response = GetInference("Resources/v2/inference/standard_field_types.json");
200+
Inference? inference = response.Inference;
201201
Assert.NotNull(inference);
202202
InferenceFields fields = inference.Result.Fields;
203203

@@ -220,8 +220,8 @@ public void StandardFieldTypes_mustExposeObjectFieldValues()
220220
[Fact(DisplayName = "standard_field_types.json – simple list fields must be recognised")]
221221
public void StandardFieldTypes_mustExposeObjectListFieldValues()
222222
{
223-
var resp = GetInference("Resources/v2/inference/standard_field_types.json");
224-
Inference? inference = resp.Inference;
223+
InferenceResponse response = GetInference("Resources/v2/inference/standard_field_types.json");
224+
Inference? inference = response.Inference;
225225
Assert.NotNull(inference);
226226
InferenceFields fields = inference.Result.Fields;
227227

@@ -254,7 +254,6 @@ public void StandardFieldTypes_mustExposeObjectListFieldValues()
254254
public void AsyncPredict_WhenComplete_MustHaveRawText()
255255
{
256256
InferenceResponse response = GetInference("Resources/v2/inference/raw_texts.json");
257-
258257
InferenceActiveOptions activeOptions = response.Inference.ActiveOptions;
259258
Assert.NotNull(activeOptions);
260259
Assert.False(activeOptions.Rag);
@@ -279,8 +278,8 @@ public void AsyncPredict_WhenComplete_MustHaveRawText()
279278
[Fact(DisplayName = "standard_field_types.json - locations must be recognised")]
280279
public void StandardFieldTypes_mustHaveLocations()
281280
{
282-
var resp = GetInference("Resources/v2/inference/standard_field_types.json");
283-
Inference? inf = resp.Inference;
281+
InferenceResponse response = GetInference("Resources/v2/inference/standard_field_types.json");
282+
Inference? inf = response.Inference;
284283
InferenceFields fields = inf.Result.Fields;
285284
Assert.NotNull(inf);
286285
SimpleField simpleField = fields["field_simple_string"].SimpleField;
@@ -324,7 +323,7 @@ private static string NormalizeLineEndings(string input) =>
324323

325324
private static InferenceResponse GetInference(string path)
326325
{
327-
var localResponse = new LocalResponse(File.ReadAllText(path));
326+
LocalResponse localResponse = new LocalResponse(File.ReadAllText(path));
328327
return localResponse.DeserializeResponse<InferenceResponse>();
329328
}
330329
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using Mindee.Input;
2+
using Mindee.Parsing.Common;
3+
using Mindee.Parsing.V2;
4+
5+
namespace Mindee.UnitTests.V2.Parsing
6+
{
7+
[Trait("Category", "V2")]
8+
[Trait("Category", "Job")]
9+
public class JobTest
10+
{
11+
[Fact]
12+
public void OkProcessing_MustHaveValidProperties()
13+
{
14+
JobResponse response = GetJob("Resources/v2/job/ok_processing.json");
15+
Assert.NotNull(response.Job);
16+
Assert.Equal(2025, response.Job.CreatedAt.Year);
17+
Assert.StartsWith("https", response.Job.PollingUrl);
18+
Assert.Null(response.Job.ResultUrl);
19+
Assert.Null(response.Job.Error);
20+
}
21+
22+
[Fact]
23+
public void OkProcessed_WebhooksOk_MustHaveValidProperties()
24+
{
25+
JobResponse response = GetJob("Resources/v2/job/ok_processed_webhooks_ok.json");
26+
Assert.NotNull(response.Job);
27+
Assert.Equal(2026, response.Job.CreatedAt.Year);
28+
Assert.StartsWith("https", response.Job.PollingUrl);
29+
Assert.StartsWith("https", response.Job.ResultUrl);
30+
Assert.Null(response.Job.Error);
31+
Assert.NotEmpty(response.Job.Webhooks);
32+
JobWebhook webhook = response.Job.Webhooks.First();
33+
Assert.NotNull(webhook.Id);
34+
Assert.Equal(2026, webhook.CreatedAt.Year);
35+
Assert.Equal("Processed", webhook.Status);
36+
Assert.Null(webhook.Error);
37+
}
38+
39+
[Fact]
40+
public void Error_422_MustHaveValidProperties()
41+
{
42+
JobResponse response = GetJob("Resources/v2/job/fail_422.json");
43+
Assert.NotNull(response.Job);
44+
Assert.Equal(2025, response.Job.CreatedAt.Year);
45+
ErrorResponse error = response.Job.Error;
46+
Assert.NotNull(error);
47+
Assert.Equal(422, error.Status);
48+
Assert.StartsWith("422-", error.Code);
49+
Assert.Single(error.Errors);
50+
Assert.Contains("must be a valid", error.Errors.First().Detail);
51+
}
52+
53+
private static JobResponse GetJob(string path)
54+
{
55+
LocalResponse localResponse = new LocalResponse(File.ReadAllText(path));
56+
return localResponse.DeserializeResponse<JobResponse>();
57+
}
58+
}
59+
}

tests/resources

0 commit comments

Comments
 (0)