Babelfish CDR
Metapackage Conversion in Progress
NB: We are converting Babelfish CDR to a Metapackage with multiple components, some links might 404 at the moment
The Babelfish CDR Library is a Java based Maven artifact for the efficient processing of JSON payloads associated with the Australian Consumer Data Right (aka open banking). It provides the ability to parse, manipulate, generate and validate compliant payloads associated with the Consumer Data Standards. It is annotated with industry standard Jackson and OpenAPI (fka Swagger) annotations to enable smooth integration into downstream projects.
Babelfish CDR is currently developed and maintained by Biza.io.
We welcome discussion around the Consumer Data Right at the DataRight.io Slack Channel.
Features
- Jackson mappings for all payloads defined within the Consumer Data Standards
- On demand conversion between payload versions powered by Orika Mapper
- All enumerations defined within Consumer Data Standards with human readable descriptions for presentation layer
- Serialisation and Deserialisation to strong Java types for all Common Field Types
- Java Validation (JSR-303) rules for all required attributes, Pagination and Product & Account Components
- Fluent Builders for all payloads powered by Lombok
- OpenAPI 3 (fka Swagger) annotations for all payloads suitable and tested for Spring Framework and Springdoc
- Reasonably well tested, over 350 validation rules and nearly 300 tests (but there is always room for more!)
- Lookup and Validation for Merchant Category Codes, BSB Numbers and SWIFT Routing Codes
Quick Start
Processing an input payload and validating it is as simple as:
ResponseBankingProductByIdV2 productResponse = new ObjectMapper().readValue(
"{\"links\":{\"self\":\"http://localhost/cds-au/v1/banking/products/073e7e70-357d-4858-8f52-92283f4edd6f\"},\"meta\":{},"
+ "\"data\":{\"productId\":\"073e7e70-357d-4858-8f52-92283f4edd6f\",\"lastUpdated\":\"2020-02-03T06:32:27Z\","
+ "\"productCategory\":\"TRANS_AND_SAVINGS_ACCOUNTS\",\"name\":\"Example Product\",\"description\":\"Example Product Description\","
+ "\"brand\":\"ACME\",\"brandName\":\"ACME Bank\",\"isTailored\":false}}",
ResponseBankingProductByIdV2.class);
Generating a compliant payload is as simple as:
ResponseBankingProductByIdV2 productResponse =
ResponseBankingProductByIdV2.builder()
.links(LinksV1.builder().self(URI.create(
"http://localhost/cds-au/v1/banking/products/073e7e70-357d-4858-8f52-92283f4edd6f"))
.build())
.meta(MetaV1.builder().build())
.data(BankingProductDetailV2.builder().productId("073e7e70-357d-4858-8f52-92283f4edd6f")
.lastUpdated(OffsetDateTime.now())
.productCategory(BankingProductCategory.TRANS_AND_SAVINGS_ACCOUNTS)
.name("Example Product").description("Example Product Description").brand("ACME")
.tailored(false).build())
.build();
Set<ConstraintViolation<ResponseBankingProductByIdV2>> productResponseValidation =
Validation.buildDefaultValidatorFactory().getValidator().validate(productResponse);
if (productResponseValidation.isEmpty()) {
System.out.println(new ObjectMapper().setSerializationInclusion(Include.NON_ABSENT).writeValueAsString(productResponse));
} else {
System.out.println(
"Object failed validation with errors of: " + productResponseValidation.toString());
}
Which yields:
{
"data":{
"productId":"073e7e70-357d-4858-8f52-92283f4edd6f",
"lastUpdated":"2020-02-05T10:02:25.26832+11:00",
"productCategory":"TRANS_AND_SAVINGS_ACCOUNTS",
"name":"Example Product",
"description":"Example Product Description",
"brand":"ACME",
"isTailored":false
},
"links":{
"self":"http://localhost/cds-au/v1/banking/products/073e7e70-357d-4858-8f52-92283f4edd6f"
},
"meta":{
}
}
Table of Contents
- Features
- Quick Start
- Usage
- Projects
- Support
- Compatibility
- Prerequisites
- Using Babelfish CDR in your project
- Extended Features
- Building
- Contributing
- License
Usage
This library is available on Maven Central and therefore can be utilised by adding the following lines to your pom.xml
:
<dependency>
<groupId>io.biza</groupId>
<artifactId>babelfish-cdr</artifactId>
<version>1.2.4</version>
</dependency>
Projects
babelfish-cdr
is utilised in a number of public projects:
If you are using babelfish-cdr
in your project drop us an email and we will add it here.
Support
Biza Pty Ltd are currently the primary maintainers of this software.
We welcome bug reports via GitHub Issues or if you prefer via email.
If you are looking for commercial support for this library please contact us via email.
Compatibility
The Babelfish CDR library tries to provide full coverage of payloads defined within the Consumer Data Standards. While we try to align our version numbers to those of the Standards unfortunately the DSB has chosen to use all of the x.y.z versioning of the semantic versioning scheme. Consequently the following table outlines the alignment between Babelfish versions and the Standards:
Babelfish CDR Version | Release Date | CDS Spec Compatibility | Notes | Status |
---|---|---|---|---|
1.2.5-SNAPSHOT (current develop) | Regular | 1.2.0 | Snapshot Development Release | Active Development |
1.2.4 (current stable) | 2020-02-25 | 1.2.0 | tag v1.2.4 | Supported |
1.2.3-SNAPSHOT | 2020-02-25 | 1.2.0 | Snapshot Development Release | Deprecated |
1.2.2 | 2020-02-12 | 1.2.0 | tag v1.2.2 | Deprecated |
1.2.1-SNAPSHOT | 2020-02-12 | 1.2.0 | Snapshot Development Release | Deprecated |
1.2.0 | 2020-02-05 | 1.2.0 | tag v1.2.0 | Deprecated |
1.0.0 | 2020-02-04 | 1.0.0 | Initial Release, do not use | Deprecated |
Prerequisites
You need the following installed and available in your $PATH during compilation:
- Java 11+
- Apache Maven 3.6.3 or later
Using Babelfish CDR in your project
Babelfish CDR is intended for embedding within your CDR client or server project as a Maven (or Gradle) dependency. Once it is added to your project you gain access to the ability to manipulate CDR payloads in a number of different ways.
Parse and Validate CDR Payload
Parsing a sample JSON payload can be conducted using Jackson and then verified using Java Validation API:
ResponseBankingProductByIdV2 productResponse = new ObjectMapper().readValue(
"{\"links\":{\"self\":\"http://localhost/cds-au/v1/banking/products/073e7e70-357d-4858-8f52-92283f4edd6f\"},\"meta\":{},"
+ "\"data\":{\"productId\":\"073e7e70-357d-4858-8f52-92283f4edd6f\",\"lastUpdated\":\"2020-02-03T06:32:27Z\","
+ "\"productCategory\":\"TRANS_AND_SAVINGS_ACCOUNTS\",\"name\":\"Example Product\",\"description\":\"Example Product Description\","
+ "\"brand\":\"ACME\",\"brandName\":\"ACME Bank\",\"isTailored\":false}}",
ResponseBankingProductByIdV2.class);
Set<ConstraintViolation<ResponseBankingProductByIdV2>> productResponseValidation =
Validation.buildDefaultValidatorFactory().getValidator().validate(productResponse);
Validate and Generate CDR Payload
Generating a CDR Payload can be conducted using the fluent builder API within Babelfish. Additionally the payload can be validated using Java Validation API.
An example is as follows:
ResponseBankingProductByIdV2 productResponse =
ResponseBankingProductByIdV2.builder()
.links(LinksV1.builder().self(URI.create(
"http://localhost/cds-au/v1/banking/products/073e7e70-357d-4858-8f52-92283f4edd6f"))
.build())
.meta(MetaV1.builder().build())
.data(BankingProductDetailV2.builder().productId("073e7e70-357d-4858-8f52-92283f4edd6f")
.lastUpdated(OffsetDateTime.now())
.productCategory(BankingProductCategory.TRANS_AND_SAVINGS_ACCOUNTS)
.name("Example Product").description("Example Product Description").brand("ACME")
.tailored(false).build())
.build();
Set<ConstraintViolation<ResponseBankingProductByIdV2>> productResponseValidation =
Validation.buildDefaultValidatorFactory().getValidator().validate(productResponse);
if (productResponseValidation.isEmpty()) {
System.out.println(new ObjectMapper().setSerializationInclusion(Include.NON_ABSENT).writeValueAsString(productResponse));
} else {
System.out.println(
"Object failed validation with errors of: " + productResponseValidation.toString());
}
Convert CDR Payload between versions
Babelfish enables you to convert between two different payload versions. This means that a Data Recipient can build for a newer (or older) version of a payload while maintaining ongoing compatibility from individual Data Holders who may be ahead or behind the version expected by the Recipient. We use Orika and a set of specific mappers for each different payload version and then make it available in BabelFishConverter
.
An example is as follows:
BankingProductDetailV2 detail = BankingProductDetailV2.builder()
.productId("073e7e70-357d-4858-8f52-92283f4edd6f").lastUpdated(OffsetDateTime.now())
.productCategory(BankingProductCategory.TRANS_AND_SAVINGS_ACCOUNTS).name("Example Product")
.description("Example Product Description").brand("ACME").isTailored(false).build();
// Downgrade it to V1
BankingProductDetailV1 downgradedDetail =
BabelFishConverter.convert(detail, BankingProductDetailV1.class);
// Upgrade back to V2
BankingProductDetailV2 upgradedDetail = BabelFishConverter.convert(downgradedDetail, BankingProductDetailV2.class);
Dynamically discover what class to use for version conversion
To make it easier to integrate Babelfish into upstream projects we also make available the BabelfishVersioner
class. Here's an example where we hand it a BankingProductDetailV2
and ask it for the nearest version (in this case the range is only 1 <= 2):
Class<?> destinationType = BabelfishVersioner.getVersionedClass(inputData.getClass(),
2, 1);
BabelfishVersioner.convert(inputData, destinationType);
This is coupled in Deep Thought to allow for dynamic payload version switching based on request parameters (in CDRVersioner).
Using in Spring
We are actively using Babelfish CDR payload descriptions within Deep Thought as part of a Spring + Springdoc project. The following demonstrates the definition of a Get Product Detail
endpoint:
@Operation(summary = "Get Product Detail",
description = "Returns details product information based on the specified product identifier")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "Response Successful",
content = @Content(schema = @Schema(oneOf = {ResponseBankingProductByIdV1.class,
ResponseBankingProductByIdV2.class}))),
@ApiResponse(responseCode = "404",
description = "Unable to find requested Product Identifier")})
@GetMapping(value = "/{productId}", produces = {MediaType.APPLICATION_JSON_VALUE})
default ResponseEntity<ResponseBankingProductByIdV2> getProductDetail(
@NotNull @Valid @PathVariable("productId") UUID productId) {
return getDelegate().getProductDetail(productId);
}
Extended Features
Babelfish incorporates a number of customised features to aid developers with integrating with and using the CDR payloads.
Enumeration Labels
All enumerations used within Babelfish provide a human readable label available via the label()
method:
System.out.println("name: " + BankingProductCategory.TRANS_AND_SAVINGS_ACCOUNTS.name()
+ " label: " + BankingProductCategory.TRANS_AND_SAVINGS_ACCOUNTS.label());
// Result: name: TRANS_AND_SAVINGS_ACCOUNTS label: Transaction & Savings
JSON (De)Serialisators
Babelfish employs a set of StdConverter
extensions to facilitate the conversion to/from Common Field Types specified within the Standards. These are embedded as @JsonSerialize
and @JsonDeserialize
annotations within payloads but can be used independently if desired.
The following provides a table of the converters available:
Common Field Type | CDS JSON Type | Babelfish Java Type | Serializer | Deserializer |
---|---|---|---|---|
DateTimeString | String | OffsetDateTime | OffsetDateTimeToDateTimeStringConverter | DateTimeStringToOffsetDateTimeConverter |
DateString | String | LocalDate | LocalDateToStringConverter | StringToLocalDateConverter |
CurrencyString | String | Currency | CurrencyToStringConverter | StringToCurrencyConverter |
RateString | String | BigDecimal | BigDecimalToRateStringConverter | RateStringToBigDecimalConverter |
AmountString | String | BigDecimal | BigDecimalToAmountStringConverter | AmountStringToBigDecimalConverter |
URIString | String | URI | UriToUriStringConverter | UriStringToUriConverter |
ExternalRef (Country) | String | Locale | CountryStringToLocaleConverter | LocaleToCountryStringConverter |
ExternalRef (Duration) | String | Duration | StringToDurationConverter | DurationToStringConverter |
ExternalRef (Period) | String | Period | StringToPeriodConverter | PeriodToStringConverter |
ExternalRef (MCC) | String | MerchantCategoryCodeType | StringToMerchantCategoryCodeConverter | MerchantCategoryCodeToStringConverter |
ExternalRef (APCA Number) | String | ApcaNumberType | StringToApcaNumberConverter | ApcaNumberToStringConverter |
ExternalRef (SWIFT Routing) | String | SwiftBicType | StringToSwiftBicConverter | SwiftBicToStringConverter |
Base64 | String (Base64) | String | StringToBase64StringConverter | Base64StringToStringConverter |
Custom Assertions
Many of the payloads contain a custom javax.validation.Constraint
called AssertTrueBabelfish
.
AssertTrueBabelfish
behaves like the default AssertTrue
validation assertion but includes an additional String[]
array called fields
which provides information about the specific field(s) which were involved within a failure of the assertion. This annotation is used extensively for validating CDR payloads which utilise the additionalValue
conditional field structure and are intended to provide human presentable error messages along with a pointer to the field(s) in question.
One such example is in BankingConstraintV1
which verifies that when a Constraint Type of MIN_BALANCE
is specified the additionalValue
field must be in CDS AmountString format.
Another notable example demonstrating compound reporting is within BankingProductFeeV1
@AssertTrueBabelfish(
message = "One of amount, balanceRate, transactionRate or accruedRate is MANDATORY",
fields = {"amount", "balanceRate", "accruedRate", "transactionRate"})
This assertion reports a set of field names which can be highlighted but for which only one at a time is accepted.
Phone Number Parsing
The CommonPhoneNumberV1
class contains a number of additional functions using Google's PhoneNumberUtil library. Most notably there is a method of setWithFullNumber
which accepts a standard Australian number (for example 0733006000) and internationalises it into the requested format and fields. Use of this extension functionality is entirely optional.
Building
babelfish-cdr
uses Maven for compilation activies.
To build the library and install it in your local Maven cache:
cd babelfish-cdr
mvn clean install
Contributing
- Clone repository and create a new branch:
$ git checkout https://github.com/bizaio/babelfish-cdr -b my_new_branch
- Make changes (including tests please!)
- Submit Pull Request for integration of changes
License
GNU Lesser General Public License v3.0 2020 - Biza Pty Ltd. Please have a look at the LICENSE.md for more details.