diff --git a/Makefile b/Makefile
index dd8ad66..762fd2f 100644
--- a/Makefile
+++ b/Makefile
@@ -17,3 +17,11 @@ download-oas:
generate-sdk:
@$(SCRIPTS_BASE)/generate-sdk/generate-sdk.sh "$(GIT_HOST)" "$(GIT_USER_ID)" "$(GIT_REPO_ID)" "$(SDK_REPO_URL)" "$(LANGUAGE)" "$(SDK_BRANCH)"
+generate-go-sdk:
+ @$(SCRIPTS_BASE)/generate-sdk/generate-sdk.sh "$(GIT_HOST)" "$(GIT_USER_ID)" "$(GIT_REPO_ID)" "$(SDK_REPO_URL)" "go" "$(SDK_BRANCH)"
+
+generate-python-sdk:
+ @$(SCRIPTS_BASE)/generate-sdk/generate-sdk.sh "$(GIT_HOST)" "$(GIT_USER_ID)" "$(GIT_REPO_ID)" "$(SDK_REPO_URL)" "python" "$(SDK_BRANCH)"
+
+generate-java-sdk:
+ @$(SCRIPTS_BASE)/generate-sdk/generate-sdk.sh "$(GIT_HOST)" "$(GIT_USER_ID)" "$(GIT_REPO_ID)" "$(SDK_REPO_URL)" "java" "$(SDK_BRANCH)"
\ No newline at end of file
diff --git a/README.md b/README.md
index 72666fd..3a8a083 100644
--- a/README.md
+++ b/README.md
@@ -13,19 +13,29 @@ If you want to modify script or templates and you can run code locally.
Requires `Go 1.21` or higher.
1. Set up the project and tools by running
- ```
+ ```bash
make project-tools
```
2. Download Open Api Specifications (OAS), that are the input for the SDK generation, by running
- ```
+ ```bash
make download-oas
```
This step needs to be done only at the first start and when OAS updates are present.
-3. Run the SDK generation for testing by
+3. Run the Go SDK generation for testing by
+ ```bash
+ make generate-go-sdk
+ ```
+ The output goes to the `./sdk-repo-updated` folder.
+ 1. Run the Python SDK generation for testing by
+ ```bash
+ make generate-python-sdk
```
- make generate-sdk
+ The output goes to the `./sdk-repo-updated` folder.
+ 2. Run the Java SDK generation for testing by
+ ```bash
+ make generate-java-sdk
```
- The output goes to the `./sdk` folder.
+ The output goes to the `./sdk-repo-updated` folder.
## Reporting issues
diff --git a/openapi-generator-config-java.yml b/openapi-generator-config-java.yml
new file mode 100644
index 0000000..19e3ae0
--- /dev/null
+++ b/openapi-generator-config-java.yml
@@ -0,0 +1,14 @@
+templateDir: templates/java
+additionalProperties:
+ artifactUrl: https://github.com/stackitcloud/stackit-sdk-java
+ developerName: STACKIT Developer Tools
+ developerEmail: developer-tools@stackit.cloud
+ developerOrganization: STACKIT
+ developerOrganizationUrl: https://www.stackit.de
+ groupId: cloud.stackit
+ hideGenerationTimestamp: true
+ licenseName: Apache License 2.0
+ licenseUrl: http://www.apache.org/licenses/
+ scmConnection: scm:git@github.com:stackitcloud/stackit-sdk-java.git
+ scmDeveloperConnection: scm:git@github.com:stackitcloud/stackit-sdk-java.git
+ scmUrl: https://github.com/stackitcloud/stackit-sdk-java
\ No newline at end of file
diff --git a/scripts/generate-sdk/.openapi-generator-ignore-java b/scripts/generate-sdk/.openapi-generator-ignore-java
new file mode 100644
index 0000000..653a423
--- /dev/null
+++ b/scripts/generate-sdk/.openapi-generator-ignore-java
@@ -0,0 +1,9 @@
+git_push.sh
+.travis.yml
+.gitignore
+api/openapi.yaml
+tox.ini
+**/.github/**
+**/AndroidManifest.xml
+pom.xml
+build.sbt
\ No newline at end of file
diff --git a/scripts/generate-sdk/generate-sdk.sh b/scripts/generate-sdk/generate-sdk.sh
index aec217b..7d45bef 100755
--- a/scripts/generate-sdk/generate-sdk.sh
+++ b/scripts/generate-sdk/generate-sdk.sh
@@ -57,6 +57,10 @@ go)
GENERATOR_VERSION="v6.6.0" # There are issues with GO SDK generation in version v7
;;
python)
+# Renovate: datasource=github-tags depName=OpenAPITools/openapi-generator versioning=semver
+ GENERATOR_VERSION="v7.14.0"
+ ;;
+java)
# Renovate: datasource=github-tags depName=OpenAPITools/openapi-generator versioning=semver
GENERATOR_VERSION="v7.14.0"
;;
@@ -94,6 +98,13 @@ python)
# Usage: generate_python_sdk GENERATOR_PATH GIT_HOST GIT_USER_ID [GIT_REPO_ID] [SDK_REPO_URL] [SDK_BRANCH]
generate_python_sdk "${jar_path}" "${GIT_HOST}" "${GIT_USER_ID}" "${GIT_REPO_ID}" "${SDK_REPO_URL}" "${SDK_BRANCH}"
;;
+java)
+ echo -e "\n>> Generating the Java SDK..."
+
+ source ${LANGUAGE_GENERATORS_FOLDER_PATH}/${LANGUAGE}.sh
+ # Usage: generate_java_sdk GENERATOR_PATH GIT_HOST GIT_USER_ID [GIT_REPO_ID] [SDK_REPO_URL] [SDK_BRANCH]
+ generate_java_sdk "${jar_path}" "${GIT_HOST}" "${GIT_USER_ID}" "${GIT_REPO_ID}" "${SDK_REPO_URL}" "${SDK_BRANCH}"
+ ;;
*)
echo "! SDK language not supported."
exit 1
diff --git a/scripts/generate-sdk/languages/java.sh b/scripts/generate-sdk/languages/java.sh
new file mode 100644
index 0000000..8121cae
--- /dev/null
+++ b/scripts/generate-sdk/languages/java.sh
@@ -0,0 +1,160 @@
+#!/bin/bash
+# This script clones the SDK repo and updates it with the generated API modules
+# Pre-requisites: Java
+set -eo pipefail
+
+ROOT_DIR=$(git rev-parse --show-toplevel)
+SDK_REPO_LOCAL_PATH="${ROOT_DIR}/sdk-repo-updated"
+
+SERVICES_FOLDER="${SDK_REPO_LOCAL_PATH}/services"
+
+GENERATOR_LOG_LEVEL="error" # Must be a Java log level (error, warn, info...)
+
+INCLUDE_SERVICES=("resourcemanager")
+
+generate_java_sdk() {
+ # Required parameters
+ local GENERATOR_JAR_PATH=$1
+ local GIT_HOST=$2
+ local GIT_USER_ID=$3
+
+ # Optional parameters
+ local GIT_REPO_ID=$4
+ local SDK_REPO_URL=$5
+ local SDK_BRANCH=$6
+
+ # Check required parameters
+ if [[ -z ${GIT_HOST} ]]; then
+ echo "! GIT_HOST not specified."
+ exit 1
+ fi
+
+ if [[ -z ${GIT_USER_ID} ]]; then
+ echo "! GIT_USER_ID id not specified."
+ exit 1
+ fi
+
+ # Check optional parameters and set defaults if not provided
+ if [[ -z ${GIT_REPO_ID} ]]; then
+ echo "GIT_REPO_ID not specified, default will be used."
+ GIT_REPO_ID="stackit-sdk-java"
+ fi
+
+ if [[ -z ${SDK_REPO_URL} ]]; then
+ echo "SDK_REPO_URL not specified, default will be used."
+ SDK_REPO_URL="https://github.com/stackitcloud/stackit-sdk-java.git"
+ fi
+
+ # Prepare folders
+ if [[ ! -d $SERVICES_FOLDER ]]; then
+ mkdir -p "$SERVICES_FOLDER"
+ fi
+
+ # Clone SDK repo
+ if [ -d ${SDK_REPO_LOCAL_PATH} ]; then
+ echo "Old SDK repo clone was found, it will be removed."
+ rm -rf ${SDK_REPO_LOCAL_PATH}
+ fi
+ git clone --quiet -b ${SDK_BRANCH} ${SDK_REPO_URL} ${SDK_REPO_LOCAL_PATH}
+
+ # Backup of the current state of the SDK services dir (services/)
+ sdk_services_backup_dir=$(mktemp -d)
+ if [[ ! ${sdk_services_backup_dir} || -d {sdk_services_backup_dir} ]]; then
+ echo "! Unable to create temporary directory"
+ exit 1
+ fi
+ cleanup() {
+ rm -rf ${sdk_services_backup_dir}
+ }
+ cp -a "${SERVICES_FOLDER}/." ${sdk_services_backup_dir}
+
+ # Cleanup after we are done
+ trap cleanup EXIT
+
+ # Remove old contents of services dir (services/)
+ rm -rf ${SERVICES_FOLDER}
+
+ # Generate SDK for each service
+ for service_json in ${ROOT_DIR}/oas/*.json; do
+ service="${service_json##*/}"
+ service="${service%.json}"
+
+ # Remove invalid characters to ensure a valid Java pkg name
+ service="${service//-/}" # remove dashes
+ service="${service// /}" # remove spaces
+ service=$(echo "${service}" | tr '[:upper:]' '[:lower:]') # convert upper case letters to lower case
+ service=$(echo "${service}" | tr -d -c '[:alnum:]') # remove non-alphanumeric characters
+
+ # Ensure the package name doesn't start with a number
+ if [[ "${service}" =~ ^[0-9] ]]; then
+ service="_${service}" # Prepend a valid prefix if it starts with a number
+ fi
+
+ if ! [[ ${INCLUDE_SERVICES[*]} =~ ${service} ]]; then
+ echo "Skipping not included service ${service}"
+ continue
+ fi
+
+ if grep -E "^$service$" ${ROOT_DIR}/blacklist.txt; then
+ echo "Skipping blacklisted service ${service}"
+ continue
+ fi
+
+ echo ">> Generating \"${service}\" service..."
+ cd ${ROOT_DIR}
+
+ mkdir -p "${SERVICES_FOLDER}/${service}/"
+ cp "${ROOT_DIR}/scripts/generate-sdk/.openapi-generator-ignore-java" "${SERVICES_FOLDER}/${service}/.openapi-generator-ignore"
+
+ SERVICE_DESCRIPTION=$(cat ${service_json} | jq .info.title --raw-output)
+
+ # Run the generator
+ java -Dlog.level=${GENERATOR_LOG_LEVEL} -jar ${jar_path} generate \
+ --generator-name java \
+ --input-spec "${service_json}" \
+ --output "${SERVICES_FOLDER}/${service}" \
+ --git-host ${GIT_HOST} \
+ --git-user-id ${GIT_USER_ID} \
+ --git-repo-id ${GIT_REPO_ID} \
+ --global-property apis,models,modelTests=false,modelDocs=false,apiDocs=false,apiTests=false,supportingFiles \
+ --additional-properties=artifactId="stackit-sdk-${service}",artifactDescription="${SERVICE_DESCRIPTION}",invokerPackage="cloud.stackit.sdk.${service}",modelPackage="cloud.stackit.sdk.${service}.model",apiPackage="cloud.stackit.sdk.${service}.api" >/dev/null \
+ --http-user-agent stackit-sdk-java/"${service}" \
+ --config openapi-generator-config-java.yml
+
+ # Remove unnecessary files
+ rm "${SERVICES_FOLDER}/${service}/.openapi-generator-ignore"
+ rm -r "${SERVICES_FOLDER}/${service}/.openapi-generator/"
+ rm "${SERVICES_FOLDER}/${service}/.github/workflows/maven.yml"
+
+ # If the service has a README.md file, move them inside the service folder
+ if [ -f ${sdk_services_backup_dir}/${service}/README.md ]; then
+ echo "Found ${service} \"README.md\" file"
+ cp -r ${sdk_services_backup_dir}/${service}/README.md ${SERVICES_FOLDER}/${service}/README.md
+ fi
+
+ # If the service has a CHANGELOG file, move it inside the service folder
+ if [ -f ${sdk_services_backup_dir}/${service}/CHANGELOG.md ]; then
+ echo "Found ${service} \"CHANGELOG\" file"
+ cp -r ${sdk_services_backup_dir}/${service}/CHANGELOG.md ${SERVICES_FOLDER}/${service}/CHANGELOG.md
+ fi
+
+ # If the service has a LICENSE file, move it inside the service folder
+ if [ -f ${sdk_services_backup_dir}/${service}/LICENSE.md ]; then
+ echo "Found ${service} \"LICENSE\" file"
+ cp -r ${sdk_services_backup_dir}/${service}/LICENSE.md ${SERVICES_FOLDER}/${service}/LICENSE.md
+ fi
+
+ # If the service has a NOTICE file, move it inside the service folder
+ if [ -f ${sdk_services_backup_dir}/${service}/NOTICE.txt ]; then
+ echo "Found ${service} \"NOTICE\" file"
+ cp -r ${sdk_services_backup_dir}/${service}/NOTICE.txt ${SERVICES_FOLDER}/${service}/NOTICE.txt
+ fi
+
+ # If the service has a VERSION file, move it inside the service folder
+ if [ -f ${sdk_services_backup_dir}/${service}/VERSION ]; then
+ echo "Found ${service} \"VERSION\" file"
+ cp -r ${sdk_services_backup_dir}/${service}/VERSION ${SERVICES_FOLDER}/${service}/VERSION
+ fi
+
+ done
+}
diff --git a/templates/java/README.md b/templates/java/README.md
new file mode 100644
index 0000000..75f1b25
--- /dev/null
+++ b/templates/java/README.md
@@ -0,0 +1,14 @@
+# Java templates
+
+This folder contains only our customized Java templates. Beside these customized templates,
+the original templates of openapi-generator for Java are used. These can be found in the
+official GitHub repo of the [openapi-generator](https://github.com/OpenAPITools/openapi-generator/tree/v7.14.0/modules/openapi-generator/src/main/resources/Java).
+
+If you need to change something in the Java Generator, try always first to add
+[user-defined templates](https://openapi-generator.tech/docs/customization#user-defined-templates),
+instead of overwriting existing templates. These ensure an easier upgrade process, to newer
+versions of the openapi-generator.
+
+If it's required to customize the original templates, you can copy them into this directory.
+Try to minimize the customization as much as possible, to ensure, that we can easily upgrade
+to newer versions in the future.
diff --git a/templates/java/README.mustache b/templates/java/README.mustache
new file mode 100644
index 0000000..f3e06d8
--- /dev/null
+++ b/templates/java/README.mustache
@@ -0,0 +1,179 @@
+# {{artifactId}}
+
+{{appName}}
+
+- API version: {{appVersion}}
+
+{{{appDescriptionWithNewLines}}}
+
+{{#infoUrl}}
+For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}})
+{{/infoUrl}}
+
+This package is part of the STACKIT Java SDK. For additional information, please visit the [GitHub repository](https://{{gitHost}}/{{{gitUserId}}}/{{{gitRepoId}}}) of the SDK.
+
+
+## Requirements
+
+Building the API client library requires:
+1. Java 1.8+
+
+## Installation
+
+To install the API client library to your local Maven repository, simply execute:
+
+```shell
+./gradlew publishToMavenLocal
+```
+
+To deploy it to a remote Maven repository instead, configure the settings of the repository and execute:
+
+```shell
+# TODO: follow up story
+# ./gradlew publishToMavenCentral
+```
+
+Refer to the [OSSRH Guide](http://central.sonatype.org/pages/ossrh-guide.html) for more information.
+
+### Maven users
+
+Add this dependency to your project's POM:
+
+```xml
+
+ {{{groupId}}}
+ {{{artifactId}}}
+
+ compile
+
+```
+
+### Gradle users
+
+Add this dependency to your project's build file:
+
+```groovy
+ repositories {
+ mavenCentral() // Needed if the '{{{artifactId}}}' jar has been published to maven central.
+ mavenLocal() // Needed if the '{{{artifactId}}}' jar has been published to the local maven repo.
+ }
+
+ dependencies {
+ implementation "{{{groupId}}}:{{{artifactId}}}:"
+ }
+```
+
+### Others
+
+At first generate the JAR by executing:
+
+```shell
+mvn clean package
+```
+
+Then manually install the following JARs:
+
+- `target/{{{artifactId}}}-.jar`
+- `target/lib/*.jar`
+
+{{#jersey2}}
+## Usage
+
+To add a HTTP proxy for the API client, use `ClientConfig`:
+```java
+{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}
+import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.ClientProperties;
+import {{{invokerPackage}}}.*;
+import {{{package}}}.{{{classname}}};
+
+...
+
+ApiClient defaultClient = Configuration.getDefaultApiClient();
+ClientConfig clientConfig = defaultClient.getClientConfig();
+clientConfig.connectorProvider(new ApacheConnectorProvider());
+clientConfig.property(ClientProperties.PROXY_URI, "http://proxy_url_here");
+clientConfig.property(ClientProperties.PROXY_USERNAME, "proxy_username");
+clientConfig.property(ClientProperties.PROXY_PASSWORD, "proxy_password");
+defaultClient.setClientConfig(clientConfig);
+
+{{{classname}}} apiInstance = new {{{classname}}}(defaultClient);
+{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
+```
+
+{{/jersey2}}
+## Getting Started
+
+Please follow the [installation](#installation) instruction and execute the following Java code:
+
+```java
+{{#apiInfo}}{{#apis}}{{#-first}}{{#operations}}{{#operation}}{{#-first}}
+import {{{invokerPackage}}}.*;
+import {{{invokerPackage}}}.auth.*;
+import {{{modelPackage}}}.*;
+import {{{package}}}.{{{classname}}};
+
+public class {{{classname}}}Example {
+
+ public static void main(String[] args) {
+ ApiClient defaultClient = Configuration.getDefaultApiClient();
+ defaultClient.setBasePath("{{{basePath}}}");
+ {{#hasAuthMethods}}{{#authMethods}}{{#isBasic}}{{#isBasicBasic}}
+ // Configure HTTP basic authorization: {{{name}}}
+ HttpBasicAuth {{{name}}} = (HttpBasicAuth) defaultClient.getAuthentication("{{{name}}}");
+ {{{name}}}.setUsername("YOUR USERNAME");
+ {{{name}}}.setPassword("YOUR PASSWORD");{{/isBasicBasic}}{{#isBasicBearer}}
+ // Configure HTTP bearer authorization: {{{name}}}
+ HttpBearerAuth {{{name}}} = (HttpBearerAuth) defaultClient.getAuthentication("{{{name}}}");
+ {{{name}}}.setBearerToken("BEARER TOKEN");{{/isBasicBearer}}{{/isBasic}}{{#isApiKey}}
+ // Configure API key authorization: {{{name}}}
+ ApiKeyAuth {{{name}}} = (ApiKeyAuth) defaultClient.getAuthentication("{{{name}}}");
+ {{{name}}}.setApiKey("YOUR API KEY");
+ // Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to null)
+ //{{{name}}}.setApiKeyPrefix("Token");{{/isApiKey}}{{#isOAuth}}
+ // Configure OAuth2 access token for authorization: {{{name}}}
+ OAuth {{{name}}} = (OAuth) defaultClient.getAuthentication("{{{name}}}");
+ {{{name}}}.setAccessToken("YOUR ACCESS TOKEN");{{/isOAuth}}{{#isHttpSignature}}
+ // Configure HTTP signature authorization: {{{name}}}
+ HttpSignatureAuth {{{name}}} = (HttpSignatureAuth) defaultClient.getAuthentication("{{{name}}}");
+ // All the HTTP signature parameters below should be customized to your environment.
+ // Configure the keyId
+ {{{name}}}.setKeyId("YOUR KEY ID");
+ // Configure the signature algorithm
+ {{{name}}}.setSigningAlgorithm(SigningAlgorithm.HS2019);
+ // Configure the specific cryptographic algorithm
+ {{{name}}}.setAlgorithm(Algorithm.ECDSA_SHA256);
+ // Configure the cryptographic algorithm parameters, if applicable
+ {{{name}}}.setAlgorithmParameterSpec(null);
+ // Set the cryptographic digest algorithm.
+ {{{name}}}.setDigestAlgorithm("SHA-256");
+ // Set the HTTP headers that should be included in the HTTP signature.
+ {{{name}}}.setHeaders(Arrays.asList("date", "host"));
+ // Set the private key used to sign the HTTP messages
+ {{{name}}}.setPrivateKey();{{/isHttpSignature}}
+ {{/authMethods}}
+ {{/hasAuthMethods}}
+
+ {{{classname}}} apiInstance = new {{{classname}}}(defaultClient);
+ {{#allParams}}
+ {{{dataType}}} {{{paramName}}} = {{{example}}}; // {{{dataType}}} | {{{description}}}
+ {{/allParams}}
+ try {
+ {{#returnType}}{{{.}}} result = {{/returnType}}apiInstance.{{{operationId}}}({{#allParams}}{{{paramName}}}{{^-last}}, {{/-last}}{{/allParams}});{{#returnType}}
+ System.out.println(result);{{/returnType}}
+ } catch (ApiException e) {
+ System.err.println("Exception when calling {{{classname}}}#{{{operationId}}}");
+ System.err.println("Status code: " + e.getCode());
+ System.err.println("Reason: " + e.getResponseBody());
+ System.err.println("Response headers: " + e.getResponseHeaders());
+ e.printStackTrace();
+ }
+ }
+}
+{{/-first}}{{/operation}}{{/operations}}{{/-first}}{{/apis}}{{/apiInfo}}
+```
+
+## Recommendation
+
+It's recommended to create an instance of `ApiClient` per thread in a multithreaded environment to avoid any potential issues.