diff --git a/generators/go-v2/ast/package.json b/generators/go-v2/ast/package.json index 7fe9468c01a..c3a0a660718 100644 --- a/generators/go-v2/ast/package.json +++ b/generators/go-v2/ast/package.json @@ -30,14 +30,14 @@ "@fern-api/browser-compatible-base-generator": "workspace:*", "@fern-api/core-utils": "workspace:*", "@fern-api/path-utils": "workspace:*", - "@fern-fern/ir-sdk": "^53.23.0", + "@fern-fern/ir-sdk": "^53.24.0", "zod": "^3.22.3" }, "devDependencies": { "@fern-api/browser-compatible-base-generator": "workspace:*", "@fern-api/core-utils": "workspace:*", "@fern-api/path-utils": "workspace:*", - "@fern-fern/ir-sdk": "^53.23.0", + "@fern-fern/ir-sdk": "^53.24.0", "@types/jest": "^29.5.12", "depcheck": "^1.4.6", "eslint": "^8.56.0", diff --git a/generators/go-v2/dynamic-snippets/package.json b/generators/go-v2/dynamic-snippets/package.json index b891c442153..5e9976e1130 100644 --- a/generators/go-v2/dynamic-snippets/package.json +++ b/generators/go-v2/dynamic-snippets/package.json @@ -34,7 +34,7 @@ "@fern-api/go-ast": "workspace:*", "@fern-api/go-formatter": "workspace:*", "@fern-api/path-utils": "workspace:*", - "@fern-fern/ir-sdk": "^53.23.0", + "@fern-fern/ir-sdk": "^53.24.0", "@types/jest": "^29.5.12", "depcheck": "^1.4.6", "eslint": "^8.56.0", diff --git a/generators/go-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts b/generators/go-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts index e0913d68176..f14e918b465 100644 --- a/generators/go-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts +++ b/generators/go-v2/dynamic-snippets/src/EndpointSnippetGenerator.ts @@ -108,6 +108,13 @@ export class EndpointSnippetGenerator { snippet: DynamicSnippets.EndpointSnippetRequest; }): go.AstNode[] { const args: go.AstNode[] = []; + const baseUrlArg = this.getConstructorBaseUrlArg({ + baseUrl: snippet.baseUrl, + environment: snippet.environment + }); + if (baseUrlArg != null) { + args.push(baseUrlArg); + } if (endpoint.auth != null) { if (snippet.auth != null) { args.push(this.getConstructorAuthArg({ auth: endpoint.auth, values: snippet.auth })); @@ -187,6 +194,70 @@ export class EndpointSnippetGenerator { }); } + private getConstructorBaseUrlArg({ + baseUrl, + environment + }: { + baseUrl: string | undefined; + environment: DynamicSnippets.EnvironmentValues | undefined; + }): go.AstNode | undefined { + const baseUrlArg = this.getBaseUrlArg({ baseUrl, environment }); + if (baseUrlArg == null) { + return undefined; + } + return go.codeblock((writer) => { + writer.writeNode( + go.invokeFunc({ + func: go.typeReference({ + name: "WithBaseURL", + importPath: this.context.getOptionImportPath() + }), + arguments_: [baseUrlArg] + }) + ); + }); + } + + private getBaseUrlArg({ + baseUrl, + environment + }: { + baseUrl: string | undefined; + environment: DynamicSnippets.EnvironmentValues | undefined; + }): go.AstNode | undefined { + if (baseUrl != null && environment != null) { + this.context.errors.add({ + severity: Severity.Critical, + message: "Cannot specify both baseUrl and environment options" + }); + return undefined; + } + if (baseUrl != null) { + return go.TypeInstantiation.string(baseUrl); + } + if (environment != null) { + if (this.context.isSingleEnvironmentID(environment)) { + const typeReference = this.context.getEnvironmentTypeReferenceFromID(environment); + if (typeReference == null) { + this.context.errors.add({ + severity: Severity.Warning, + message: `Environment "${environment}" was not found` + }); + return undefined; + } + return go.TypeInstantiation.reference(typeReference); + } + if (this.context.isMultiEnvironmentValues(environment)) { + this.context.errors.add({ + severity: Severity.Warning, + message: + "The Go SDK doesn't support a multi-environment client option yet; use the baseUrl option instead" + }); + } + } + return undefined; + } + private getConstructorBearerAuthArg({ auth, values diff --git a/generators/go-v2/dynamic-snippets/src/__test__/__snapshots__/imdb.test.ts.snap b/generators/go-v2/dynamic-snippets/src/__test__/__snapshots__/imdb.test.ts.snap index 616fa890e46..eadbd3fb122 100644 --- a/generators/go-v2/dynamic-snippets/src/__test__/__snapshots__/imdb.test.ts.snap +++ b/generators/go-v2/dynamic-snippets/src/__test__/__snapshots__/imdb.test.ts.snap @@ -120,6 +120,32 @@ func do() () { " `; +exports[`imdb (sync) > GET /movies/{movieId} w/ baseURL 1`] = ` +"package example + +import ( + client "github.com/acme/acme-go/client" + option "github.com/acme/acme-go/option" + context "context" +) + +func do() () { + client := client.NewClient( + option.WithBaseURL( + "http://localhost:8080", + ), + option.WithToken( + "", + ), + ) + client.Imdb.GetMovie( + context.TODO(), + "movie_xyz", + ) +} +" +`; + exports[`imdb (sync) > GET /movies/{movieId} w/ exportedClientName 1`] = ` "package example diff --git a/generators/go-v2/dynamic-snippets/src/__test__/__snapshots__/single-url-environment-default.test.ts.snap b/generators/go-v2/dynamic-snippets/src/__test__/__snapshots__/single-url-environment-default.test.ts.snap new file mode 100644 index 00000000000..76d4f3794ab --- /dev/null +++ b/generators/go-v2/dynamic-snippets/src/__test__/__snapshots__/single-url-environment-default.test.ts.snap @@ -0,0 +1,98 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`single-url-environment-default > custom baseURL 1`] = ` +"package example + +import ( + context "context" + client "github.com/acme/acme-go/client" + option "github.com/acme/acme-go/option" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + "http://localhost:8080", + ), + option.WithToken( + "", + ), + ) + client.Dummy.GetDummy( + context.TODO(), + ) +} +" +`; + +exports[`single-url-environment-default > invalid baseURL and environment 1`] = ` +[ + { + "message": "Cannot specify both baseUrl and environment options", + "path": [], + "severity": "CRITICAL", + }, +] +`; + +exports[`single-url-environment-default > invalid environment 1`] = ` +[ + { + "message": "Environment "Unrecognized" was not found", + "path": [], + "severity": "WARNING", + }, +] +`; + +exports[`single-url-environment-default > production environment 1`] = ` +"package example + +import ( + context "context" + acme "github.com/acme/acme-go" + client "github.com/acme/acme-go/client" + option "github.com/acme/acme-go/option" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + acme.Environments.Production, + ), + option.WithToken( + "", + ), + ) + client.Dummy.GetDummy( + context.TODO(), + ) +} +" +`; + +exports[`single-url-environment-default > staging environment 1`] = ` +"package example + +import ( + context "context" + acme "github.com/acme/acme-go" + client "github.com/acme/acme-go/client" + option "github.com/acme/acme-go/option" +) + +func do() { + client := client.NewClient( + option.WithBaseURL( + acme.Environments.Staging, + ), + option.WithToken( + "", + ), + ) + client.Dummy.GetDummy( + context.TODO(), + ) +} +" +`; diff --git a/generators/go-v2/dynamic-snippets/src/__test__/examples.test.ts b/generators/go-v2/dynamic-snippets/src/__test__/examples.test.ts index 1d418078939..623116f0a6f 100644 --- a/generators/go-v2/dynamic-snippets/src/__test__/examples.test.ts +++ b/generators/go-v2/dynamic-snippets/src/__test__/examples.test.ts @@ -14,6 +14,8 @@ describe("examples", () => { method: "GET", path: "/metadata" }, + baseUrl: undefined, + environment: undefined, auth: AuthValues.bearer({ token: "" }), @@ -35,6 +37,8 @@ describe("examples", () => { method: "GET", path: "/metadata" }, + baseUrl: undefined, + environment: undefined, auth: AuthValues.bearer({ token: "" }), @@ -56,6 +60,8 @@ describe("examples", () => { method: "POST", path: "/movie" }, + baseUrl: undefined, + environment: undefined, auth: AuthValues.bearer({ token: "" }), @@ -89,6 +95,8 @@ describe("examples", () => { method: "POST", path: "/big-entity" }, + baseUrl: undefined, + environment: undefined, auth: AuthValues.bearer({ token: "" }), @@ -128,6 +136,8 @@ describe("examples (errors)", () => { auth: AuthValues.bearer({ token: "" }), + baseUrl: undefined, + environment: undefined, pathParameters: undefined, queryParameters: undefined, headers: undefined, diff --git a/generators/go-v2/dynamic-snippets/src/__test__/exhaustive.test.ts b/generators/go-v2/dynamic-snippets/src/__test__/exhaustive.test.ts index c1df36b203e..96e68f6d9e2 100644 --- a/generators/go-v2/dynamic-snippets/src/__test__/exhaustive.test.ts +++ b/generators/go-v2/dynamic-snippets/src/__test__/exhaustive.test.ts @@ -14,6 +14,8 @@ describe("exhaustive", () => { method: "POST", path: "/container/list-of-primitives" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), @@ -30,6 +32,8 @@ describe("exhaustive", () => { method: "POST", path: "/container/list-of-objects" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), @@ -71,6 +75,8 @@ describe("exhaustive (errors)", () => { method: "POST", path: "/container/list-of-objects" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), diff --git a/generators/go-v2/dynamic-snippets/src/__test__/file-upload.test.ts b/generators/go-v2/dynamic-snippets/src/__test__/file-upload.test.ts index 33881fc1c78..2f9a16ec52d 100644 --- a/generators/go-v2/dynamic-snippets/src/__test__/file-upload.test.ts +++ b/generators/go-v2/dynamic-snippets/src/__test__/file-upload.test.ts @@ -13,6 +13,8 @@ describe("file-upload (success)", () => { method: "POST", path: "/" }, + baseUrl: undefined, + environment: undefined, auth: undefined, pathParameters: undefined, queryParameters: undefined, @@ -30,6 +32,8 @@ describe("file-upload (success)", () => { method: "POST", path: "/just-file" }, + baseUrl: undefined, + environment: undefined, auth: undefined, pathParameters: undefined, queryParameters: undefined, @@ -46,6 +50,8 @@ describe("file-upload (success)", () => { method: "POST", path: "/just-file-with-query-params" }, + baseUrl: undefined, + environment: undefined, auth: undefined, pathParameters: undefined, queryParameters: { diff --git a/generators/go-v2/dynamic-snippets/src/__test__/imdb.test.ts b/generators/go-v2/dynamic-snippets/src/__test__/imdb.test.ts index c5d56da4141..cc9037d52e2 100644 --- a/generators/go-v2/dynamic-snippets/src/__test__/imdb.test.ts +++ b/generators/go-v2/dynamic-snippets/src/__test__/imdb.test.ts @@ -14,6 +14,8 @@ describe("imdb (success)", () => { method: "GET", path: "/movies/{movieId}" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), @@ -32,6 +34,8 @@ describe("imdb (success)", () => { method: "POST", path: "/movies/create-movie" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), @@ -66,6 +70,33 @@ describe("imdb (sync)", () => { method: "GET", path: "/movies/{movieId}" }, + baseUrl: undefined, + environment: undefined, + auth: dynamic.AuthValues.bearer({ + token: "" + }), + pathParameters: { + movieId: "movie_xyz" + }, + queryParameters: undefined, + headers: undefined, + requestBody: undefined + }); + expect(response.snippet).toMatchSnapshot(); + }); + + it("GET /movies/{movieId} w/ baseURL", () => { + const generator = buildDynamicSnippetsGenerator({ + irFilepath: AbsoluteFilePath.of(`${DYNAMIC_IR_TEST_DEFINITIONS_DIRECTORY}/imdb.json`), + config: buildGeneratorConfig() + }); + const response = generator.generateSync({ + endpoint: { + method: "GET", + path: "/movies/{movieId}" + }, + baseUrl: "http://localhost:8080", + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), @@ -93,6 +124,8 @@ describe("imdb (sync)", () => { method: "GET", path: "/movies/{movieId}" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), @@ -118,6 +151,8 @@ describe("imdb (errors)", () => { method: "GET", path: "/movies/{movieId}" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), @@ -141,6 +176,8 @@ describe("imdb (errors)", () => { method: "POST", path: "/movies/create-movie" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), @@ -165,6 +202,8 @@ describe("imdb (errors)", () => { method: "POST", path: "/movies/create-movie" }, + baseUrl: undefined, + environment: undefined, auth: dynamic.AuthValues.bearer({ token: "" }), diff --git a/generators/go-v2/dynamic-snippets/src/__test__/single-url-environment-default.test.ts b/generators/go-v2/dynamic-snippets/src/__test__/single-url-environment-default.test.ts new file mode 100644 index 00000000000..45c17d77fa4 --- /dev/null +++ b/generators/go-v2/dynamic-snippets/src/__test__/single-url-environment-default.test.ts @@ -0,0 +1,133 @@ +import { buildDynamicSnippetsGenerator } from "./utils/buildDynamicSnippetsGenerator"; +import { DYNAMIC_IR_TEST_DEFINITIONS_DIRECTORY } from "./utils/constant"; +import { buildGeneratorConfig } from "./utils/buildGeneratorConfig"; +import { AuthValues } from "@fern-fern/ir-sdk/api/resources/dynamic"; +import { AbsoluteFilePath } from "@fern-api/path-utils"; +import { TestCase } from "./utils/TestCase"; + +describe("single-url-environment-default", () => { + it("production environment", async () => { + const generator = buildDynamicSnippetsGenerator({ + irFilepath: AbsoluteFilePath.of( + `${DYNAMIC_IR_TEST_DEFINITIONS_DIRECTORY}/single-url-environment-default.json` + ), + config: buildGeneratorConfig() + }); + const response = await generator.generate({ + endpoint: { + method: "GET", + path: "/dummy" + }, + auth: AuthValues.bearer({ + token: "" + }), + baseUrl: undefined, + environment: "Production", + pathParameters: undefined, + queryParameters: undefined, + headers: undefined, + requestBody: undefined + }); + expect(response.snippet).toMatchSnapshot(); + }); + + it("staging environment", async () => { + const generator = buildDynamicSnippetsGenerator({ + irFilepath: AbsoluteFilePath.of( + `${DYNAMIC_IR_TEST_DEFINITIONS_DIRECTORY}/single-url-environment-default.json` + ), + config: buildGeneratorConfig() + }); + const response = await generator.generate({ + endpoint: { + method: "GET", + path: "/dummy" + }, + auth: AuthValues.bearer({ + token: "" + }), + baseUrl: undefined, + environment: "Staging", + pathParameters: undefined, + queryParameters: undefined, + headers: undefined, + requestBody: undefined + }); + expect(response.snippet).toMatchSnapshot(); + }); + + it("custom baseURL", async () => { + const generator = buildDynamicSnippetsGenerator({ + irFilepath: AbsoluteFilePath.of( + `${DYNAMIC_IR_TEST_DEFINITIONS_DIRECTORY}/single-url-environment-default.json` + ), + config: buildGeneratorConfig() + }); + const response = await generator.generate({ + endpoint: { + method: "GET", + path: "/dummy" + }, + auth: AuthValues.bearer({ + token: "" + }), + baseUrl: "http://localhost:8080", + environment: undefined, + pathParameters: undefined, + queryParameters: undefined, + headers: undefined, + requestBody: undefined + }); + expect(response.snippet).toMatchSnapshot(); + }); + + it("invalid environment", async () => { + const generator = buildDynamicSnippetsGenerator({ + irFilepath: AbsoluteFilePath.of( + `${DYNAMIC_IR_TEST_DEFINITIONS_DIRECTORY}/single-url-environment-default.json` + ), + config: buildGeneratorConfig() + }); + const response = await generator.generate({ + endpoint: { + method: "GET", + path: "/dummy" + }, + auth: AuthValues.bearer({ + token: "" + }), + baseUrl: undefined, + environment: "Unrecognized", + pathParameters: undefined, + queryParameters: undefined, + headers: undefined, + requestBody: undefined + }); + expect(response.errors).toMatchSnapshot(); + }); + + it("invalid baseURL and environment", async () => { + const generator = buildDynamicSnippetsGenerator({ + irFilepath: AbsoluteFilePath.of( + `${DYNAMIC_IR_TEST_DEFINITIONS_DIRECTORY}/single-url-environment-default.json` + ), + config: buildGeneratorConfig() + }); + const response = await generator.generate({ + endpoint: { + method: "GET", + path: "/dummy" + }, + auth: AuthValues.bearer({ + token: "" + }), + baseUrl: "http://localhost:8080", + environment: "Production", + pathParameters: undefined, + queryParameters: undefined, + headers: undefined, + requestBody: undefined + }); + expect(response.errors).toMatchSnapshot(); + }); +}); diff --git a/generators/go-v2/dynamic-snippets/src/context/DynamicSnippetsGeneratorContext.ts b/generators/go-v2/dynamic-snippets/src/context/DynamicSnippetsGeneratorContext.ts index 59e0597dc79..813d80d9391 100644 --- a/generators/go-v2/dynamic-snippets/src/context/DynamicSnippetsGeneratorContext.ts +++ b/generators/go-v2/dynamic-snippets/src/context/DynamicSnippetsGeneratorContext.ts @@ -4,7 +4,7 @@ import { FernGeneratorExec } from "@fern-api/browser-compatible-base-generator"; import { BaseGoCustomConfigSchema, resolveRootImportPath } from "@fern-api/go-ast"; -import { FernFilepath, dynamic, TypeId, Name } from "@fern-fern/ir-sdk/api"; +import { FernFilepath, dynamic, TypeId, Name, EnvironmentId } from "@fern-fern/ir-sdk/api"; import { HttpEndpointReferenceParser } from "@fern-api/fern-definition-schema"; import { TypeInstance } from "../TypeInstance"; import { DiscriminatedUnionTypeInstance } from "../DiscriminatedUnionTypeInstance"; @@ -369,10 +369,52 @@ export class DynamicSnippetsGeneratorContext extends AbstractDynamicSnippetsGene }); } + public getEnvironmentTypeReferenceFromID(environmentID: string): go.TypeReference | undefined { + if (this.ir.environments == null) { + return undefined; + } + const environments = this.ir.environments.environments; + switch (environments.type) { + case "singleBaseUrl": { + const environment = environments.environments.find((env) => env.id === environmentID); + if (environment == null) { + return undefined; + } + return this.getEnvironmentTypeReference(environment.name); + } + case "multipleBaseUrls": { + const environment = environments.environments.find((env) => env.id === environmentID); + if (environment == null) { + return undefined; + } + return this.getEnvironmentTypeReference(environment.name); + } + default: + assertNever(environments); + } + } + + public isSingleEnvironmentID(environment: dynamic.EnvironmentValues): environment is EnvironmentId { + return typeof environment === "string"; + } + + public isMultiEnvironmentValues( + environment: dynamic.EnvironmentValues + ): environment is dynamic.MultipleEnvironmentUrlValues { + return typeof environment === "object"; + } + public newParameterNotRecognizedError(parameterName: string): Error { return new Error(`"${parameterName}" is not a recognized parameter for this endpoint`); } + private getEnvironmentTypeReference(name: Name): go.TypeReference { + return go.typeReference({ + name: `Environments.${this.getTypeName(name)}`, + importPath: this.rootImportPath + }); + } + private isListTypeReference(typeReference: dynamic.TypeReference): boolean { if (typeReference.type === "optional") { return this.isListTypeReference(typeReference.value); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62d5a8971bf..c2a0aaae747 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -425,8 +425,8 @@ importers: specifier: workspace:* version: link:../../../packages/commons/path-utils '@fern-fern/ir-sdk': - specifier: ^53.23.0 - version: 53.23.0 + specifier: ^53.24.0 + version: 53.24.0 zod: specifier: ^3.22.3 version: 3.23.8 @@ -516,8 +516,8 @@ importers: specifier: workspace:* version: link:../../../packages/commons/path-utils '@fern-fern/ir-sdk': - specifier: ^53.23.0 - version: 53.23.0 + specifier: ^53.24.0 + version: 53.24.0 '@types/jest': specifier: ^29.5.12 version: 29.5.12 @@ -8043,8 +8043,8 @@ packages: '@fern-fern/ir-sdk@53.18.0': resolution: {integrity: sha512-KXHiAn8wjL9VIjjR9z8fXso0O2oaCMUSy9BSYRiGjEOmbIBUhplxSXjM3wSEXQ19hiPpsRYJTCCjnaZVP0OVrw==} - '@fern-fern/ir-sdk@53.23.0': - resolution: {integrity: sha512-9U6uGs9WFDnVg57VyM7s6LKCkA30JOYDQwuLrNh50Meme0m8ce4GDZ/naVncfhrGjWfxt7PSjy1vlVLBi+PKZA==} + '@fern-fern/ir-sdk@53.24.0': + resolution: {integrity: sha512-cR/GIvqLaK8Oeql0WLv8nUsYNfplBHXKHoHv49CJfRP0xMr/RLmiQCZm2RAH+hnMha282oEMXYQeZDoZhatjKw==} '@fern-fern/ir-sdk@53.7.0': resolution: {integrity: sha512-PoCj8yOep3kFeDZYORAzqPwVtCSNmbT2SfR/AoxKCcikeZ5i+4Um4ZXx1e6UaAy7dIYF5kWeRc6lptFBRoj7Gw==} @@ -15246,7 +15246,7 @@ snapshots: '@fern-fern/ir-sdk@53.18.0': {} - '@fern-fern/ir-sdk@53.23.0': {} + '@fern-fern/ir-sdk@53.24.0': {} '@fern-fern/ir-sdk@53.7.0': {}