Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions bin/configs/spring-http-interface-oauth.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
generatorName: spring
library: spring-http-interface
outputDir: samples/client/petstore/spring-http-interface-oauth
inputSpec: modules/openapi-generator/src/test/resources/3_0/spring/petstore-with-fake-endpoints-models-for-testing.yaml
templateDir: modules/openapi-generator/src/main/resources/JavaSpring
additionalProperties:
artifactId: spring-http-interface-oauth
snapshotVersion: "true"
hideGenerationTimestamp: "true"
modelNameSuffix: 'Dto'
generatedConstructorWithRequiredArgs: "false"
clientRegistrationId: "petstore-oauth"
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public class SpringCodegen extends AbstractJavaCodegen
public static final String USE_SPRING_BUILT_IN_VALIDATION = "useSpringBuiltInValidation";
public static final String USE_DEDUCTION_FOR_ONE_OF_INTERFACES = "useDeductionForOneOfInterfaces";
public static final String SPRING_API_VERSION = "springApiVersion";
public static final String CLIENT_REGISTRATION_ID = "clientRegistrationId";

@Getter
public enum RequestMappingMode {
Expand Down Expand Up @@ -163,6 +164,8 @@ public enum RequestMappingMode {
protected boolean useSpringBuiltInValidation = false;
@Getter @Setter
protected boolean useDeductionForOneOfInterfaces = false;
@Getter @Setter
protected String clientRegistrationId = null;

public SpringCodegen() {
super();
Expand Down Expand Up @@ -288,6 +291,7 @@ public SpringCodegen() {

cliOptions.add(CliOption.newBoolean(USE_DEDUCTION_FOR_ONE_OF_INTERFACES, "whether to use deduction for generated oneOf interfaces", useDeductionForOneOfInterfaces));
cliOptions.add(CliOption.newString(SPRING_API_VERSION, "Value for 'version' attribute in @RequestMapping (for Spring 7 and above)."));
cliOptions.add(CliOption.newString(CLIENT_REGISTRATION_ID, "Client registration ID for OAuth2 in Spring HTTP Interface (@ClientRegistrationId annotation)."));
supportedLibraries.put(SPRING_BOOT, "Spring-boot Server application.");
supportedLibraries.put(SPRING_CLOUD_LIBRARY,
"Spring-Cloud-Feign client with Spring-Boot auto-configured settings.");
Expand Down Expand Up @@ -456,6 +460,7 @@ public void processOpts() {
convertPropertyToBooleanAndWriteBack(OPTIONAL_ACCEPT_NULLABLE, this::setOptionalAcceptNullable);
convertPropertyToBooleanAndWriteBack(USE_SPRING_BUILT_IN_VALIDATION, this::setUseSpringBuiltInValidation);
convertPropertyToBooleanAndWriteBack(USE_DEDUCTION_FOR_ONE_OF_INTERFACES, this::setUseDeductionForOneOfInterfaces);
convertPropertyToStringAndWriteBack(CLIENT_REGISTRATION_ID, this::setClientRegistrationId);

additionalProperties.put("springHttpStatus", new SpringHttpStatusLambda());

Expand Down Expand Up @@ -803,6 +808,11 @@ public void setIsVoid(boolean isVoid) {
// But use a sensible tag name if there is none
objs.put("tagName", "default".equals(firstTagName) ? firstOperation.baseName : firstTagName);
objs.put("tagDescription", escapeText(firstTag.getDescription()));

// Add clientRegistrationId for spring-http-interface with OAuth
if (SPRING_HTTP_INTERFACE.equals(library) && clientRegistrationId != null && !clientRegistrationId.isEmpty()) {
operations.put("clientRegistrationId", clientRegistrationId);
}
}

removeImport(objs, "java.util.List");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import org.springframework.http.ResponseEntity;
{{/useResponseEntity}}
import org.springframework.web.bind.annotation.*;
import org.springframework.web.service.annotation.*;
{{#operations}}{{#clientRegistrationId}}
import org.springframework.security.oauth2.client.annotation.ClientRegistrationId;
{{/clientRegistrationId}}{{/operations}}
import org.springframework.web.multipart.MultipartFile;
{{#reactive}}

Expand All @@ -32,6 +35,9 @@ import {{javaxPackage}}.annotation.Generated;
{{>generatedAnnotation}}

{{#operations}}
{{#clientRegistrationId}}
@ClientRegistrationId("{{clientRegistrationId}}")
{{/clientRegistrationId}}
public interface {{classname}} {
{{#operation}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6223,4 +6223,31 @@ public void testExtensionsOnSchema_issue9183() throws IOException {
));
}

@Test
public void testClientRegistrationIdAnnotation() throws IOException {
final SpringCodegen codegen = new SpringCodegen();
codegen.setLibrary("spring-http-interface");
codegen.setClientRegistrationId("my-oauth-client");

final Map<String, File> files = generateFiles(codegen, "src/test/resources/3_0/petstore.yaml");

// Check that the @ClientRegistrationId annotation is generated at class level
JavaFileAssert.assertThat(files.get("PetApi.java"))
.hasImports("org.springframework.security.oauth2.client.annotation.ClientRegistrationId")
.assertTypeAnnotations()
.containsWithNameAndAttributes("ClientRegistrationId", ImmutableMap.of("value", "\"my-oauth-client\""));
}

@Test
public void testClientRegistrationIdAnnotationNotPresentWhenNotConfigured() throws IOException {
final SpringCodegen codegen = new SpringCodegen();
codegen.setLibrary("spring-http-interface");
// clientRegistrationId not set

final Map<String, File> files = generateFiles(codegen, "src/test/resources/3_0/petstore.yaml");

// Check that the @ClientRegistrationId annotation is NOT generated
assertFileNotContains(files.get("PetApi.java").toPath(), "@ClientRegistrationId", "ClientRegistrationId");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator

# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.

# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs

# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux

# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux

# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.openapi-generator-ignore
README.md
pom.xml
src/main/java/org/openapitools/api/AnotherFakeApi.java
src/main/java/org/openapitools/api/FakeApi.java
src/main/java/org/openapitools/api/FakeClassnameTags123Api.java
src/main/java/org/openapitools/api/PetApi.java
src/main/java/org/openapitools/api/StoreApi.java
src/main/java/org/openapitools/api/UserApi.java
src/main/java/org/openapitools/configuration/HttpInterfacesAbstractConfigurator.java
src/main/java/org/openapitools/model/AdditionalPropertiesAnyTypeDto.java
src/main/java/org/openapitools/model/AdditionalPropertiesArrayDto.java
src/main/java/org/openapitools/model/AdditionalPropertiesBooleanDto.java
src/main/java/org/openapitools/model/AdditionalPropertiesClassDto.java
src/main/java/org/openapitools/model/AdditionalPropertiesIntegerDto.java
src/main/java/org/openapitools/model/AdditionalPropertiesNumberDto.java
src/main/java/org/openapitools/model/AdditionalPropertiesObjectDto.java
src/main/java/org/openapitools/model/AdditionalPropertiesStringDto.java
src/main/java/org/openapitools/model/AnimalDto.java
src/main/java/org/openapitools/model/ApiResponseDto.java
src/main/java/org/openapitools/model/ArrayOfArrayOfNumberOnlyDto.java
src/main/java/org/openapitools/model/ArrayOfNumberOnlyDto.java
src/main/java/org/openapitools/model/ArrayTestDto.java
src/main/java/org/openapitools/model/BigCatDto.java
src/main/java/org/openapitools/model/CapitalizationDto.java
src/main/java/org/openapitools/model/CatDto.java
src/main/java/org/openapitools/model/CategoryDto.java
src/main/java/org/openapitools/model/ChildWithNullableDto.java
src/main/java/org/openapitools/model/ClassModelDto.java
src/main/java/org/openapitools/model/ClientDto.java
src/main/java/org/openapitools/model/ContainerDefaultValueDto.java
src/main/java/org/openapitools/model/DogDto.java
src/main/java/org/openapitools/model/EnumArraysDto.java
src/main/java/org/openapitools/model/EnumClassDto.java
src/main/java/org/openapitools/model/EnumTestDto.java
src/main/java/org/openapitools/model/FileDto.java
src/main/java/org/openapitools/model/FileSchemaTestClassDto.java
src/main/java/org/openapitools/model/FormatTestDto.java
src/main/java/org/openapitools/model/HasOnlyReadOnlyDto.java
src/main/java/org/openapitools/model/ListDto.java
src/main/java/org/openapitools/model/MapTestDto.java
src/main/java/org/openapitools/model/MixedPropertiesAndAdditionalPropertiesClassDto.java
src/main/java/org/openapitools/model/Model200ResponseDto.java
src/main/java/org/openapitools/model/NameDto.java
src/main/java/org/openapitools/model/NullableMapPropertyDto.java
src/main/java/org/openapitools/model/NumberOnlyDto.java
src/main/java/org/openapitools/model/OrderDto.java
src/main/java/org/openapitools/model/OuterCompositeDto.java
src/main/java/org/openapitools/model/OuterEnumDto.java
src/main/java/org/openapitools/model/ParentWithNullableDto.java
src/main/java/org/openapitools/model/PetDto.java
src/main/java/org/openapitools/model/ReadOnlyFirstDto.java
src/main/java/org/openapitools/model/ResponseObjectWithDifferentFieldNamesDto.java
src/main/java/org/openapitools/model/ReturnDto.java
src/main/java/org/openapitools/model/SpecialModelNameDto.java
src/main/java/org/openapitools/model/TagDto.java
src/main/java/org/openapitools/model/TypeHolderDefaultDto.java
src/main/java/org/openapitools/model/TypeHolderExampleDto.java
src/main/java/org/openapitools/model/UserDto.java
src/main/java/org/openapitools/model/XmlItemDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.19.0-SNAPSHOT
97 changes: 97 additions & 0 deletions samples/client/petstore/spring-http-interface-oauth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Spring HTTP Interface with OAuth2 (@ClientRegistrationId)

This sample demonstrates the use of the `@ClientRegistrationId` annotation with Spring HTTP Interface clients.

## Overview

This code was generated by the [OpenAPI Generator](https://openapi-generator.tech) project.
When generating Spring HTTP Interface clients, you can specify a `clientRegistrationId` parameter to automatically add the `@ClientRegistrationId` annotation to all generated API interfaces.

## Configuration

Add the `clientRegistrationId` property to your generator configuration:

```yaml
generatorName: spring
library: spring-http-interface
additionalProperties:
clientRegistrationId: "petstore-oauth"
```

Or via command line:

```bash
openapi-generator-cli generate \
-g spring \
--library spring-http-interface \
--additional-properties clientRegistrationId=petstore-oauth \
-i petstore.yaml \
-o ./output
```

## Generated Code

The generated interface will include the `@ClientRegistrationId` annotation at the class level:

```java
@ClientRegistrationId("petstore-oauth")
public interface PetApi {

@HttpExchange(
method = "GET",
value = "/pet/{petId}",
accept = { "application/json" }
)
ResponseEntity<PetDto> getPetById(@PathVariable("petId") Long petId);
}
```

## Spring Security Integration

This annotation is part of Spring Security's OAuth2 integration for HTTP Service Clients. It automatically associates OAuth2 tokens with HTTP requests.

### Requirements

- Spring Boot 3.5+
- Spring Security 6.5+

### Application Properties

Configure your Spring application with the OAuth2 client registration:

```yaml
spring:
security:
oauth2:
client:
registration:
petstore-oauth:
client-id: your-client-id
client-secret: your-client-secret
authorization-grant-type: client_credentials
scope: read,write
provider:
petstore-oauth:
token-uri: https://auth.example.com/oauth/token
```

### Bean Configuration

Use `RestClientHttpServiceGroupConfigurer` to configure the HTTP Service Proxy Factory:

```java
@Configuration
public class HttpInterfaceConfig extends HttpInterfacesAbstractConfigurator {

public HttpInterfaceConfig() {
super(RestClient.builder()
Copy link

@cubic-dev-ai cubic-dev-ai bot Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Bean configuration example builds a bare RestClient and passes it to HttpInterfacesAbstractConfigurator, which takes a WebClient and applies no OAuth2 configurer—clients will lack OAuth2 support and the snippet won’t compile.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At samples/client/petstore/spring-http-interface-oauth/README.md, line 87:

<comment>Bean configuration example builds a bare RestClient and passes it to HttpInterfacesAbstractConfigurator, which takes a WebClient and applies no OAuth2 configurer—clients will lack OAuth2 support and the snippet won’t compile.</comment>

<file context>
@@ -78,31 +77,21 @@ spring:
 
-        return factory.createClient(PetApi.class);
+    public HttpInterfaceConfig() {
+        super(RestClient.builder()
+            .baseUrl("https://petstore.example.com/v2")
+            .build());
</file context>
Fix with Cubic

.baseUrl("https://petstore.example.com/v2")
.build());
}
}
```

## References

- [Spring Security HTTP Service Client Integration](https://docs.spring.io/spring-security/reference/servlet/oauth2/client/http-service-client.html)
- [Spring Framework HTTP Interface](https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface)
80 changes: 80 additions & 0 deletions samples/client/petstore/spring-http-interface-oauth/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.openapitools</groupId>
<artifactId>spring-http-interface-oauth</artifactId>
<packaging>jar</packaging>
<name>spring-http-interface-oauth</name>
<version>1.0.0-SNAPSHOT</version>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- @Nullable annotation -->
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>0.2.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Loading