Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java Maven Feign x-www-form-urlencoded codegen broken #4908

Open
5 of 6 tasks
tonymurphy opened this issue Jan 2, 2020 · 4 comments
Open
5 of 6 tasks

Java Maven Feign x-www-form-urlencoded codegen broken #4908

tonymurphy opened this issue Jan 2, 2020 · 4 comments

Comments

@tonymurphy
Copy link

tonymurphy commented Jan 2, 2020

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • What's the version of OpenAPI Generator used?
  • Have you search for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Bounty to sponsor the fix (example)
Description

Maven Generator generates an invalid Feign interface when form encoding required..

openapi-generator version

from pom.xml

    <properties>
        <logback.version>1.2.3</logback.version>
        <slf4j.version>1.7.26</slf4j.version>

        <feign.version>10.2.3</feign.version>
        <feign.form.version>3.8.0</feign.form.version>
        <jackson.version>2.9.5</jackson.version>
        <okhttp3.version>3.11.0</okhttp3.version>
        <oltu-version>1.0.0</oltu-version>
        <okio.version>2.1.0</okio.version>

    </properties>

    <dependencies>

        <!-- Logging dependencies -->

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <!-- Feign dependencies -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
             <version>${feign.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-hystrix</artifactId>
             <version>${feign.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-slf4j</artifactId>
             <version>${feign.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-jackson</artifactId>
             <version>${feign.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
             <version>${feign.version}</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign.form</groupId>
            <artifactId>feign-form</artifactId>
             <version>${feign.form.version}</version>
        </dependency>

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>${okhttp3.version}</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okio</groupId>
            <artifactId>okio</artifactId>
            <version>${okio.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.oltu.oauth2</groupId>
            <artifactId>org.apache.oltu.oauth2.client</artifactId>
            <version>${oltu-version}</version>
        </dependency>

        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.openapitools</groupId>
            <artifactId>jackson-databind-nullable</artifactId>
            <version>0.2.1</version>
        </dependency>

        <!-- Jackson dependencies -->

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jdk8</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
           <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.6.0</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.5.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.5.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>
<build>
<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>4.2.2</version>
    <executions>
        <execution>
            <id>oidc-api-contract</id>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${project.build.directory}/contracts/oidc-api.yaml</inputSpec>
                <generatorName>java</generatorName>
                <configOptions>
                    <java8>true</java8>
                    <dateLibrary>java8</dateLibrary>
                    <library>feign</library>
                </configOptions>
                <templateDirectory>src/main/resources/Java</templateDirectory>
                <output>${project.build.directory}/generated-sources</output>
                <apiPackage>com.demo.oidc.api</apiPackage>
                <modelPackage>com.demo.oidc.model</modelPackage>
                <supportingFilesToGenerate>ApiClient.java,HttpBasicAuth.java,ApiKeyAuth.java,HttpBearerAuth.java,StringUtil.java,RFC3339DateFormat.java,EncodingUtils.java</supportingFilesToGenerate>
                <generateApis>true</generateApis>
                <generateApiTests>false</generateApiTests>
                <generateModelTests>false</generateModelTests>
                <invokerPackage>com.demo.oidc.api</invokerPackage>
                <httpUserAgent>oidc-generated-client</httpUserAgent>
                <library>feign</library>
                <additionalProperties>
                    <additionalProperty>jackson</additionalProperty>
                </additionalProperties>
            </configuration>
        </execution>
    </executions>
</plugin>
</build>
OpenAPI declaration file content or url
info:
  title: Keycloak Open ID Connect API
  version: '1.0'
  description: Open API Definition for Authentication
tags:
  - name: 'Open ID Connect'
paths:
  /auth/realms/demo/protocol/openid-connect/token:
    post:
      summary: Direct Access Grant
      description: 'https://tools.ietf.org/html/rfc6749#section-10.7'
      tags:
        - 'Open ID Connect'
      operationId: directAccessGrantCode
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/AccessTokenRequest'
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessTokenResponse'
        '401':
          description: Not authenticated
        '403':
          description: Access denied
components:
  schemas:
    AccessTokenRequest:
      type: object
      properties:
        client_id:
          type: string
        client_secret:
          type: string
        grant_type:
          type: string
        scope:
          type: string
        username:
          type: string
        password:
          type: string
      required:
        - client_id
        - client_secret
        - grant_type
        - scope
        - username
        - password
    AccessTokenResponse:
      type: object
      properties:
        access_token:
          type: string
        expires_in:
          type: integer
        refresh_expires_in:
          type: integer
        refresh_token:
          type: string
        token_type:
          type: string
        id_token:
          type: string
        not-before-policy:
          type: integer
          format: int64
        session_state:
          type: string
        scope:
          type: string
Command line used for generation
mvn clean generate-sources
Steps to reproduce

OpenIdConnectApi.java -

There is a code generator console log message about ignoring form parameters - interface is generated with no method parameters

    /**
    * Direct Access Grant
    * https://tools.ietf.org/html/rfc6749#section-10.7
            * @param clientId  (required)
            * @param clientSecret  (required)
            * @param grantType  (required)
            * @param scope  (required)
            * @param username  (required)
            * @param password  (required)
        * @return AccessTokenResponse
    */
    @RequestLine("POST /auth/realms/gumtree/protocol/openid-connect/token")
    @Headers({
    "Content-Type: application/x-www-form-urlencoded",
    "Accept: application/json"
    })
    Single<AccessTokenResponse> directAccessGrantCode();
Related issues/PRs

Seems related to..

#50

Suggest a fix
     * Direct Access Grant
     * https://tools.ietf.org/html/rfc6749#section-10.7
     *
     * @param accessTokenRequest (required)
     * @return AccessTokenResponse
     */
    @RequestLine("POST /auth/realms/demo/protocol/openid-connect/token")
    @Headers({
            "Content-Type: application/x-www-form-urlencoded",
            "Accept: application/json"
    })
   AccessTokenResponse directAccessGrantCode(AccessTokenRequest accessTokenRequest);
@iamlothian
Copy link

iamlothian commented Apr 23, 2020

I have a similar issue, version feign-*-10.4.2 and feign-form-3.8.0

Params are included but they are camelCase and not as documented.

the following openapi 3.0.1 operation

/difam/oauth2/realms/root/realms/{realm}/access_token:
  post:
    operationId: accessTokenRequest
    tags:
        - openAM
    parameters:
      - $ref: '#/components/parameters/realm'
    requestBody:
      required: true
      content:
        application/x-www-form-urlencoded:
          schema:
            $ref: '#/components/schemas/accessTokenRequest'
    responses:
      200:
        description: access token response
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/tokenResponse'

components:

  parameters:

    realm:
      name: realm
      in: path
      description: the realm the request should use
      required: true
      schema:
        type: string

  schemas:

    grantType:
      type: string
      enum:
        - client_credentials # for the Client Credentials grant flow.

    accessTokenRequest:
      title: accessTokenRequest
      type: object
      required:
        - client_id
        - client_secret
        - grant_type
      properties:
        client_id:
          type: string
        client_secret:
          type: string
        grant_type:
          $ref: '#/components/schemas/grantType'
        scope:
          type: string
 
    tokenResponse:
      type: object
      properties:
        access_token:
          type: string
        scopes:
          type: string
        token_type:
          type: string
        expires_in:
          description: how many seconds till the token expires
          type: integer

results in the following generated code:

  /**
   * 
   * 
   * @param realm the realm the request should use (required)
   * @param clientId  (required)
   * @param clientSecret  (required)
   * @param grantType  (required)
   * @param scope  (optional)
   * @return TokenResponse
   */
  @RequestLine("POST /difam/oauth2/realms/root/realms/{realm}/access_token")
  @Headers({
    "Content-Type: application/x-www-form-urlencoded",
    "Accept: application/json",
  })
  TokenResponse accessTokenRequest(@Param("realm") String realm, @Param("clientId") String clientId, @Param("clientSecret") String clientSecret, @Param("grantType") GrantType grantType, @Param("scope") String scope);

documentation for reference: https://swagger.io/docs/specification/describing-request-body/

expected:

POST /difam/oauth2/realms/root/realms/DSP/access_token HTTP/1.1
Host: 
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=dsp-service-client&client_secret=DspSecret&scope=ReadWrite.All:Dsp.SecurityCtx.Users

actual

POST /difam/oauth2/realms/root/realms/DSP/access_token HTTP/1.1
Host: 
Content-Type: application/x-www-form-urlencoded

grantType=client_credentials&clientId=dsp-service-client&clientSecret=DspSecret&scope=ReadWrite.All:Dsp.SecurityCtx.Users

@iamlothian
Copy link

Using the following results in no body being sent at all

<properties>
    <feign-version>11.0</feign-version>
    <feign-form-version>3.8.0</feign-form-version>
</properties>
<dependencies>
    <!-- HTTP client: Netflix Feign -->
    <dependency>
        <groupId>org.apache.oltu.oauth2</groupId>
        <artifactId>org.apache.oltu.oauth2.client</artifactId>
        <version>1.0.2</version>
    </dependency>
    <!-- <dependency>
			<groupId>io.github.openfeign</groupId>
			<artifactId>feign-java8</artifactId>
			<version>${feign-version}</version>
		</dependency> -->
    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-core</artifactId>
        <version>${feign-version}</version>
    </dependency>
    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-jackson</artifactId>
        <version>${feign-version}</version>
    </dependency>
    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-slf4j</artifactId>
        <version>${feign-version}</version>
    </dependency>
    <dependency>
        <groupId>io.github.openfeign.form</groupId>
        <artifactId>feign-form</artifactId>
        <version>${feign-form-version}</version>
    </dependency>
    <dependency>
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-okhttp</artifactId>
        <version>${feign-version}</version>
    </dependency>
    <dependency>
        <!-- Required to use PATCH -->
        <groupId>io.github.openfeign</groupId>
        <artifactId>feign-httpclient</artifactId>
        <version>${feign-version}</version>
    </dependency>
</dependencies>

I also tried having the template use @Param("{{paramName}}") rather than @Param("{{baseName}}" but this also sent no body

@wing328
Copy link
Member

wing328 commented Apr 23, 2020

I assume you're using the latest version (4.3.0)

What does the correct code look like? if you've fixed the code locally and confirm it works in your case, we can plot the fix back into the template.

@iamlothian
Copy link

iamlothian commented Apr 23, 2020

Looks like openapi-codegen-version was 4.1.1 missed updating that one. let me try with that.
-- EDIT 1:
This seems to have resolved the issue for me. Thanks.
-- EDIT 2:
Scratch that, now client endpoints that were working now fail... headers are being sent in the body by feign. I'll raise a separate issue for that with a reproducible scenario.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants