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

Documentation/Feature request: Server selection #543

Closed
florianbepunkt opened this issue Dec 25, 2023 · 13 comments
Closed

Documentation/Feature request: Server selection #543

florianbepunkt opened this issue Dec 25, 2023 · 13 comments
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@florianbepunkt
Copy link

Does oazapft support server variables? If so how would you use them. If not, this is a feature request.

Example

{
   "openapi": "3.0.2",
   "servers": [
      {
         "url": "https://{environment}.example.de",
         "variables": {
            "environment": {
               "default": "api",
               "enum": [
                  "api",
                  "api.dev",
                  "api.staging"
               ]
            }
         }
      }
   ]
}
@Xiphe
Copy link
Collaborator

Xiphe commented Jan 8, 2024

I don't think this is supported yet 👍

@Xiphe Xiphe added enhancement New feature or request help wanted Extra attention is needed labels Jan 8, 2024
@bdm-k
Copy link
Contributor

bdm-k commented Feb 20, 2024

I find it natural to specify server variables using RequestOpts. However, in order to enforce strict typing, we need to make RequestOpts generic, something like:

export type RequestOpts<T> = {
  baseUrl?: string;
  fetch?: typeof fetch;
  formDataConstructor?: new () => FormData;
  headers?: HeadersInit | CustomHeaders;
  serverVariables?: T;
} & Omit<RequestInit, "body" | "headers">;

Taking the example at the top, T should be the following type.

{ environment?: "api" | "api.dev" | "api.staging" }

@Xiphe
Copy link
Collaborator

Xiphe commented Feb 20, 2024

seems feasible. T should default to void.

@bdm-k
Copy link
Contributor

bdm-k commented Feb 21, 2024

I discovered that server variables are taken into consideration when generating the servers constant. I’m not sure what is the intended purpose of this constant. I’m thinking that it might be an option to utilize the constant.

@Xiphe
Copy link
Collaborator

Xiphe commented Feb 21, 2024

I feel like understanding the use-cases here would make sense. Its not really clear to me

@florianbepunkt
Copy link
Author

A good overview of uses cases is described here: https://swagger.io/docs/specification/api-host-and-base-path/

Common use cases for server templating:
Specifying multiple protocols (such as HTTP vs HTTPS).
SaaS (hosted) applications where each customer has their own subdomain.
Regional servers in different geographical regions (example: Amazon Web Services).
Single API definition for SaaS and on-premise APIs.

At our company we use different environments (dev, staging, prod) and use templating for the api base url (api.service.de, dev.api.service.de, staging.api.service.de, etc). We wrote a thin wrapper around oazapft for this.

@bdm-k
Copy link
Contributor

bdm-k commented Feb 21, 2024

Considering your use case, I thought it would be most desirable to be able to specify server variables via the environment variables. What do you think?

@Xiphe
Copy link
Collaborator

Xiphe commented Feb 22, 2024

Let's keep the generated code platform agnostic and not use environment variables.
The use cases described suggest that usually this would be global configuration. Our API for this is the defaults export.

import { defaults, servers } from "./myGeneratedApi.ts";

defaults.baseUrl = servers.prod;

the Defaults extend RequestOpts so i think it the suggestion from @bdm-k would fit here.

What do you say @florianbepunkt, what would your expected usage of this? Where and when would you expect to set server variables?

@florianbepunkt
Copy link
Author

Not sure if variables in the OpenAPI spec are purely scoped to server base urls – this is the only place we are using them. If they are, a function that takes a dict of variables as input and outputs the templated string would make sense to me.

Example:

const spec = {
  servers: [
    {
      url: "{protocol}://{environment}.your-domain.de/identity",
      variables: {
        environment: {
          default: "api",
          enum: ["api", "api.dev", "api.test", "api.staging"],
        },
        protocol: {
          default: "https",
          enum: ["https", "http"],
        },
      },
    },
  ],
}

defaults.baseUrl = await someFnFromOazapft({ protocol: "https://", environment: Promise.resolve("api.test") }); // returns "https://api.test.your-domain.de/identity",

@Xiphe
Copy link
Collaborator

Xiphe commented Feb 22, 2024

This makes sense, thank you for the input.

  1. Let's get confirmation where server variables are meant to be used.
  2. I like the api you're proposing here, we might even just provide server factories on the servers object when a serve has variables.
// myGeneratedApi.ts
/**
 * DO NOT MODIFY - This file has been generated using oazapfts.
 * See https://www.npmjs.com/package/oazapfts
 */
import { applyServerVariables } from "@oazapfts/runtime/servers";

// ...other code

export const servers = {
  server1: "https://petstore.swagger.io/v2",
  server2: (
    serverVariables: {
      protocol: 'https://' | 'http://',
      environment: 'prod' | 'staging' | 'test'
    }
  ) => (
    applyServerVariables("{protocol}://{environment}.your-domain.de/identity", serverVariables)
  )
};
// code using the client
import { defaults, servers } from './myGeneratedApi.ts';

defaults.baseUrl = servers.server2({ protocol: "https://", environment: "prod" });

I'm afraid we'd either only allow sync values here or deal with the promises internally. I would not want to require top level await here.

@bdm-k
Copy link
Contributor

bdm-k commented Feb 22, 2024

Current Oazapfts can already generate that function. So, I believe this issue is about the documentation.

@Xiphe
Copy link
Collaborator

Xiphe commented Feb 23, 2024

lol. I wasn't aware of that :)

@Xiphe
Copy link
Collaborator

Xiphe commented Feb 23, 2024

So yeah, thanks @bdm-k for pointing this out. This is already working:

// code using the client
import { defaults, servers } from './myGeneratedApi.ts';

defaults.baseUrl = servers.server2({ protocol: "https://", environment: "prod" });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants