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] Add generation of assertions from IDL dependencies (#8722) #10131

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

enriquebarba97
Copy link

@enriquebarba97 enriquebarba97 commented Aug 11, 2021

This pull request adds the automatic generation of assertions for inter-parameter dependencies using IDL in some Java generators. You can find full context and details in the corresponding issue #8722.

The dependencies are defined in an x-dependencies vendor extension in each operation. As an example, here is a Youtube API operation with its dependencies:

/youtube/v3/commentThreads:
    get:
      description: Retrieves a list of resources, possibly filtered.
      operationId: youtube.commentThreads.list
      parameters:
        - description: The *part* parameter specifies a comma-separated list of one or more commentThread resource properties that the API response will include.
          explode: true
          in: query
          name: part
          required: true
          schema:
            items:
              type: string
            type: array
          style: form
        - description: Returns the comment threads of all videos of the channel and the channel comments as well.
          in: query
          name: allThreadsRelatedToChannelId
          schema:
            type: string
        - description: Returns the comment threads for all the channel comments (ie does not include comments left on videos).
          in: query
          name: channelId
          schema:
            type: string
        - description: Returns the comment threads with the given IDs for Stubby or Apiary.
          explode: true
          in: query
          name: id
          schema:
            items:
              type: string
            type: array
          style: form
        - description: The *maxResults* parameter specifies the maximum number of items that should be returned in the result set.
          in: query
          name: maxResults
          schema:
            maximum: 100
            minimum: 1
            type: integer
        - description: "Limits the returned comment threads to those with the specified moderation status. Not compatible with the 'id' filter. Valid values: published, heldForReview, likelySpam."
          in: query
          name: moderationStatus
          schema:
            enum:
              - published
              - heldForReview
              - likelySpam
              - rejected
            type: string
        - in: query
          name: order
          schema:
            enum:
              - orderUnspecified
              - time
              - relevance
            type: string
        - description: The *pageToken* parameter identifies a specific page in the result set that should be returned. In an API response, the nextPageToken and prevPageToken properties identify other pages that could be retrieved.
          in: query
          name: pageToken
          schema:
            type: string
        - description: Limits the returned comment threads to those matching the specified key words. Not compatible with the 'id' filter.
          in: query
          name: searchTerms
          schema:
            type: string
        - description: The requested text format for the returned comments.
          in: query
          name: textFormat
          schema:
            enum:
              - textFormatUnspecified
              - html
              - plainText
            type: string
        - description: Returns the comment threads of the specified video.
          in: query
          name: videoId
          schema:
            type: string
      x-dependencies:
        - OnlyOne(allThreadsRelatedToChannelId, channelId, id, videoId);
        - ZeroOrOne(id, maxResults);
        - ZeroOrOne(id, moderationStatus);
        - ZeroOrOne(id, order);
        - ZeroOrOne(id, pageToken);
        - ZeroOrOne(id, searchTerms);
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CommentThreadListResponse"
          description: Successful response
        "400":
          description: 400
        "403":
          description: 403
        "404":
          description: 404
      security:
        - Oauth2:
            - https://www.googleapis.com/auth/youtube.force-ssl
          Oauth2c:
            - https://www.googleapis.com/auth/youtube.force-ssl
      tags:
        - commentThreads

And this is how the generated assertions look like in a Spring server stub.

// Check dependency: OnlyOne(allThreadsRelatedToChannelId, channelId, id, videoId);
if(!DependencyUtil.OnlyOneDependency((allThreadsRelatedToChannelId != null),(channelId != null),(id != null && !id.isEmpty()),(videoId != null))){
    return new ResponseEntity("Dependency not satisfied: OnlyOne(allThreadsRelatedToChannelId, channelId, id, videoId);", HttpStatus.BAD_REQUEST);
}
        
// Check dependency: ZeroOrOne(id, maxResults);
if(!DependencyUtil.ZeroOrOneDependency((id != null && !id.isEmpty()),(maxResults != null))){
    return new ResponseEntity("Dependency not satisfied: ZeroOrOne(id, maxResults);", HttpStatus.BAD_REQUEST);
}
        
// Check dependency: ZeroOrOne(id, moderationStatus);
if(!DependencyUtil.ZeroOrOneDependency((id != null && !id.isEmpty()),(moderationStatus != null))){
    return new ResponseEntity("Dependency not satisfied: ZeroOrOne(id, moderationStatus);", HttpStatus.BAD_REQUEST);
}
        
// Check dependency: ZeroOrOne(id, order);
if(!DependencyUtil.ZeroOrOneDependency((id != null && !id.isEmpty()),(order != null))){
    return new ResponseEntity("Dependency not satisfied: ZeroOrOne(id, order);", HttpStatus.BAD_REQUEST);
}
        
// Check dependency: ZeroOrOne(id, pageToken);
if(!DependencyUtil.ZeroOrOneDependency((id != null && !id.isEmpty()),(pageToken != null))){
    return new ResponseEntity("Dependency not satisfied: ZeroOrOne(id, pageToken);", HttpStatus.BAD_REQUEST);
}
        
// Check dependency: ZeroOrOne(id, searchTerms);
if(!DependencyUtil.ZeroOrOneDependency((id != null && !id.isEmpty()),(searchTerms != null))){
    return new ResponseEntity("Dependency not satisfied: ZeroOrOne(id, searchTerms);", HttpStatus.BAD_REQUEST);
}

To check the dependencies, a DependenciesUtil.java file with static methods is generated. The code checks for the x-dependencies extensions in the preprocessOpenAPI method, and adds the supporting file if found, so the DependenciesUtil.java file is not generated if IDL is not used. I am not convinced that is the best way of doing this, so any suggestions are welcome.

The generators supported right now are the following Java client library:

  • google-api-client
  • jersey2
  • native
  • okhttp-gson
  • resteasy
  • resttemplate
  • webclient

And the following servers:

  • Spring
  • java-msf4j-server

To check the files and expressions generated, you can use the youtubeIDL.yaml OAS specification with dependencies for the Youtube API. More OAS files for other APIs with IDL dependencies can be found here

Any feedback or suggestions are appreciated.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package 
    ./bin/generate-samples.sh
    ./bin/utils/export_docs_generators.sh
    
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    For Windows users, please run the script in Git BASH.
  • File the PR against the correct branch: master, 5.3.x, 6.0.x
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

@bbdouglas @sreeshas @jfiala @lukoyanov @cbornet @jeff9finger @karismann @Zomzog @lwlee2608 @nmuesch @wing328

enriquebarba97 and others added 8 commits July 18, 2021 16:45
…ls#8722)

Added experimental generation of assertions for dependencies expressed in IDL.
Supported Java clients and servers:

- google-api-client
- jersey2
- native
- okhttp-gson
- resteasy
- resttemplate
- webclient

- Spring
- java-msf4j-server
Changed predefined dependencies to calls to static methods. Missing the possibility to add those static methods during generation
Added static methods for dependency utils for Java Clients.
@borsch
Copy link
Member

borsch commented Aug 17, 2021

IMHO: might be better to do

if (DependencyUtil.doNotSatisfyZeroOrOneDependency(..)) rather than always add ! for each dependency check

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

Successfully merging this pull request may close these issues.

2 participants