OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.95k stars 6.59k forks source link

[REQ] Support per-file template data for supporting files #15085

Open nebulon42 opened 1 year ago

nebulon42 commented 1 year ago

Is your feature request related to a problem? Please describe.

Encountered this need with the typescript-angular client generator (already extended by me) but in theory it is independent from the generated language. Use case: Want to output generated models in different directories ("domains") according to a vendor extension tag used in the specification file.

Standard generator outputs all models in one directory and generates a models.ts file (barrel file) through which the models are exported. If splitting up in multiple directories we need multiple barrel files which is possible by adding several SupportingFiles. Additionally, those files should only output the models which are in that directory and not all of them. So each SupportingFile needs its own list of models for that.

This is currently not possible because the bundle (from buildSupportFileBundle) only contains global data and it is not possible to pass individual data to a SupportingFile.

I have resorted to extending SupportingFile to be able to pass individual data to it and then adding that data to the bundle so that it is available in the template. However, that required me to extend DefaultGenerator which is problematic because plugins like the Gradle plugin invoke DefaultGenerator and have no capability to invoke other generators (I think).

Describe the solution you'd like

One solution would be if the possibility to pass individual data to a SupportingFile would be available in core. For that

Downside here is that somebody who wants to add data per SupportFile has to do so in the right place. That means after the needed data is available and before the templating occurs compared to just having a defined place where supporting files are added. However, it is also possible to define supporting files at the usual place and then modify them again when some data should be added.

Describe alternatives you've considered

If the above is undesirable other possibilities would be

Additional context

I might have missed other possibilities to solve this. Also ones that are able to achieve it without changing core. I'm happy to provide PRs for any of the described solutions.

itineric commented 11 months ago

Have you found some workaround ? I have the exactly same usecase as yours.

nebulon42 commented 11 months ago

Not a workaround, but in https://github.com/Eurofunk/openapi-generator/commit/ee1bc8ffff7ccf200393eb719736a0fb2dfe6e30 I have implemented an extension to the Gradle plugin to be able to add your own class that implements the Generator interface. With that you then can implement a Generator that accepts per-file template data.

itineric commented 11 months ago

I did not find a better way either. Since I use cli the work was a little harder but I have done pretty much the same as you. Where did you extend the Codegen to trigger your extension for each supporting file? The supporting file generation method is private.

nebulon42 commented 11 months ago

I extended the SupportingFile with my own subclass to be able to hold that data and put that into the supporting files list where necessary. I did hook into processTemplateToFile. There I retrieve the supporting file by template path:

    private SupportingFile getSupportingFile(final String templateName, final String outputFilename) {
        return config.supportingFiles()
            .stream()
            .filter(supportingFile -> isFileMatchingTemplate(templateName, outputFilename, supportingFile))
            .findFirst()
            .orElse(null);
    }

    private boolean isFileMatchingTemplate(final String templateName,
                                           final String outputFilename,
                                           final SupportingFile file) {
        return Objects.equals(templateName, file.getTemplateFile()) && outputFilename.contains(getFilePath(file));
    }

Then I check if it has the type of my supporting file. If that is the case I add the data to the template data, call the parent method and afterwards remove the data again from the template data.