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: Re-using params for several methods #357

Open
pontus-eliason opened this issue Jun 20, 2022 · 4 comments
Open

Feature: Re-using params for several methods #357

pontus-eliason opened this issue Jun 20, 2022 · 4 comments
Labels

Comments

@pontus-eliason
Copy link

pontus-eliason commented Jun 20, 2022

Hi!

For our methods, we have multiple common params for each method, let's call them Username and Password. So if we have 100 methods, we are for each one required to write:

"params": [
    {"$ref": "#/components/contentDescriptors/Username"},
    {"$ref": "#/components/contentDescriptors/Password"},
    {
        "name": "X",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/X"
        }
    }
]

I tried to conjure up some make-believe kind of schema that could solve this, but I'm actually stumped. Because it seems very difficult to do, since the params must be an array, and the inheritance through some sort of allOf quickly becomes clumsy.

I need this for quite a simple reason:
When we generate code from the specification, it would greatly help to be able to say "All request params follow this interface containing Username and Password".

The only current solution I've been able to think of is to do something like this using specification extensions:

"contentDescriptors": [
    {
        "name": "Username",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/Username"
        },
        "x-interface": "CredentialProperties"
    },
    {
        "name": "Password",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/Password"
        },
        "x-interface": "CredentialProperties"
    }
]

And then alter the code generation logic in a way that makes the 2 properties be added to a custom interface, so when we receive it on the server end (in Java) we get a class RequestX which implements interface CredentialProperties.

Is there a better way of doing this?

@github-actions
Copy link

Welcome to OpenRPC! Thank you for taking the time to create an issue. Please review the guidelines

@BelfordZ
Copy link
Member

BelfordZ commented Jul 6, 2022

@pontus-eliason firstly, I want to commend you for the great use of spec extensions!

I think you are on the right track with this. One thing that comes to mind that you could try is to run a pre-processing step on your openrpc document which would remove the x-interface key and in turn, add in the params to all the required places.

for example, if you start with

{
  methods: [{
    method: "foo",
    params: [
      {
        "name": "X",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/X"
        }
      }
    ],
    result: [],
    x-extend-params: ["CredentialProperties"]
  }],
  components: {
    contentDescriptors: {
       username: {
         "name": "Username",
         "required": true,
         "schema": {
           "$ref": "#/components/schemas/Username"
         },
         "x-interface": "CredentialProperties"
       },
       {
         "name": "Password",
         "required": true,
         "schema": {
           "$ref": "#/components/schemas/Password"
         },
         "x-interface": "CredentialProperties"
       }
    }
  }

then you'd run your spec extension pre-processor and get back:

{
  methods: [{
    method: "foo",
    params: [
      {"$ref": "#/components/contentDescriptors/Username"},
      {"$ref": "#/components/contentDescriptors/Password"},
      {
        "name": "X",
        "required": true,
        "schema": {
          "$ref": "#/components/schemas/X"
        }
      }
    ],
    result: [],
    x-extends-interface: ["CredentialProperties"]
  }],
  components: {
    contentDescriptors: {
       username: {
         "name": "Username",
         "required": true,
         "schema": {
           "$ref": "#/components/schemas/Username"
         },
         "x-interface": "CredentialProperties"
       },
       {
         "name": "Password",
         "required": true,
         "schema": {
           "$ref": "#/components/schemas/Password"
         },
         "x-interface": "CredentialProperties"
       }
    }
  }

once running through codegen, you would (presumably) get the same resulting interfaces.

Another similar approach would be to use root-level vendor extension, and create your own abstraction around applying content descriptors to specific methods, and once again, run a preprocessor on it.

If for whatever reason pre-processing is not an option, then you would need tooling to support your specification extension, or have a plugin/extension interface allowing you to add code to handle your extension.

Hopefully that helps.

@BelfordZ
Copy link
Member

BelfordZ commented Jul 6, 2022

You could also have your specification extension make use of tags in order to group methods based on what interfaces they extend from. Then you would only need the 1 field x-interface which matches contentDescriptors with the methods that they should be added to.

@BelfordZ
Copy link
Member

BelfordZ commented Jul 6, 2022

Something to consider: when params is byPosition (method expects an array), you would need a way to specify the order in which the params should be added/extended/inherited (what ever you wanna call that =p).

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

No branches or pull requests

2 participants