Skip to content

Commit b35755e

Browse files
authored
feat: ✨ convert EDIFACT ➡ BO4E (#3)
1 parent 1e4ad8a commit b35755e

12 files changed

Lines changed: 268 additions & 1 deletion

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using EDILibrary;
2+
using FluentAssertions;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using TransformerBeeClient.Model;
5+
using Xunit;
6+
7+
namespace TransformerBeeClient.IntegrationTest;
8+
9+
/// <summary>
10+
/// Tests that a edifact can be converted to bo4e
11+
/// </summary>
12+
public class EdifactToBo4eTests : IClassFixture<ClientFixture>
13+
{
14+
15+
private readonly ClientFixture _client;
16+
17+
public EdifactToBo4eTests(ClientFixture clientFixture)
18+
{
19+
_client = clientFixture;
20+
}
21+
22+
[Fact]
23+
public async Task Edifact_Can_Be_Converted_To_Bo4e()
24+
{
25+
var httpClientFactory = _client.HttpClientFactory;
26+
ICanConvertToBo4e client = new TransformerBeeRestClient(httpClientFactory);
27+
var edifactString = await File.ReadAllTextAsync("TestEdifacts/FV2310/55001.edi");
28+
var result = await client.ConvertToBo4e(edifactString, EdifactFormatVersion.FV2310);
29+
result.Should().BeOfType<List<Marktnachricht>>();
30+
result.Single().Transaktionen.Should().NotBeNullOrEmpty();
31+
}
32+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
UNA:+,? '
2+
UNB+UNOC:3+9912345789012:500+9909876543210:500+230703:1059+ASDFHGJ'
3+
UNH+11223344556678+UTILMD:D:11A:UN:S1.1'
4+
BGM+E01+918273746512345678901'
5+
DTM+137:202306300558?+00:303'
6+
NAD+MS+9912345789012::293'
7+
NAD+MR+9909876543210::293'
8+
IDE+24+918273746512345678901'
9+
IMD++Z36+Z13'
10+
DTM+92:202212312300?+00:303'
11+
STS+7++E01'
12+
LOC+Z16+78889918283'
13+
LOC+Z17+DE0000111122223333444455556667778'
14+
RFF+Z13:55001'
15+
SEQ+Z01'
16+
CCI+Z30++Z07'
17+
CCI+Z19++11X0-0000-0116-J'
18+
CCI+++Z15'
19+
CCI+++Z88'
20+
CAV+Z74:::Z09'
21+
CAV+Z73:::Z11'
22+
SEQ+Z12'
23+
QTY+Z16:0:P1'
24+
SEQ+Z03'
25+
CCI+++E13'
26+
CAV+Z30:::788811123'
27+
SEQ+Z75'
28+
CCI+Z61++ZG1'
29+
NAD+Z09+++Schaefer:Ulrike:::Frau:Z01'
30+
NAD+Z04+++Schaefer:Ulrike:::Frau:Z01+Flughafenstrasse::64+Vilseck++92247 +DE'
31+
NAD+DP++++Flughafenstrasse::64+Vilseck++92247+DE'
32+
NAD+Z05+++Schaefer:Ulrike:::Frau:Z01+Flughafenstrasse::64+Vilseck++92247 +DE'
33+
UNT+31+11223344556678'
34+
UNZ+1+ASDFHGJ'

TransformerBeeClient/TransformerBeeClient.IntegrationTest/TransformerBeeClient.IntegrationTest.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,10 @@
2222
/>
2323
</ItemGroup>
2424

25+
<ItemGroup>
26+
<None Update="TestEdifacts\FV2310\55001.edi">
27+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
28+
</None>
29+
</ItemGroup>
30+
2531
</Project>

TransformerBeeClient/TransformerBeeClient.sln.DotSettings.user

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=de4efd69_002D9f4a_002D4e00_002D83fa_002Ddaa381865bd8/@EntryIndexedValue">&lt;SessionState ContinuousTestingMode="0" IsActive="True" Name="ConnectionTest" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"&gt;&#xD;
33
&lt;TestAncestor&gt;&#xD;
44
&lt;TestId&gt;xUnit::80EF1570-CB4E-4E56-A8BC-56C787A48543::net8.0::TransformerBeeClient.UnitTest.ConnectionTests&lt;/TestId&gt;&#xD;
5+
&lt;TestId&gt;xUnit::77598126-7BB0-4C07-BF06-331033E0DE78::net8.0::TransformerBeeClient.IntegrationTest.EdifactToBo4eTests.Edifact_Shall_Be_Converted_To_Bo4e&lt;/TestId&gt;&#xD;
56
&lt;/TestAncestor&gt;&#xD;
67
&lt;/SessionState&gt;</s:String></wpf:ResourceDictionary>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using EDILibrary;
2+
using TransformerBeeClient.Model;
3+
4+
namespace TransformerBeeClient;
5+
6+
/// <summary>
7+
/// Interface of all the things that can convert EDIFACT to BO4E
8+
/// </summary>
9+
/// <remarks>This will be useful if you want to mock the client elsewhere</remarks>
10+
public interface ICanConvertToBo4e
11+
{
12+
/// <summary>
13+
/// convert an edifact to BO4E
14+
/// </summary>
15+
/// <param name="edifact">edifact message as string</param>
16+
/// <param name="formatVersion"><see cref="EdifactFormatVersion"/></param>
17+
/// <returns><see cref="Marktnachricht"/></returns>
18+
public Task<List<Marktnachricht>> ConvertToBo4e(string edifact, EdifactFormatVersion formatVersion);
19+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Text.Json.Serialization;
2+
using BO4E.BO;
3+
using Newtonsoft.Json;
4+
5+
namespace TransformerBeeClient.Model;
6+
7+
/// <summary>
8+
/// BOneyComb is a data structure that represents a "transaction" in the marktkommunikation.
9+
/// 1 transaction = 1 Geschäftsvorfall
10+
/// </summary>
11+
public class BOneyComb
12+
{
13+
/// <summary>
14+
/// the business objects
15+
/// </summary>
16+
[JsonPropertyName("stammdaten")] // we want to support both System.Text and Newtonsoft as long as BO4E.net does so
17+
[JsonProperty(PropertyName = "stammdaten")]
18+
public List<BusinessObject> Stammdaten { get; set; }
19+
20+
/// <summary>
21+
/// Transaktionsdaten are metadata related to the Marktprozess and are not related to a specific Business object.
22+
/// </summary>
23+
[JsonPropertyName("transaktionsdaten")] // we want to support both System.Text and Newtonsoft as long as BO4E.net does so
24+
[JsonProperty(PropertyName = "transaktionsdaten")]
25+
public Dictionary<string, string> Transaktionsdaten { get; set; }
26+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using EDILibrary;
2+
3+
namespace TransformerBeeClient.Model;
4+
5+
/// <summary>
6+
/// The request to convert edifact to bo4e
7+
/// </summary>
8+
internal class EdifactToBo4eRequest
9+
{
10+
// internally we only use system.text, no newtonsoft
11+
12+
/// <summary>
13+
/// the edifact as plain string
14+
/// </summary>
15+
[System.Text.Json.Serialization.JsonPropertyName("EDI")]
16+
public string Edifact { get; set; }
17+
18+
/// <summary>
19+
/// the format version to use
20+
/// </summary>
21+
[System.Text.Json.Serialization.JsonPropertyName("FormatPackage")]
22+
public EdifactFormatVersion FormatVersion { get; set; }
23+
24+
/// <summary>
25+
/// legacy for MP. can be false by default
26+
/// </summary>
27+
[System.Text.Json.Serialization.JsonPropertyName("UseMap")]
28+
public bool UseMap { get; set; } = false;
29+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using EDILibrary;
2+
3+
namespace TransformerBeeClient.Model;
4+
5+
/// <summary>
6+
/// The response to a <see cref="EdifactToBo4eRequest"/>
7+
/// </summary>
8+
internal class EdifactToBo4eResponse
9+
{
10+
// internally we only use system.text, no newtonsoft
11+
12+
/// <summary>
13+
/// the bo4e as plain json string
14+
/// </summary>
15+
[System.Text.Json.Serialization.JsonPropertyName("BO4E")]
16+
public string Bo4eJsonString { get; set; }
17+
18+
/// <summary>
19+
/// the format version that was used
20+
/// </summary>
21+
[System.Text.Json.Serialization.JsonPropertyName("FormatPackage")]
22+
public EdifactFormatVersion FormatVersion { get; set; }
23+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Text.Json.Serialization;
2+
using Newtonsoft.Json;
3+
4+
namespace TransformerBeeClient.Model;
5+
6+
/// <summary>
7+
/// A marktnachricht contains of 1 or more <see cref="BOneyComb"/>s
8+
/// </summary>
9+
/// <remarks>E.g. a UTILMD message with more than 1 IDE contains multiple transactions</remarks>
10+
public class Marktnachricht
11+
{
12+
/// <summary>
13+
/// the UNH (interchange header) of the message
14+
/// </summary>
15+
[JsonPropertyName("UNH")] // we want to support both System.Text and Newtonsoft as long as BO4E.net does so
16+
[JsonProperty(PropertyName = "UNH")]
17+
public string? UNH { get; set; }
18+
19+
/// <summary>
20+
/// One marktnachricht contains at least 1 transaction aka BOneyComb
21+
/// </summary>
22+
[JsonPropertyName("transaktionen")]// we want to support both System.Text and Newtonsoft as long as BO4E.net does so
23+
[JsonProperty(PropertyName = "transaktionen")]
24+
public List<BOneyComb> Transaktionen { get; set; }
25+
26+
/// <summary>
27+
/// Nachrichtendaten are similar to <see cref="BOneyComb.Transaktionsdaten"/> but are not 100% identical.
28+
/// </summary>
29+
[JsonPropertyName("nachrichtendaten")] // we want to support both System.Text and Newtonsoft as long as BO4E.net does so
30+
[JsonProperty(PropertyName = "nachrichtendaten")]
31+
public Dictionary<string, string> Nachrichtendaten { get; set; }
32+
}

TransformerBeeClient/TransformerBeeClient/RestClient.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
1+
using System.Text;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
using EDILibrary;
5+
using TransformerBeeClient.Model;
6+
17
namespace TransformerBeeClient;
28

39
/// <summary>
410
/// a client for the transformer.bee REST API
511
/// </summary>
6-
public class TransformerBeeRestClient
12+
public class TransformerBeeRestClient : ICanConvertToBo4e
713
{
814
private readonly HttpClient _httpClient;
15+
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
16+
{
17+
PropertyNameCaseInsensitive = true,
18+
Converters = { new JsonStringEnumConverter() }
19+
};
920

1021
/// <summary>
1122
/// Provide the constructor with a http client factory.
@@ -43,4 +54,39 @@ public async Task<bool> IsAvailable()
4354
var response = await _httpClient.GetAsync(versionUrl);
4455
return response.IsSuccessStatusCode;
4556
}
57+
58+
/// <summary>
59+
/// convert an edifact to BO4E
60+
/// </summary>
61+
/// <param name="edifact">edifact message as string</param>
62+
/// <param name="formatVersion"><see cref="EdifactFormatVersion"/></param>
63+
/// <returns><see cref="Marktnachricht"/></returns>
64+
/// <exception cref="HttpRequestException"></exception>
65+
public async Task<List<Marktnachricht>> ConvertToBo4e(string edifact, EdifactFormatVersion formatVersion)
66+
{
67+
var uriBuilder = new UriBuilder(_httpClient!.BaseAddress)
68+
{
69+
Path = "/v1/transformer/EdiToBo4E"
70+
};
71+
72+
var convertUrl = uriBuilder.Uri.AbsoluteUri;
73+
var request = new EdifactToBo4eRequest
74+
{
75+
Edifact = edifact,
76+
FormatVersion = formatVersion,
77+
};
78+
var requestJson = JsonSerializer.Serialize(request, _jsonSerializerOptions);
79+
var httpResponse = await _httpClient.PostAsync(convertUrl, new StringContent(requestJson, Encoding.UTF8, "application/json"));
80+
if (!httpResponse.IsSuccessStatusCode)
81+
{
82+
throw new HttpRequestException($"Could not convert {edifact} to BO4E. Status code: {httpResponse.StatusCode}");
83+
}
84+
var responseContent = await httpResponse.Content.ReadAsStringAsync();
85+
var bo4eResponse = JsonSerializer.Deserialize<EdifactToBo4eResponse>(responseContent, _jsonSerializerOptions);
86+
// todo: handle the case that the deserialization fails and bo4eResponse is null
87+
var unescapedJson = bo4eResponse!.Bo4eJsonString.Unescape();
88+
var result = JsonSerializer.Deserialize<List<Marktnachricht>>(unescapedJson, _jsonSerializerOptions);
89+
// todo: handle the case that the deserialization fails and result is null
90+
return result;
91+
}
4692
}

0 commit comments

Comments
 (0)