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

Improve Doing API First Development documentation #27215

Open
1 task done
timothystone opened this issue Sep 9, 2024 · 8 comments
Open
1 task done

Improve Doing API First Development documentation #27215

timothystone opened this issue Sep 9, 2024 · 8 comments
Labels
area: triage $$ bug-bounty $$ https://www.jhipster.tech/bug-bounties/ theme: undefined $100 https://www.jhipster.tech/bug-bounties/

Comments

@timothystone
Copy link
Contributor

Overview of the feature request

The current PR for this documentation aims to improve the existing guide from the current starting point.

However, in working with the core JHipster team during #1335 and in my own professional work with JHipster microservices with a Contract First approach ("API First") and the provided tooling of the OpenAPI Maven Generator Plugin, I have found that there are some additional improvements to the guide for an end-to-end API First approach.

This issue (and accompanying PR) intends to track the end-to-end documentation of API-First development with JHipster and fully leveraging both the current JHipster default of the Delegate Pattern and the implementation of the delegates with Mapstruct.

Motivation for or Use Case

IMHO, Contract First/API First development, has a very specific meaning: one starts with the Open API Specification and defers as much work to the tooling as possible, providing implementation details. For example, delegate implementations (using the delegate pattern).

However, as written, the documentation only provides a guide to the implementation of the delegates and provides no guidance on JHipster default behaviors or managing the same. For example:

If you choose "API First ..." in project generation, the project gets a Swagger Docker Compose file, some default settings applied to the project, and an Open API Specification with a single schema for Problem. The documentation currently provides no guidance on the use of DTOs and Entities. More importantly JHipster's default behaviors and configuration work against the API First developer, leaving elements of "cruft." For example: generating entities with JDL, during JHipster initiation (or after) creates implementations that do not take advantage of the initial intent of API First, as default behaviors of JHipster are not "API First Aware."

This improvement will seek to provide documentation on:

  1. Creating of a JDL to "kickstart" the generator
  2. changes to template to set the DTO path of the generated models to the expected packaging defaults of JHipster
  3. Implementation of MapStruct
  4. Disabling of the default RestControllers, deferring to the generator delegates
  5. Examples of the delegate implementations
  6. Example Database with Relationships for a more complete e2e view of API First development

Related issues or PR

See PR 1335 (Issue #25030).

This issue and PR will supersede #25030 but should not delay the integration of existing PR, as that PR aims to improve the existing documentation.

  • Checking this box is mandatory (this is just to show you read everything)
@timothystone
Copy link
Contributor Author

As soon as I have a PR, I'll reference.

@timothystone
Copy link
Contributor Author

timothystone commented Sep 12, 2024

I'll track my notes in this issue for comment.

nb. I will use a mix of shell syntax for brevity; if anything is unclear, please quote reply.

My first deep dive into the code and the behaviors show that some of the defaults in JHipster are intentional., for example the <modelPackage> value of the openapi-generator-maven-plugin. It is defined as <package-name>.service.api.dto.

This "moves" the generator's DTOs "out of the way" of the default DTOs generated when using mapstruct in the JDL for the defined entities.

So for example...

application {
  config {
     ...
     enableSwaggerCodegen true
  }
  entities *
  use mapstruct for *
}

entity Foo {
  ...
}

will generate DTO and Mappers at <package.name>.service.{dto,mapper}.

This is acceptable, but you have to know what is being generated in contrast to the OpenAPI specification to leverage either. Thus no. 2 of the defined goals may be moot.

Moreover, JHipster will generate full implementations of @RestControllers for the entities while at the same time the default configuration of the generator employed, c.f. the plugin configuration:generatorName=spring. The spring generator is defaulted to configuration:configOptions:delegatePattern=true. This will generate entity controllers bound to identical HTTP methods based on the paths.

Following the guide as written means moving the JHipster controllers out of the way to employ the delegate implementations.

NB. The JHipster *Resource.java controllers are helpful to the novice user in guiding implementation of the service classes however.

More to come.

@mraible mraible added $100 https://www.jhipster.tech/bug-bounties/ $$ bug-bounty $$ https://www.jhipster.tech/bug-bounties/ labels Sep 13, 2024
@Tcharl
Copy link
Contributor

Tcharl commented Sep 15, 2024

Totally agrees.
Jhipster should ideally generate an -api.yml per endpoint, and rely on the openapi plugin to generate the controller (delegate mode) and dto.

Is there a use case where user don't need DTO? Could it be clarified before V9?

@timothystone
Copy link
Contributor Author

Is there a use case where user don't need DTO? Could it be clarified before V9?

No. The use of the annotation processors in the maven-complier-plugin for mapstruct indeed will generate the Mappers for the DTOs generated by the openapi-generator-maven-plugin.

Jhipster should ideally generate an -api.yml per endpoint, and rely on the openapi plugin to generate the controller (delegate mode) and dto.

Could you elaborate? I think you mean that "ideally JHipster would generate the same operation endpoints for the HTTP mappings, e.g., @GetMapping(String) in the src/main/resources/swagger/api.yml" that it is being generated by the plugin in the delegates. Is that correct?

Right now the api.yml only includes the Problem schema.

@aruvic
Copy link

aruvic commented Nov 11, 2024

Hi @timothystone @timothystone-knsl, if I'm not wrong you use both accounts?
I was researching and exploring the same topic and would like to contribute, as I need this functionality as well.

With this message, I aim to reactivate this thread and offer my help. However, we should agree on the approach to solving this.

  • Would one option be to process the OAS3 (OpenAPI spec file), parse the schemas from components section, and use object definitions to create JDL entities, utilizing OAS3 $ref for relationships?
  • Then, we could generate the JHipster app from the JDL file, skipping the Resource.java and JHipster's DTO generation, while allowing openapi-generator to generate the Controller, DTOs, and Delegates.
  • OpenAPI's DTO files would need a "DTO" suffix to be aligned with JHI's DTO file naming

At this stage, we would have JHipster-generated JPA entities, along with OpenAPI-generated DTOs, Controllers, and Delegates.

We would need a way for JHipster to generate the MapStruct mappers and service implementations (Delegate implementations). Since the JPA entities (JHipster) and DTOs (opeonapi-generator) originate from the same OAS3 spec, I think this could work.

ZenWave has this plugin "openapi-to-jdl": https://github.com/ZenWave360/zenwave-sdk#usage

Br,
Alen Ruvic

@timothystone
Copy link
Contributor Author

Alen,

Both accounts are fine, but the *-knsl account will be retired. I'm moving to a new role with a new company.

You've got all the details correct with my intention in the issue. I look forward to revisiting this. I had opened this tracking issue to highlighted these issues with the current "support."

@aruvic
Copy link

aruvic commented Nov 12, 2024

Hi @timothystone,

thanks for revisiting this again. As I said, let me know how I can help.

Do we have the capability in JHipster to parse OAS3x and convert OAS3x schemas to JDL entities and relationships? I assume this would require new code. The OAS 3.1 schema is fully aligned with JSON Schema 2020-12, so this would essentially mean converting JSON Schema to JDL entities and relationships.

Is following the intented outcome of the process:

  • domain -> JHipster (JPA entities based on the JDL file)
  • repository -> JHipster (repositories based on the JDL file)
  • service\api\dto -> openapi-generator (DTOs)
  • service\dto -> JHipster (nothing, ignore for entities originating in OAS3)
  • service\mapper -> JHipster (MapStruct mappers between service\api\dto\*.java and domain\*.java)
  • service\impl -> JHipster (Service implementation of openapi-generator generated delegate in web\api\*ApiDelegate.class)
  • web\api -> openapi-generator (interface, controller, delegate)
  • web\rest -> JHipster (nothing, ignore for entities originating in OAS3)

@aruvic
Copy link

aruvic commented Nov 18, 2024

Hi @timothystone,

Please see below my analysis. The problem is much bigger then only updating the documentation or changing a little bit the openapi plugin.

I found following discrepancies:

  • as MapStruct mappers are needed it's not possible to disable generation of DTO in service\dto, but DTO's are not needed as openapi-generator will generate them
  • it's not possible to disable generation of *Resourc.java in web\rest
  • JHipster and openapi-generator, handle types diffrently. Following types can't be generated out of the box with OAS3 + openapi-generator: BigDecimal, Instant, ZonedDateTime, Duration
    • I dont know how to disable this in openapi-generator: bigDecimalAsString (Treat BigDecimal values as Strings to avoid precision loss.)
  • Enumaration class is generated inline in the DTO and not in domain/enumeration by openapi-generator
  • Blob is generated as org.springframework.core.io.Resource without "private String _BLOBNAME_ContentType;"
  • openapi-generator is not generating Validations like @NotNull automaticaly and <useBeanValidation>true is ignored. I dont know why.
  • JHipster and OpenAPI Generator handle relationships in DTOs differently

JHipster

  • Parent-to-Child (One-to-Many):
    • Omitted from the parent DTO (e.g., AuthorDTO does NOT contain List).
  • Child-to-Parent (Many-to-One):
    • Included in the child DTO (e.g., BookDTO contains AuthorDTO).

Example (JHipster):

public class AuthorDTO {
    private Long id;
    private String name;
}

public class BookDTO {
    private Long id;
    private String title;
    private AuthorDTO author; // Reference to parent
}

OpenAPI Generator

  • Parent-to-Child (One-to-Many):
    • Included in the parent DTO (e.g., AuthorDTO contains List).
  • Child-to-Parent (Many-to-One):
    • Omitted from the child DTO (e.g., BookDTO does NOT contain AuthorDTO).
  • Example (OpenAPI Generator):
public class AuthorDTO {
    private Long id;
    private String name;
    @Valid
    private List<@Valid BookDTO> books = new ArrayList<>(); // Reference to children
}

public class BookDTO {
    private Long id;
    private String title;
}

openapi-generator maven plugin changes I did as well:

<apiPackage>pro.private5g.jhipster.web.rest</apiPackage
<modelPackage>pro.private5g.jhipster.service.dto</modelPackage>
<modelNameSuffix>DTO</modelNameSuffix>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: triage $$ bug-bounty $$ https://www.jhipster.tech/bug-bounties/ theme: undefined $100 https://www.jhipster.tech/bug-bounties/
Projects
None yet
Development

No branches or pull requests

4 participants