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

feat(cli): openapi support assigning servers to an environment #4237

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/cli/openapi-ir-sdk/fern/definition/commons.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ types:
docs: Populated by `X-Server-Name`
url: string
audiences: optional<list<string>>
environment:
type: optional<string>
docs: Populated by `X-Server-Environment`

SdkGroupName:
type: list<string>
Expand Down
1 change: 1 addition & 0 deletions packages/cli/openapi-ir-sdk/fern/definition/finalIr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ types:
description: optional<string>
basePath: optional<string>
servers: list<commons.Server>
defaultEnvironment: optional<string>
groups:
docs: |
Top level group information populated through `x-fern-groups`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ export interface Server extends FernOpenapiIr.WithDescription {
name: string | undefined;
url: string;
audiences: string[] | undefined;
/** Populated by `X-Server-Environment` */
environment: string | undefined;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface OpenApiIntermediateRepresentation {
description: string | undefined;
basePath: string | undefined;
servers: FernOpenapiIr.Server[];
defaultEnvironment: string | undefined;
/** Top level group information populated through `x-fern-groups`. */
groups: Record<string, FernOpenapiIr.SdkGroupInfo>;
tags: FernOpenapiIr.Tags;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const Server: core.serialization.ObjectSchema<serializers.Server.Raw, Fer
name: core.serialization.string().optional(),
url: core.serialization.string(),
audiences: core.serialization.list(core.serialization.string()).optional(),
environment: core.serialization.string().optional(),
})
.extend(WithDescription);

Expand All @@ -20,5 +21,6 @@ export declare namespace Server {
name?: string | null;
url: string;
audiences?: string[] | null;
environment?: string | null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const OpenApiIntermediateRepresentation: core.serialization.ObjectSchema<
description: core.serialization.string().optional(),
basePath: core.serialization.string().optional(),
servers: core.serialization.list(Server),
defaultEnvironment: core.serialization.string().optional(),
groups: core.serialization.record(core.serialization.string(), SdkGroupInfo),
tags: Tags,
hasEndpointsMarkedInternal: core.serialization.boolean(),
Expand All @@ -51,6 +52,7 @@ export declare namespace OpenApiIntermediateRepresentation {
description?: string | null;
basePath?: string | null;
servers: Server.Raw[];
defaultEnvironment?: string | null;
groups: Record<string, SdkGroupInfo.Raw>;
tags: Tags.Raw;
hasEndpointsMarkedInternal: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ describe("Fern Definition Builder", () => {
globalHeaders: [],
idempotencyHeaders: [],
groups: {},
channel: []
channel: [],
defaultEnvironment: undefined
},
true,
false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`x-fern-servers x-fern-servers docs 1`] = `
{
"definitionFiles": {
"transcripts.yml": {
"types": {
"SpeechModel": {
"docs": "The speech model to use for the transcription.",
"type": "string",
},
},
},
},
"packageMarkerFile": {},
"rootApiFile": {
"default-environment": "Production",
"display-name": "Test extension \`x-fern-enum\` for enums",
"environments": {
"Production": {
"urls": {
"api": "https://api.example.com",
"auth": "https://auth.example.com",
},
},
"Sandbox": {
"urls": {
"api": "https://api-sandbox.example.com",
"auth": "https://auth-sandbox.example.com",
},
},
},
"error-discrimination": {
"strategy": "status-code",
},
"name": "api",
},
}
`;

exports[`x-fern-servers x-fern-servers simple 1`] = `
{
"definitionFiles": {
"transcripts.yml": {
"types": {
"SpeechModel": {
"docs": "The speech model to use for the transcription.",
"type": "string",
},
},
},
},
"packageMarkerFile": {},
"rootApiFile": {
"default-environment": "Production",
"display-name": "Test extension \`x-fern-enum\` for enums",
"environments": {
"Production": {
"urls": {
"api": "https://api.example.com",
"auth": "https://auth.example.com",
},
},
"Sandbox": {
"urls": {
"api": "https://api-sandbox.example.com",
"auth": "https://auth-sandbox.example.com",
},
},
},
"error-discrimination": {
"strategy": "status-code",
},
"name": "api",
},
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
openapi: 3.1.0
info:
title: Test extension `x-fern-enum` for enums
version: 1.0.0
paths: {}
servers:
- url: https://api.example.com
x-fern-server:
name: api
environment: Production
- url: https://auth.example.com
x-fern-server:
name: auth
environment: Production
- url: https://api-sandbox.example.com
x-fern-server:
name: api
environment: Sandbox
- url: https://auth-sandbox.example.com
x-fern-server:
name: auth
environment: Sandbox

components:
schemas:
SpeechModel:
type: string
description: The speech model to use for the transcription.
x-fern-sdk-group-name: transcripts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { testConvertOpenAPI } from "./testConvertOpenApi";

describe("x-fern-servers", () => {
testConvertOpenAPI("x-fern-servers", "openapi.yml");
});
44 changes: 44 additions & 0 deletions packages/cli/openapi-ir-to-fern/src/buildEnvironments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,50 @@ function extractUrlsFromEnvironmentSchema(
}

export function buildEnvironments(context: OpenApiIrConverterContext): void {
const fullEnvironments: Record<string, Record<string, string>> = {};
for (const server of context.ir.servers) {
if (server.name != null && server.environment != null) {
const environment = fullEnvironments[server.environment] ?? {};
environment[server.name] = server.url;
fullEnvironments[server.environment] = environment;
}
}

// If the environments are valid (e.g. the keys across environments are the same), add them to the builder
// and return, otherwise then you want to go down the original path of adding the environments to the builder.
if (Object.keys(fullEnvironments).length > 0) {
const firstEnvironmentName = Object.keys(fullEnvironments)[0];
const firstEnvironment = Object.values(fullEnvironments)[0];

if (firstEnvironment != null) {
const maybeEnvironmentUrls = firstEnvironment != null ? Object.values(firstEnvironment) : [];
// If any of the environments aren't valid, don't respect the lists
const environmentsAreValid = Object.values(fullEnvironments).every((environment) => {
const theseEnvUrls = Object.keys(environment);
const firstEnvUrls = Object.keys(firstEnvironment);
if (theseEnvUrls.length !== firstEnvUrls.length) {
return false;
}

return theseEnvUrls.every((value, index) => value === firstEnvUrls[index]);
});

if (maybeEnvironmentUrls.length > 0 && environmentsAreValid) {
for (const [environmentName, urls] of Object.entries(fullEnvironments)) {
context.builder.addEnvironment({
name: environmentName,
schema: { urls }
});
}
// Set the first environment as the default environment this should
// be a safe check since we've already validated the length of the Record.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
context.builder.setDefaultEnvironment(firstEnvironmentName!);
return;
}
}
}

const topLevelServersWithName: Record<string, RawSchemas.EnvironmentSchema> = {};
const topLevelSkippedServers = [];
for (const server of context.ir.servers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > anyOf > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": null,
"endpoints": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > apiture > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": "APIs for digital banking client applications.

## Customer Accounts
Expand Down Expand Up @@ -18600,6 +18601,7 @@ For recurring transfer schedules, \`endsOn\`, \`count\`, and \`amountLimit\` are
{
"audiences": null,
"description": null,
"environment": null,
"name": null,
"url": "https://api.apiture.com/banking",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > application-pdf-content-type > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": null,
"endpoints": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > aries > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": null,
"endpoints": [
{
Expand Down Expand Up @@ -113384,6 +113385,7 @@ exports[`open api parser > aries > parse open api 1`] = `
{
"audiences": null,
"description": null,
"environment": null,
"name": null,
"url": "/",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ The parameter should map to a JSON encoded list of strings.
"summary": null,
},
],
"defaultEnvironment": null,
"description": "AssemblyAI API",
"endpoints": [
{
Expand Down Expand Up @@ -17947,6 +17948,7 @@ The default value is 'en_us'.
{
"audiences": null,
"description": "AssemblyAI API",
"environment": null,
"name": null,
"url": "https://api.assemblyai.com",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > audio-mpeg-content-type > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": null,
"endpoints": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > availability > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": null,
"endpoints": [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > belvo > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": "# Introduction

Belvo is an open banking API for Latin America that allows companies to access banking and fiscal information in a secure as well as agile way.
Expand Down Expand Up @@ -193811,18 +193812,21 @@ For \`session_expired\` errors, the description can be (among others):
{
"audiences": null,
"description": "Production",
"environment": null,
"name": "Production",
"url": "https://api.belvo.com",
},
{
"audiences": null,
"description": "Sandbox",
"environment": null,
"name": "Sandbox",
"url": "https://sandbox.belvo.com",
},
{
"audiences": null,
"description": "Development",
"environment": null,
"name": "Development",
"url": "https://development.belvo.com",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > deel > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": null,
"endpoints": [
{
Expand Down Expand Up @@ -74052,12 +74053,14 @@ exports[`open api parser > deel > parse open api 1`] = `
{
"audiences": null,
"description": "Production server",
"environment": null,
"name": null,
"url": "https://api.letsdeel.com/rest/v1",
},
{
"audiences": null,
"description": "Demo server",
"environment": null,
"name": null,
"url": "https://api-staging.letsdeel.com/rest/v1",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ exports[`open api parser > default-content > parse open api 1`] = `
"apiVersion": null,
"basePath": null,
"channel": [],
"defaultEnvironment": null,
"description": null,
"endpoints": [
{
Expand Down Expand Up @@ -101,6 +102,7 @@ exports[`open api parser > default-content > parse open api 1`] = `
{
"audiences": null,
"description": null,
"environment": null,
"name": null,
"url": "https://ai.com",
},
Expand Down
Loading
Loading