Skip to main content

Generate a Typed OpenAPI Client

The SAP Cloud SDK offers an OpenAPI client generator as a Maven plugin and as a command-line tool. Either can be used to generate a client library for a REST API based on its OpenAPI specification. The OpenAPI generator is a wrapper around the public open-source OpenAPI Generator where we have adjusted the mustache templates to integrate the generated client library with the SAP Cloud SDK core.

Localisation

The generator is designed to generate source code in english. You may also generate a client based on other languages in the OpenAPI specification. However, languages that use non-latin characters, specifically languages that read from right to left or that don't have capitalisation, may not be supported.

The generated Java classes need the following dependency to be present in your project:

<dependency>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>openapi-core</artifactId>
</dependency>

To use the generated client library, either the SAP Cloud SDK BOM should be part of your project's <dependencyManagement> section, or the version for the openapi-core artifact must be mentioned explicitly here.

Generated Java Classes and Usage

In this section, we will explain how to use the OpenAPI Generator Maven Plugin. A sample result can be seen in our open-source public repository with the SodaStore OpenAPI. Given the sodastore.json as input the following service classes get generated:

openapi-api-sample/
├── src/main/
│ ├── java/com/sap/cloud/sdk/datamodel/openapi/sample/
│ | ├── api/
│ | | ├── OrdersApi.java
│ | | └── SodasApi.java
│ | └── model/
│ | └── ...
│ └── resources/
│ └── sodastore.json
└── pom.xml

The generated API can be used like the following:

Destination destination;

// create the service with a destination
SodasApi service = new SodasApi(destination);

// create an entity object
SodaWithId soda = new SodaWithId().id(0L).name("Cola").brand("SAP-Cola").quantity(100).price(1.5f);

// execute request for entity update
service.sodasPut(soda);

Services can be instantiated in different ways:

new SodasApi( Destination )
new SodasApi( new ApiClient() )
new SodasApi( new ApiClient(RestTemplate) )
new SodasApi( new ApiClient(Destination) )

The ApiClient allows for versatile customization. Default service base-path, user-agent and request headers can be assigned. When using Destination these parameters are extracted automatically from the provided destination. Alternatively, the RestTemplate based constructor can be used for fully customized requests.

Using the OpenAPI Generator Maven Plugin

To use the Maven plugin following XML snippet should be added to the POM file of your project:

<plugin>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<!-- Maintain Maven property sap-cloud-sdk.version in your POM with the latest SAP Cloud SDK version -->
<version>${sap-cloud-sdk.version}</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<phase>generate-sources</phase>
</execution>
</executions>
<configuration>
<inputSpec>${project.basedir}/sample.yaml</inputSpec>
<outputDirectory>${project.basedir}/openapi</outputDirectory>
<apiPackage>com.mycompany.openapi.sample.api</apiPackage>
<modelPackage>com.mycompany.openapi.sample.model</modelPackage>
<apiMaturity>released</apiMaturity>
<!-- (Optional) You can add a custom copyright header:
<copyrightHeader>Copyright (c) this year, my company</copyrightHeader>

Or use the SAP copyright header:
<sapCopyrightHeader>true</sapCopyrightHeader>
-->
</configuration>
</plugin>

Maven requires that we specify the version for plugins explicitly. In above code snippet you can see the version tag pointing to the Maven property sap-cloud-sdk.version. Maintain such a property in your POM and ensure that you always use the latest SAP Cloud SDK version.

<properties>
<!-- Use latest version always as per https://sap.github.io/cloud-sdk/docs/java/release-notes -->
<sap-cloud-sdk.version>5.XX.X</sap-cloud-sdk.version>
</properties>

Maven will run the generator within the generate-sources phase which is executed before compile.

note

The phase generate-sources is just a recommendation from our side, it can be changed per your project's requirement. Refer to the Maven build lifecycle guide for a complete explanation.

Please note that the version of the above plugin must be specified at least once in your project, preferably in the root POM. Also, the version of the plugin should be the same as of the SAP Cloud SDK. We recommend using a Maven property for defining the version of both the Maven plugin and the SAP Cloud SDK BOM.

Using the Plugin from the Command Line

The maven plugin can also be invoked without a project from the command line using -D parameter flags, for example:

mvn com.sap.cloud.sdk.datamodel:openapi-generator-maven-plugin:5.XX.X:generate -Dopenapi.generate.inputSpec=foo -Dopenapi.generate.outputDirectory=bar -Dopenapi.generate.apiPackage=api -Dopenapi.generate.modelPackage=model

See the full list of parameters here.

Generating Java Client Library for Multiple OpenAPI Specifications

This Maven plugin processes one OpenAPI specification per execution. In case you want to generate sources for multiple OpenAPI specifications then you need to create multiple executions of the plugin each corresponding to a particular OpenAPI specification. As an example refer to the plugin XML below:

<plugin>
<groupId>com.sap.cloud.sdk.datamodel</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-sample1-id</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/sample1.yaml</inputSpec>
</configuration>
</execution>
<execution>
<id>generate-sample2-id</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/sample2.yaml</inputSpec>
</configuration>
</execution>
</executions>
<configuration>
<apiPackage>com.mycompany.openapi.sample.api</apiPackage>
<modelPackage>com.mycompany.openapi.sample.api.model</modelPackage>
<outputDirectory>${project.basedir}/openapi</outputDirectory>
<apiMaturity>released</apiMaturity>
</configuration>
</plugin>

Available Parameters

Passing Additional Properties to the Underlying OpenAPI Generator

You can pass arbitrary additional configuration properties to the underlying open source OpenAPI generator via <additionalProperties>.

The complete list of available parameters with their description is as follows:

ParameterDefaultRequiredDescription
<inputSpec>-YesLocation of the OpenAPI specification file
<outputDirectory>-YesOutput directory for generated sources
<apiPackage>-YesPackage name for the generated API classes
<modelPackage>-YesPackage name for the generated Model classes
<deleteOutputDirectory>FalseNoDetermines whether to delete the output directory before running the generator
<apiMaturity>releasedNoDefines the maturity of the OpenAPI for which Java classes are generated. Possible values are released and beta. Please note if you define it as beta then @Beta annotations are added to the generated classes which indicate that they are in an experimental state
<compileScope>NONENoAdds the generated sources to the compilation or test phase. Respective values are COMPILE and TEST_COMPILE
<copyrightHeader>nullNoCopyright header to be added at the top of generated files
<verbose>FalseNoRun the generator with verbose output
<sapCopyrightHeader>FalseNoAdd the SAP copyright header (overrides copyrightHeader)
<enableOneOfAnyOfGeneration>FalseNoDefines whether to enable processing of anyOf/oneOf keywords during client generation
<additionalProperties>[]NoDefines a map of key-value pairs that will be passed to the Java generator. E.g. <param>value</param>

Customize Java Class and Method Names with OpenAPI Vendor Extensions

The OpenAPI generator uses the tag field to determine the Java service class name and the operationId to determine the Java method for each service operation. You can influence the service class name and the method names by adding extension fields to the OpenAPI specification. Thereby you can leave the tag and operationId fields untouched if they need to stay stable.

Java Class Name

The Java class name can be influenced with the extension field x-sap-cloud-sdk-api-name. The OpenAPI generator takes the value of x-sap-cloud-sdk-api-name and adds an Api suffix (if not already present) to determine the class name.

In the following example the custom value CustomOperations takes precedence over the tag value Operations

/operation/path:
get:
summary: My operation
operationId: myOperation
x-sap-cloud-sdk-api-name: CustomOperations
tags:
- Operations

The Java class for this service operation will be named CustomOperationsApi.

You can use the extension field x-sap-cloud-sdk-api-name on an operation, on a path or on the root level of the OpenAPI specification. All subordinated objects inherit its value automatically. For instance, if you assign the field x-sap-cloud-sdk-api-name to one path, all operations under that path inherit this field automatically.

Java Method Name

The Java method name can be overwritten with the extension field x-sap-cloud-sdk-operation-name.

In the following example the custom value performMyOperation takes precedence over the operationId value myOperation.

/operation/path:
get:
summary: My Operation
x-sap-cloud-sdk-operation-name: performMyOperation
operationId: myOperation

The Java method for this service operation will be named peformMyOperation.

Field Order

The order of fields in the OpenAPI specification does not matter. Only the right nesting is necessary, that is, the two fields x-sap-cloud-sdk-operation-name and x-sap-cloud-sdk-api-name should be assigned to an operation (get, post, etc).

Support for anyOf/oneOf keywords

info

From version 5.4 of the SAP Cloud SDK, the OpenAPI generator can be configured to generate clients for specification with anyOf/oneOf keywords by using the <enableOneOfAnyOfGeneration> parameter in the OpenAPI maven generator plugin. But, the generated client though may not be feature complete and work as expected for all cases involving anyOf/oneOf although supporting some use cases.

By default, the client generation for input specification with anyOf/oneOf keywords in the following locations fails:

  • Keywords existing under the paths part, under any operation while defining either the requestbody or responses
Keywords under paths
paths:
/APIPath:
put:
summary: ...
requestBody:
description: ...
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/SomeSchema'
- $ref: '#/components/schemas/SomeOtherSchema'
responses:
'200':
description: ...
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/SomeSchema'
- type: array
items:
$ref: '#/components/schemas/SomeOtherSchema'
  • Keywords existing under any of the schemas either under any property in properties or under additionalProperties
Keywords under properties of schema
components:
schemas:
SampleSchema:
title: SampleSchema
description: ...
type: object
properties:
embeddedData:
$ref: '#/components/schemas/EmbeddedData'
questions:
type: array
title: questions
items:
title: Question
oneOf:
- $ref: '#/components/schemas/mc'
- $ref: '#/components/schemas/te'
- $ref: '#/components/schemas/db'
discriminator:
propertyName: type
AnotherSampleSchema:
description: ...
type: ...
anotherProperty:
description: ...
oneOf:
- type: string
enum:
- ENDOFBLOCK
- ENDOFSURVEY
- $ref: '#/components/schemas/SurveyID'
additionalProperties:
oneOf:
- $ref: '#/components/schemas/SomeSchema'
- $ref: '#/components/schemas/SomeOtherSchema'

To make the generation to succeed, please use <enableOneOfAnyOfGeneration> parameter in the OpenAPI maven generator plugin and re-generate the client again.