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

[Feature Request] Plugin System #562

Open
fimion opened this issue Jan 30, 2024 · 14 comments
Open

[Feature Request] Plugin System #562

fimion opened this issue Jan 30, 2024 · 14 comments

Comments

@fimion
Copy link
Contributor

fimion commented Jan 30, 2024

Based on #293 and #341 and my own interests (I want to decorate the generated functions), I would like to formally open a feature request for a Plugin System for the oazapfts code generation.

The initial points we should probably define are:

  • plugin registration method
  • primary points for plugin modification

I'm more than happy to lead the charge on this.

@Xiphe
Copy link
Collaborator

Xiphe commented Jan 30, 2024

Hi @fimion!
Thanks for bringing this up! I still think that a Plugin System would be a great benefit for this library. (What do you think @fgnass @jonkoops?)

As I'm basically in maintenance & PM mode for oazapfts currently I don't think I can be of much help but when you're interested in leading this I'd be more then happy to assist in design and review 👍

@Xiphe
Copy link
Collaborator

Xiphe commented Jan 30, 2024

My gut-feeling is that the codebase is not in the greatest shape to be hooked into. So this might bring some long needed refactors with it.
I'd hope that adding in such a system would not only bring a great feature to the outside but also causes the codebase to become cleaner 🤞
Or in other words: I would like to not just jam this in 😅

But before working on actual code, let's discuss the points you already brought up

@fimion
Copy link
Contributor Author

fimion commented Jan 30, 2024

It's understandble to want to not just jam this in.

Plugin Registration

I'm imagining from a generator cli end user standpoint that we would provide a command line argument that would let you point to multiple scripts that would be able to register at start up.

From a development perspective, I'd have these scripts need an object as default export that would have a predefined shape to them with potential methods attached that could hook into the various parts. We could expose a method to help with the type definitions as well as potentially do some run time checks?

a plugin file would ultimately look something like this:

import { defineOazapftsPlugin } from "oazapfts/codegen";

export default defineOazapftsPlugin({
  // Various methods and or properties here
 });

Initial Registration points

From an outside developer perspective (who has not fully studied the code), here are some initial places i could see allowing people to hook into stuff

  • After downloading the oas file/before AST generation (access to the full OAS object)
  • before converting a schema to a type (access to schema object, access to full AST object, access to full OAS object)
  • after converting a schema to a type (access to schema object, access to schema-to-type AST object, access to full AST object, access to full OAS object)
  • before converting an endpoint to a function (access to current endpoint object, access to full AST object, access to full OAS object)
  • after converting an endpoint to a function (access to current endpoint object, access to endpoint-to-function AST object, access to full AST object, access to full OAS object)
  • after AST generation is complete (access to full AST, access to full OAS object)
  • before writing to file (after AST generation is complete) (access to file name, access to file output string, access to full OAS object)
  • after writing to file (access to file name, access to file output string, access to full OAS object)

I feel like with this amount of access, there would be good opportunity to modify things as needed.

@Xiphe
Copy link
Collaborator

Xiphe commented Jan 30, 2024

Will look at this tomorrow and give feedback 🤙

@Xiphe
Copy link
Collaborator

Xiphe commented Jan 31, 2024

Cool! This makes a lot of sense to me 👍


Additional hooks I imagine helpful:

  • A way to directly change the defaults object AST. Probably at the beginning of API generation (access to defaults AST, full AST object, full OAS object)
  • A way to directly change the servers object AST. Similar as above

In general I wonder If we might be able to create a backbone architecture that takes a OAS object in und calls plugins for the single steps required to create an API client. (We'll probably also need some shared context/state)

Maybe we can then refactor generate.ts into either a plugin or into default code that is being run from within the plugin orchestrator.


Regarding the plugin registration on the CLI level I'm a little torn between args --plugin ./myPlugin.js --plugin ./myOtherPlugin.ts and a configuration file.

But whatever the case, those would eventually land as imported objects in the generateSource entry. So maybe let's just start there.


This is exiting! Looking forward to this 🤙

@fimion
Copy link
Contributor Author

fimion commented Feb 11, 2024

Alright, i'm gonna start digging into some of this today.

@fimion
Copy link
Contributor Author

fimion commented Feb 11, 2024

I've made a first pass at what the definition of a plugin would look like based on this discussion, but have not yet started adding the actual implementation of the hooks for this into the existing system. (figured I'd wait to make sure i'm headed in the right direction).

The next step would be to move the current functionality over to use this "lifecycle hook" based system possibly.

Xiphe added a commit that referenced this issue Feb 23, 2024
in order to move towards a more functional and modular architecture
move state that previously was helt on Generator class to a context

the idea behind this is to pass the context object around between
internal units as well as plugin callbacks and all shared state
would be managed on the context object

ref #562
Xiphe added a commit that referenced this issue Mar 1, 2024
in order to move towards a more functional and modular architecture
move state that previously was helt on Generator class to a context

the idea behind this is to pass the context object around between
internal units as well as plugin callbacks and all shared state
would be managed on the context object

ref #562
Xiphe added a commit that referenced this issue Mar 1, 2024
in order to move towards a more functional and modular architecture
move state that previously was helt on Generator class to a context

the idea behind this is to pass the context object around between
internal units as well as plugin callbacks and all shared state
would be managed on the context object

ref #562
@Xiphe
Copy link
Collaborator

Xiphe commented Mar 1, 2024

Had a few more thoughts to this.

We should keep in mind that the codegen also has a "library" API -> you don't need to use it from CLI but can also directly call it in a build script.

This means:

  • We need at least two ways to add plugins (via config and manually)
  • Given Plugins can extend CLI args they should also be able extend the library config object
  • This should be done in a way that options added by plugins are recognized by ts language server
import { generateSource, definePlugin } from 'oazapfts';

type MyCustomPluginOpts {
  petThePanda: boolean;
}

const myPlugin = definePlugin<MyCustomPluginOpts>((hooks) => { /* ... */ });

const api = await generateSource(mySpec, { plugins: [myPlugin], petThePanda: true });

Not sure if this is the ideal way to get there nor if it's even feasible but I hope it illustrates my point.

With this in mind It might maybe also make sense to create an abstraction on top of minimist for the plugins where we require this:

definePlugin<MyCustomPluginOpts>(
  {
    cliArgs: {
      petThePanda: {
        name: 'panda', // would default to kebab('petThePanda')
        type: "boolean",
        alias: ["p"],
        description: "wether the API should pet the panda or not"
      }
    }
  },
  (hooks) => {}
);

@fimion
Copy link
Contributor Author

fimion commented Mar 1, 2024

Yeah, i see what you are saying. have a bit of a head cold today, so i'll spend some time pondering this. we will need to change it so that definePlugin doesn't automatically call the function it is passed

@Xiphe
Copy link
Collaborator

Xiphe commented Mar 10, 2024

Hi @fimion

Made quite some progress on #584. I have started to prepare some of the hook-in points in the new generateApi.ts file (See TODO-HOOK comments).

There are still some other things to refactor. Will look into that when I find my next batch of time for this.

  1. I think Ideally the hooks would not nest. So that I can remove/replace a default implementation and all other hooks would still fire. Unfortunately this is currently not really possible because aliases and enumAliases are created when needed, as side-effects of the client-method generation. (This also has the effect that unused components/schemas are not generated). I might explore always generating all schemas - before creating the method implementations.
  2. I will probably consolidate everything in codegen/src/index.ts with the generateApi. Might just call that oazapfts(). All library related hooks/plugins would then be orchestrated there.
  3. In addition to that there are some cli-only hooks (parsing cli args to options, writing the source to file)

@Xiphe
Copy link
Collaborator

Xiphe commented Mar 10, 2024

We might also want to get a closer look at what redux-toolkit is doing with the library. As I think that project might benefit a lot if the integration could be implemented as a plugin.

@Xiphe
Copy link
Collaborator

Xiphe commented Apr 23, 2024

Hey @fimion just want to check in with you on this.

I have been unavailable for the past two months but would like to try to move the current WIP to a stable release now. Are you still interested in helping out with the plugin setup? I'm a little torn between moving forward and potentially moving into parts where you already have plans or waiting for you to chime in.

Let me know if you have a preference on how we should continue.

@fimion
Copy link
Contributor Author

fimion commented Apr 23, 2024

I too have been somewhat unavailable the last couple of months and my schedule is gonna be pretty packed until late may. I would say go ahead an proceed how you would like, and i'll see if i can jump in and help where i can.

@Xiphe
Copy link
Collaborator

Xiphe commented Apr 24, 2024

Awesome! Will do. All the best for your upcoming projects 🤙

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

No branches or pull requests

2 participants