Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ final class ODataGenericConverter<JavaT> extends AbstractTypeConverter<JavaT, St
new ODataGenericConverter<>(Duration.class, Duration::toString, Duration::parse);

private static final ODataGenericConverter<byte[]> BINARY =
new ODataGenericConverter<>(byte[].class, Base64.getEncoder()::encodeToString, Base64.getDecoder()::decode);
new ODataGenericConverter<>(
byte[].class,
Base64.getEncoder()::encodeToString,
ODataGenericConverter::decodeBinary);

private static final ODataGenericConverter<String> STRING =
new ODataGenericConverter<>(String.class, Function.identity(), Function.identity());
Expand All @@ -73,6 +76,15 @@ final class ODataGenericConverter<JavaT> extends AbstractTypeConverter<JavaT, St
private final Function<JavaT, String> serializer;
private final Function<String, JavaT> deserializer;

@Nonnull
private static byte[] decodeBinary( @Nonnull final String value )
{
// Normalize URL-safe characters to standard Base64
final String normalized = value.replace('-', '+').replace('_', '/');

return Base64.getDecoder().decode(normalized);
}

@Nonnull
@Override
public ConvertedObject<String> toDomainNonNull( @Nonnull final JavaT object )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.util.Base64;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
Expand Down Expand Up @@ -158,6 +159,15 @@ public static class ReferenceObject extends VdmEntity<ReferenceObject>
"GeographyPoint":{"type":"Point","coordinates":[142.1,64.1]}\
}\
""";

private static final String PAYLOAD_ODATA_REFERENCE_BASE64URL =
PAYLOAD_ODATA_REFERENCE.replace("\"BinaryValue\":\"AQID\"", "\"BinaryValue\":\"-__v\"");

private static final String PAYLOAD_ODATA_REFERENCE_MIXED_BASE64_ALPHABET =
PAYLOAD_ODATA_REFERENCE.replace("\"BinaryValue\":\"AQID\"", "\"BinaryValue\":\"+__v\"");

static final String Base_64 = "+//v";
static final String Base_64_Url = "-__v";
}

@Test
Expand All @@ -174,6 +184,21 @@ void testBinaryFieldParsingFromResponsePayload()
assertThat(ser).isEqualTo(ReferenceObject.PAYLOAD_ODATA_REFERENCE);
}

@Test
void testBinaryFieldParsingFromBase64UrlResponsePayload()
{
final ODataRequestResultGeneric result = mockRequestResult(ReferenceObject.PAYLOAD_ODATA_REFERENCE_BASE64URL);
final ReferenceObject referenceResult = result.as(ReferenceObject.class);

Objects.requireNonNull(referenceResult);
assertThat(referenceResult.getBinaryValue())
.isEqualTo(Base64.getUrlDecoder().decode(ReferenceObject.Base_64_Url));

final String ser =
new CreateRequestBuilder<>("/", referenceResult, "EntityCollection").toRequest().getSerializedEntity();
assertThat(ser).contains("\"BinaryValue\":\"" + ReferenceObject.Base_64 + "\"");
}

@Test
void testCustomFieldParsingFromResponsePayload()
{
Expand All @@ -186,6 +211,21 @@ void testCustomFieldParsingFromResponsePayload()
assertThat(referenceResult.<Object> getCustomField("GeographyPoint")).isInstanceOf(Map.class);
}

@Test
void testBinaryFieldParsingFromMixedBase64AlphabetNormalized()
{
final ODataRequestResultGeneric result =
mockRequestResult(ReferenceObject.PAYLOAD_ODATA_REFERENCE_MIXED_BASE64_ALPHABET);
final ReferenceObject referenceResult = result.as(ReferenceObject.class);

Objects.requireNonNull(referenceResult);
assertThat(referenceResult.getBinaryValue()).isEqualTo(Base64.getDecoder().decode(ReferenceObject.Base_64));

final String ser =
new CreateRequestBuilder<>("/", referenceResult, "EntityCollection").toRequest().getSerializedEntity();
assertThat(ser).contains("\"BinaryValue\":\"" + ReferenceObject.Base_64 + "\"");
}

@SneakyThrows
private static ODataRequestResultGeneric mockRequestResult( final String payload )
{
Expand Down
3 changes: 2 additions & 1 deletion release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
### ✨ New Functionality

- [OpenAPI] Cloud SDK OpenAPI Generator now supports `apache-httpclient` library besides Spring RestTemplate through the newly introduced module `openapi-core-apache`.
- [IAS] Add `IasOptions.withTokenFormat()` to allow specifying token format

### 📈 Improvements

-

### 🐛 Fixed Issues

-
- [OData v4] Binary deserialization can now handle both `Base64URL` and `Base64`.