Skip to content

Commit

Permalink
feat(php): Generate single url environment (#4699)
Browse files Browse the repository at this point in the history
  • Loading branch information
amckinney authored Sep 19, 2024
1 parent 2c4c19d commit b310353
Show file tree
Hide file tree
Showing 284 changed files with 2,702 additions and 819 deletions.
23 changes: 19 additions & 4 deletions generators/php/codegen/src/asIs/RawClient.Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace <%= namespace%>;

use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Utils;
Expand All @@ -13,13 +14,27 @@
class RawClient
{
/**
* @param ClientInterface $client The HTTP client used to make requests.
* @param array<string, string> $headers The HTTP headers sent with the request.
* @var ClientInterface $client
*/
private ClientInterface $client;

/**
* @var array<string, string> $headers
*/
private array $headers;

/**
* @param ?array{
* baseUrl?: string,
* client?: ClientInterface,
* headers?: array<string, string>,
* } $options
*/
public function __construct(
private readonly ClientInterface $client,
private readonly array $headers = [],
public readonly ?array $options = null,
) {
$this->client = $this->options['client'] ?? new Client();
$this->headers = $this->options['headers'] ?? [];
}

/**
Expand Down
2 changes: 1 addition & 1 deletion generators/php/codegen/src/asIs/RawClientTest.Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ protected function setUp(): void
$this->mockHandler = new MockHandler();
$handlerStack = HandlerStack::create($this->mockHandler);
$client = new Client(['handler' => $handlerStack]);
$this->rawClient = new RawClient($client);
$this->rawClient = new RawClient(['client' => $client]);
}

public function testHeaders(): void
Expand Down
28 changes: 26 additions & 2 deletions generators/php/codegen/src/ast/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Writer } from "./core/Writer";
export declare namespace Map {
interface Args {
entries: Entry[] | undefined;
multiline?: boolean;
}

interface Entry {
Expand All @@ -14,17 +15,40 @@ export declare namespace Map {

export class Map extends AstNode {
private entries: Map.Entry[];
private multiline: boolean;

constructor({ entries }: Map.Args) {
constructor({ entries, multiline }: Map.Args) {
super();
this.entries = entries ?? [];
this.multiline = multiline ?? false;
}

public write(writer: Writer): void {
if (this.multiline) {
this.writeMultiline(writer);
return;
}
this.writeCompact(writer);
}

private writeMultiline(writer: Writer): void {
writer.writeLine("[");
writer.indent();
for (const { key, value } of this.entries) {
key.write(writer);
writer.write(" => ");
value.write(writer);
writer.writeLine(",");
}
writer.dedent();
writer.write("]");
}

private writeCompact(writer: Writer): void {
writer.write("[");
for (const [index, { key, value }] of this.entries.entries()) {
if (index > 0) {
writer.write(",");
writer.write(", ");
}
key.write(writer);
writer.write(" => ");
Expand Down
2 changes: 1 addition & 1 deletion generators/php/codegen/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ export { AbstractPhpGeneratorContext, type FileLocation } from "./context/Abstra
export { AbstractPhpGeneratorCli } from "./cli/AbstractPhpGeneratorCli";
export { BasePhpCustomConfigSchema } from "./custom-config/BasePhpCustomConfigSchema";
export { PhpFile } from "./project/PhpFile";
export * as php from "./php";
export { FileGenerator } from "./FileGenerator";
export * as php from "./php";
7 changes: 7 additions & 0 deletions generators/php/sdk/src/SdkGeneratorCli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { generateModels } from "@fern-api/php-model";
import { RootClientGenerator } from "./root-client/RootClientGenerator";
import { SubPackageClientGenerator } from "./subpackage-client/SubPackageClientGenerator";
import { WrappedEndpointRequestGenerator } from "./endpoint/request/WrappedEndpointRequestGenerator";
import { EnvironmentGenerator } from "./environment/EnvironmentGenerator";

export class SdkGeneratorCLI extends AbstractPhpGeneratorCli<SdkCustomConfigSchema, SdkGeneratorContext> {
protected constructContext({
Expand Down Expand Up @@ -48,6 +49,7 @@ export class SdkGeneratorCLI extends AbstractPhpGeneratorCli<SdkCustomConfigSche
generateModels(context);
this.generateRootClient(context);
this.generateSubpackages(context);
this.generateEnvironment(context);
await context.project.persist();
}

Expand Down Expand Up @@ -93,4 +95,9 @@ export class SdkGeneratorCLI extends AbstractPhpGeneratorCli<SdkCustomConfigSche
}
}
}

private generateEnvironment(context: SdkGeneratorContext) {
const environmentGenerator = new EnvironmentGenerator(context);
environmentGenerator.generate();
}
}
71 changes: 65 additions & 6 deletions generators/php/sdk/src/SdkGeneratorContext.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { assertNever } from "@fern-api/core-utils";
import {
Name,
HttpEndpoint,
Expand Down Expand Up @@ -73,6 +72,13 @@ export class SdkGeneratorContext extends AbstractPhpGeneratorContext<SdkCustomCo
});
}

public getEnvironmentsClassReference(): php.ClassReference {
return php.classReference({
name: "Environments",
namespace: this.getRootNamespace()
});
}

public getExceptionClassReference(): php.ClassReference {
return php.classReference({
name: "Exception",
Expand Down Expand Up @@ -132,9 +138,11 @@ export class SdkGeneratorContext extends AbstractPhpGeneratorContext<SdkCustomCo
});
}

public getDefaultBaseUrlForEndpoint(endpoint: HttpEndpoint): php.AstNode {
// TODO: Add support for environments.
return php.codeblock("''");
public getDefaultBaseUrlForEndpoint(endpoint: HttpEndpoint): php.CodeBlock {
if (endpoint.baseUrl != null) {
return this.getBaseUrlForEnvironment(endpoint.baseUrl);
}
return this.getDefaultBaseUrl();
}

public getRootClientClassName(): string {
Expand All @@ -152,6 +160,10 @@ export class SdkGeneratorContext extends AbstractPhpGeneratorContext<SdkCustomCo
return "client";
}

public getHeadersOptionName(): string {
return "headers";
}

public getClientOptionsName(): string {
return this.getOptionsName();
}
Expand All @@ -175,6 +187,11 @@ export class SdkGeneratorContext extends AbstractPhpGeneratorContext<SdkCustomCo
key: this.getGuzzleClientOptionName(),
valueType: php.Type.reference(this.guzzleClient.getClientInterfaceClassReference()),
optional: true
},
{
key: this.getHeadersOptionName(),
valueType: php.Type.map(php.Type.string(), php.Type.string()),
optional: true
}
]);
}
Expand All @@ -189,8 +206,15 @@ export class SdkGeneratorContext extends AbstractPhpGeneratorContext<SdkCustomCo
]);
}

private getComputedClientName(): string {
return `${upperFirst(camelCase(this.config.organization))}Client`;
public getEnvironmentAccess(name: Name): php.CodeBlock {
return php.codeblock((writer) => {
writer.writeNode(this.getEnvironmentsClassReference());
writer.write(`::${this.getEnvironmentName(name)}->value`);
});
}

public getEnvironmentName(name: Name): string {
return name.pascalCase.safeName;
}

public getRawAsIsFiles(): string[] {
Expand Down Expand Up @@ -239,4 +263,39 @@ export class SdkGeneratorContext extends AbstractPhpGeneratorContext<SdkCustomCo
const errorDeclaration = this.getErrorDeclarationOrThrow(errorId);
return this.getFileLocation(errorDeclaration.name.fernFilepath, ERRORS_DIRECTORY);
}

private getDefaultBaseUrl(): php.CodeBlock {
const defaultEnvironmentId = this.ir.environments?.defaultEnvironment;
if (defaultEnvironmentId == null) {
return php.codeblock("''");
}
return this.getBaseUrlForEnvironment(defaultEnvironmentId);
}

private getBaseUrlForEnvironment(environmentId: string): php.CodeBlock {
const environmentName =
environmentId != null
? this.ir.environments?.environments._visit({
singleBaseUrl: (value) => {
return value.environments.find((env) => {
return env.id === environmentId;
})?.name;
},
multipleBaseUrls: (value) => {
return value.environments.find((env) => {
return env.id === environmentId;
})?.name;
},
_other: () => undefined
})
: undefined;
if (environmentName == null) {
return php.codeblock("''");
}
return this.getEnvironmentAccess(environmentName);
}

private getComputedClientName(): string {
return `${upperFirst(camelCase(this.config.organization))}Client`;
}
}
3 changes: 2 additions & 1 deletion generators/php/sdk/src/core/RawClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export class RawClient {
public instantiate({ arguments_ }: { arguments_: Arguments }): php.ClassInstantiation {
return php.instantiateClass({
classReference: this.getClassReference(),
arguments_
arguments_,
multiline: true
});
}

Expand Down
8 changes: 6 additions & 2 deletions generators/php/sdk/src/endpoint/http/HttpEndpointGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,15 @@ export class HttpEndpointGenerator extends AbstractEndpointGenerator {

private getBaseURLForEndpoint({ endpoint }: { endpoint: HttpEndpoint }): php.CodeBlock {
return php.codeblock((writer) => {
const baseUrlOptionName = this.context.getBaseUrlOptionName();
const rawClientFieldName = this.context.rawClient.getFieldName();
const clientOptionsName = this.context.getClientOptionsName();
const requestOptionName = this.context.getRequestOptionsName();
const baseUrlOptionName = this.context.getBaseUrlOptionName();
const defaultBaseUrl = this.context.getDefaultBaseUrlForEndpoint(endpoint);

writer.write(`$this->${requestOptionName}['${baseUrlOptionName}'] ?? `);
writer.write(
`$this->${requestOptionName}['${baseUrlOptionName}'] ?? $this->${rawClientFieldName}->${clientOptionsName}['${baseUrlOptionName}'] ?? `
);
writer.writeNode(defaultBaseUrl);
});
}
Expand Down
31 changes: 31 additions & 0 deletions generators/php/sdk/src/environment/EnvironmentGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { SdkGeneratorContext } from "../SdkGeneratorContext";
import { SingleUrlEnvironmentGenerator } from "./SingleUrlEnvironmentGenerator";
import { MultiUrlEnvironmentGenerator } from "./MultiUrlEnvironmentGenerator";

export class EnvironmentGenerator {
private context: SdkGeneratorContext;

public constructor(context: SdkGeneratorContext) {
this.context = context;
}

public generate(): void {
return this.context.ir.environments?.environments._visit({
singleBaseUrl: (value) => {
const environments = new SingleUrlEnvironmentGenerator({
context: this.context,
singleUrlEnvironments: value
});
this.context.project.addSourceFiles(environments.generate());
},
multipleBaseUrls: (value) => {
const environments = new MultiUrlEnvironmentGenerator({
context: this.context,
multiUrlEnvironments: value
});
this.context.project.addSourceFiles(environments.generate());
},
_other: () => undefined
});
}
}
29 changes: 29 additions & 0 deletions generators/php/sdk/src/environment/MultiUrlEnvironmentGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { RelativeFilePath } from "@fern-api/fs-utils";
import { PhpFile, FileGenerator } from "@fern-api/php-codegen";
import { MultipleBaseUrlsEnvironments } from "@fern-fern/ir-sdk/api";
import { SdkCustomConfigSchema } from "../SdkCustomConfig";
import { SdkGeneratorContext } from "../SdkGeneratorContext";

export declare namespace MultiUrlEnvironmentGenerator {
interface Args {
context: SdkGeneratorContext;
multiUrlEnvironments: MultipleBaseUrlsEnvironments;
}
}

export class MultiUrlEnvironmentGenerator extends FileGenerator<PhpFile, SdkCustomConfigSchema, SdkGeneratorContext> {
private multiUrlEnvironments: MultipleBaseUrlsEnvironments;

constructor({ context, multiUrlEnvironments }: MultiUrlEnvironmentGenerator.Args) {
super(context);
this.multiUrlEnvironments = multiUrlEnvironments;
}

public doGenerate(): PhpFile {
throw new Error("Multiple environment URLs are not supported yet");
}

protected getFilepath(): RelativeFilePath {
throw new Error("Multiple environment URLs are not supported yet");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { php, PhpFile, FileGenerator } from "@fern-api/php-codegen";
import { join, RelativeFilePath } from "@fern-api/fs-utils";
import { SingleBaseUrlEnvironments } from "@fern-fern/ir-sdk/api";
import { SdkCustomConfigSchema } from "../SdkCustomConfig";
import { SdkGeneratorContext } from "../SdkGeneratorContext";

export declare namespace SingleUrlEnvironmentGenerator {
interface Args {
context: SdkGeneratorContext;
singleUrlEnvironments: SingleBaseUrlEnvironments;
}
}

export class SingleUrlEnvironmentGenerator extends FileGenerator<PhpFile, SdkCustomConfigSchema, SdkGeneratorContext> {
private singleUrlEnvironments: SingleBaseUrlEnvironments;

constructor({ context, singleUrlEnvironments }: SingleUrlEnvironmentGenerator.Args) {
super(context);
this.singleUrlEnvironments = singleUrlEnvironments;
}

public doGenerate(): PhpFile {
const enum_ = php.enum_({
...this.context.getEnvironmentsClassReference(),
backing: "string"
});

for (const environment of this.singleUrlEnvironments.environments) {
enum_.addMember({
name: this.context.getEnvironmentName(environment.name),
value: environment.url
});
}

return new PhpFile({
clazz: enum_,
directory: RelativeFilePath.of(""),
rootNamespace: this.context.getRootNamespace(),
customConfig: this.context.customConfig
});
}

protected getFilepath(): RelativeFilePath {
return join(
this.context.project.filepaths.getSourceDirectory(),
RelativeFilePath.of(`${this.context.getEnvironmentsClassReference().name}.php`)
);
}
}
Loading

0 comments on commit b310353

Please sign in to comment.