Skip to content
Snippets Groups Projects
README.md 22.47 KiB

Logo

\textcolor{#890000}{\text{THIS SDK IS IN DEVELOPMENT AND NOT READY FOR PRODUCTION USE YET!!!}}


About the FIT-Connect Java SDK

The Java SDK for FIT-Connect enables to build clients for senders and subscribers without directly interacting with any REST-Endpoints. It provides a simple fluent API that guides through the creation and sending of a submission, as well as receiving submissions as a subscriber.

For further information, check out the official docs: FIT-Connect Documentation as well as the:

Outline

Getting Started

How to set up the SDK project locally.

Build Dependencies

This section lists major frameworks/libraries used in the SDK.

  • Java 11 (LTS)
  • Maven 3.x
  • Junit 5

FIT-Connect dependencies:

Further 3rd party dependencies:

  • Nimbus-Jose JWT
  • Spring Web/HTTP
  • Jackson FasterXMl
  • JCommander
  • Snakeyaml
  • OpenCSV

Prerequisites

  • Java Runtime >= 11, check your current setup in your commandline
    java --version 

(back to top)

Add FIT-Connect SDK to your build

To add a dependency on FIT-Connect using Maven, use the following:

<dependency>
  <groupId>dev.fitko.fitconnect.sdk</groupId>
  <artifactId>client</artifactId>
  <version>[Latest Version]</version>
</dependency>

With [Latest Version] of the last stable build or snapshot.

If you use a snapshot version, please add the maven snapshot repo to your pom.

<distributionManagement>
  <snapshotRepository>
  <id>ossrh</id>
  <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
  </snapshotRepository>
</distributionManagement>

(back to top)

Modules

See the projects' module documentation for more specific information:

Setup

The following steps show how to get the SDK running

  1. Create and account on the self-service portal

  2. Get your sender/subscriber API-key credentials

  3. Clone the sdk repo

    git clone https://git.fitko.de/fit-connect/sdk-java
  4. Build the project with the included maven wrapper (no separate maven installation needed)

    ./mvnw clean install -DskipTests
  5. Configure your config.yml (see template)

    • add Sender OAuth credentials from the self-service portal to SENDER section
    • add Subscriber OAuth credentials from the self-service portal to SUBSCRIBER section
    • add reference to private decryption key (JWK) to SUBSCRIBER section
    • add reference to private signature key (JWK) to SUBSCRIBER section
  6. Load a configuration via:

      final ApplicationConfig config = ApplicationConfigLoader.loadConfigFromPath(Path.of("path/to/config.yml"));
  7. Afterwards the config is used to initialize clients with the ClientFactory that offer clients for sender, subscriber and routing:

      final SenderClient senderClient = ClientFactory.getSenderClient(config);
        //
      final SubscriberClient subscriberClient = ClientFactory.getSubscriberClient(config);
        //
      final RoutingClient routingClient = ClientFactory.getRoutingClient(config);

(back to top)

API Usage for Routing

The Routing-Client allows to retrieve data about areas and services as well as their service destination. A typical workflow using the RoutingClient and SenderClient would be:

  1. Find the areaId for an area via routing
  2. Find the destination for a leikaKey and an areaId via routing
  3. Submit a new submission to the destination using the SenderClient

Finding Areas

Areas can be searched with one or more search criteria:

final RoutingClient routingClient = ClientFactory.getRoutingClient(config);

final var citySearchCriterion = "Leip*";
final var zipCodeSearchCriterion = "04229";

// get first 5 area results
final List<Area> areas = routingClient.findAreas(List.of(citySearchCriterion, zipCodeSearchCriterion), 0, 5);

LOGGER.info("Found {} areas", areas.size());
for (final Area area : areas){
    LOGGER.info("Area {} with id {} found", area.getName(), area.getId());
}

Finding Destinations

For searching a destination the DestinationSearch.Builder is used pass a search request to the routing client. The leikaKey is mandatory, as well as (max.) one other search criterion for the area/region, like one of:

  • areaId = identifier of an area that can be retrieved via findAreas(...) of the routing client
  • ARS = amtlicher Regionalschlüssel
  • AGS = amtlicher Gemeindeschlüssel

Note: Both, the leikaKey service-identifier and the region keys ars/agscannot be retrieved via the routing client, but can be found here:

Find destination by service identifier and areaId

final RoutingClient routingClient = ClientFactory.getRoutingClient(config);

final DestinationSearch search = DestinationSearch.Builder()
        .withLeikaKey("99123456760610")
        .withAreaId("48566") // areaId of "Leipzig"
        .withLimit(3)
        .build();

// get first 3 route results
final List<Route> routes = routingClient.findDestinations(search);

LOGGER.info("Found {} routes for service identifier {}", routes.size(), leikaKey);
for (final Route route : routes){
    LOGGER.info("Route {} with destinationId {} found", route.getName(), route.getDestinationId());
}

Find destination by service identifier and region key (ARS/AGS)

Besides the areaId another search criterion for the area/region can be used as well:

final RoutingClient routingClient = ClientFactory.getRoutingClient(config);

final DestinationSearch search = DestinationSearch.Builder()
        .withLeikaKey("99123456760610")
        .withArs("147130000000") // example ars for "Leipzig"
        .withLimit(3)
        .build();

// get first 3 route results
final List<Route> routes = routingClient.findDestinations(search);

LOGGER.info("Found {} routes for service identifier {}", routes.size(), leikaKey);
for (final Route route : routes){
    LOGGER.info("Route {} with destinationId {} found", route.getName(), route.getDestinationId());
}

API Usage for Sender

For sending submission and already encrypted submissions builder are provided to construct the necessary payload to be sent. Builders allow method chaining and guide through all necessary steps.

The examples below expect a loaded configuration via the ApplicationConfigLoader.

Hand in already encrypted submission (e.g. from frontend)

If all data, metadata and attachments are encrypted outside the SDK the sender client allows to retrieve the public key for encryption as well as sending of already encrypted payloads.

For further information on how to implement end-2-end encryption please check the FIT-Connect documentation.

1. Retrieve public encryption key:

final var destinationId = UUID.fromString("d2d43892-9d9c-4630-980a-5af341179b14");
final String publicJwkAsJsonString =  ClientFactory.getSenderClient(config).getPublicKey(destinationId);

2. Send encrypted data

To send an encrypted submission the builder SendableEncryptedSubmission.Builder is needed to construct an SendableEncryptedSubmission object. The payload can be submitted as shown below via the ClientFactory.

If optional attachments are added, the UUID needs to be provided for each attachment to be able to announce them before the submission is actually sent. This is necessary because the SDK does not have access to the already encrypted metadata that, which contains that information.

// The constructed client can be reused to send multiple submissions
final SenderClient senderClient = ClientFactory.getSenderClient(config);

final SendableEncryptedSubmission encryptedSubmission = SendableEncryptedSubmission.Builder()
        .setDestination(UUID.fromString("d2d43892-9d9c-4630-980a-5af341179b14"))
        .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Führerscheinummeldung")
        .setEncryptedMetadata("$encrpyt€ed metadata")
        .setEncryptedData("{$encrpyt€ed json}")
        .addEncryptedAttachment(UUID.fromString("d2d43892-9d9c-4630-980a-5af341179b14"), "$encrpyt€ed @tt@chment") // optional
        .build();

final SentSubmission sentSubmission = senderClient.send(encryptedSubmission);
Important
If destination id (destinationId) and service type (leikaKey) are provided by a frontend component, they MUST NOT be blindly trusted. Instead, the sender's backend MUST check if sending submissions of this service type to the specified destination is allowed.

Hand in a new submission with unencrypted data

If all data, metadata and attachments are encrypted in the sender using the SDK, the client automatically handles the encryption.

Before the actual sending, a set of validations is applied to guarantee a compatibility with the subscriber. One of these validations checks, if the submission data meets the requirements of the provided schema URI. Currently, this submission data schema validation is only run, if the data was provided in JSON form. A similar XML validation will be implemented in the future.

Be aware that this example is not end-2-end encrypted, see FIT-Connect documentation for details.

To send a submission the builder SendableSubmission.Builder is needed to construct a SendableSubmission object. The payload can be submitted as shown below via the ClientFactory.

// The constructed client can be reused to send multiple submissions
final SenderClient senderClient = ClientFactory.getSenderClient(config);

final SendableSubmission sendableSubmission = SendableSubmission.Builder()
        .setDestination(UUID.fromString("d2d43892-9d9c-4630-980a-5af341179b14"))
        .setServiceType("urn:de:fim:leika:leistung:99400048079000", "Führerscheinummeldung")
        .setJsonData("{ \"someString\" : \"foo\"}", URI.create("https://test.fitko.dev/fit-connect/schema/submission-data-schema.json"))
        // optional properties
        .addAttachment(Attachment.fromPath(Path.of("path/to/attachment.txt"), "text/plain"))
        .setReplyChannel(ReplyChannel.fromEmail("test@mail.org"))
        .build();

final SentSubmission sentSubmission = senderClient.send(sendableSubmission);

Read the latest status of a submission

For checking the current status of a submission getStatusForSubmission gets the latest event from the event-log.

final SentSubmission sentSubmission =  ... // persisted sent submission by sender client
final EventStatus submissionStatus = ClientFactory.getSenderClient(config).getStatusForSubmission(sentSubmission);

LOGGER.info("Current status for submission {} => {}", sentSubmission.getSubmissionId(), submissionStatus.getStatus());

The example output shows the current state of the submission after being created, following the transitions in the diagram below:

Current status for submission 43cf7163-5163-4bc8-865e-be96e271ecc3 => incomplete

Validating callbacks

When receiving callbacks from the FIT-Connect system, sender (and subscriber) of submissions have to check if the received data is valid to prevent unauthorized calls from messing with the business processes and user data. The values needed for this validation are transmitted within the received HTTP requests. FIT-Connect uses the HMAC mechanism to proof the validity of its calls.